Refactor library makefile path detection logic.

makefiles/include.mk
--------------------
  o Refactor library makefile loader logic into reusable functions.
    - Common logic to infer paths - repo:onf-make/makefiles/, local makefiles/, etc.
  o Simplify logic:
    - remove inlined include(-once) guard macros, replaced by a function.
    - reduce global makefile path variables to a count of two.

makefiles/library-makefiles.mk
------------------------------
  o Define functions able to infer library makefile paths.
  o Added function include-once
    - Guard macros are used to include a makefile at most once.
  o Added function gen-mk-paths
  o Added function gen-mk-paths--var-top
  o Added function gen-mk-paths--var-dir
    - Infer path to (a) library makefile(s) directory, assign to var.
    - Infer path to sandbox root, assign to var.
  o Added function gen-mk-include
    - Infer library make paths and include $(lib-mk)/makefiles/include.mk

Change-Id: I2cc4dbf11c4e9d61aee4033d0196f2c9aac0420f
diff --git a/makefiles/include.mk b/makefiles/include.mk
index 418e59e..fcb57fd 100644
--- a/makefiles/include.mk
+++ b/makefiles/include.mk
@@ -1,6 +1,6 @@
 # -*- makefile -*-
 # -----------------------------------------------------------------------
-# Copyright 2017-2023 Open Networking Foundation (ONF) and the ONF Contributors
+# Copyright 2017-2024 Open Networking Foundation (ONF) and the ONF Contributors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,33 +14,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # -----------------------------------------------------------------------
-# SPDX-FileCopyrightText: 2017-2023 Open Networking Foundation (ONF) and the ONF Contributors
+# SPDX-FileCopyrightText: 2017-2024 Open Networking Foundation (ONF) and the ONF Contributors
 # SPDX-License-Identifier: Apache-2.0
 # -----------------------------------------------------------------------
-# https://gerrit.opencord.org/plugins/gitiles/onf-make
-# ONF.makefiles.include.version = 1.1
-# -----------------------------------------------------------------------
-#
-ifndef mk-include--onf-make # single-include guard macro
 
 $(if $(DEBUG),$(warning ENTER))
 
 ##-------------------##
 ##---]  GLOBALS  [---##
 ##-------------------##
+# DEBUG := 1
+# DEBUG-onf-mk-paths := 1
 
 ## -----------------------------------------------------------------------
-## Define vars based on relative import (normalize symlinks)
-## Usage: include makefiles/onf-lib/include.mk
-## Overide-by: makefiles/onf-lib/makefiles/include.mk
-##             when repo:onf-make is used as a git-submodule
+## [LOADER] Define path vars based on library include directory
 ## -----------------------------------------------------------------------
-onf-mk-abs    := $(abspath $(lastword $(MAKEFILE_LIST)))
-onf-mk-top    := $(subst /include.mk,$(null),$(onf-mk-abs))
-onf-mk-tmp    := $(onf-mk-top)/tmp
-ONF_MAKEDIR   := $(onf-mk-top)
+$(foreach makefile,$(lastword $(MAKEFILE_LIST)),\
+  $(foreach makedir,$(abspath $(dir $(makefile))),\
+    $(eval include $(makedir)/library-makefiles.mk)\
+))
 
-TOP ?= $(patsubst %/makefiles/include.mk,%,$(onf-mk-abs))
+$(call gen-mk-paths,onf-mk) # [ALSO] $(call gen-mk-include,onf-mk)
+
+## Missing required vars are fatal
+onf-mk-dir ?= $(error onf-mk-dir= is required)
+onf-mk-top ?= $(error onf-mk-top= is required)
+onf-mk-tmp := $(onf-mk-top)/tmp
+
+ONF_MAKEDIR   := $(onf-mk-dir)#   # TODO: Deprecate ONF_MAKEDIR and MAKEDIR
 
 #--------------------##
 ##---]  INCLUDES  [---##
@@ -67,6 +68,11 @@
 include $(ONF_MAKEDIR)/todo.mk
 include $(ONF_MAKEDIR)/help/variables.mk
 
+##------------------------------------##
+##---]  Languages & Interpreters  [---##
+##------------------------------------##
+# include $(ONF_MAKEDIR)/src/golang/mod-update.mk
+
 ##---------------------##
 ##---]  ON_DEMAND  [---##
 ##---------------------##
@@ -83,8 +89,4 @@
 
 $(if $(DEBUG),$(warning LEAVE))
 
-mk-include--onf-make := true
-
-endif # mk-include--onf-make
-
 # [EOF]
diff --git a/makefiles/library-makefiles.mk b/makefiles/library-makefiles.mk
new file mode 100644
index 0000000..84c507d
--- /dev/null
+++ b/makefiles/library-makefiles.mk
@@ -0,0 +1,230 @@
+# -*- makefile -*-
+# -----------------------------------------------------------------------
+# Copyright 2017-2024 Open Networking Foundation (ONF) and the ONF Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2017-2024 Open Networking Foundation (ONF) and the ONF Contributors
+# SPDX-License-Identifier: Apache-2.0
+## -----------------------------------------------------------------------
+## Intent:
+##   - This makefile defines logic for interacting with library makefiles.
+##   - Derived path construction assigned to make variables.
+##   - Avoid duplicate makefile inclusion.
+##   - Logic is non-destructive, at most two variables are defined.
+## -----------------------------------------------------------------------
+## Note:
+##   o make function $(foreach) is often used purely for side effects.
+##     It will artifically create context allowing declaration of named
+##     local variables to improve readability.
+## Todo:
+##   o GNU Make v4.4 introduced $(let var[, ..var],list,action)
+## -----------------------------------------------------------------------
+## Usage:
+##   include path-to-makefiles/makefiles/include.mk
+##     prefix := project-mk
+##     $(call gen-mk-paths,{prefix})
+##     $(call gen-mk-include,{prefix})
+##     $(info makefiles directory ($prefix) is $(project-mk-dir))
+## -----------------------------------------------------------------------
+##   {prefix}-dir    makefiles/ directory containing include.mk (absolute path)
+##   {prefix}-top    sandbox root: path to parent makefile directory that
+##                   included {prefix}-dir/makefiles/include.mk
+## -----------------------------------------------------------------------
+
+ifndef space
+  null  :=#
+  space := $(null) $(null)
+endif
+
+$(if $(DEBUG),$(eval DEBUG-onf-mk-paths := 1)) # Enabled by global debug flag
+$(if $(-onf-mk-paths),$(warning ENTER))
+
+## -----------------------------------------------------------------------
+## Intent: Helper function used to trim trailing directory slash from a path
+## -----------------------------------------------------------------------
+dir-wo-slash = $(strip \
+  $(foreach path,$(dir $(1)),\
+    $(patsubst %/,%,$(path))\
+  ))
+
+## -----------------------------------------------------------------------
+## Intent: Include a makefile at most once
+## -----------------------------------------------------------------------
+## Usage:
+##   $(call include-once,path/to/makefile)
+## Debug-mode:
+##   % make DEBUG-onf-mk-paths=1#
+## -----------------------------------------------------------------------
+## Given:
+##   scalar - path to a makefile (relative or absolute)
+## Return:
+##   none
+## -----------------------------------------------------------------------
+## Pre:
+##   none
+## Post:
+##   Define a unique make variable to indicate a makefile has been loaded.
+##   include-once-^-^path/to/makefile := true
+## -----------------------------------------------------------------------
+include-once =\
+  $(if $(DEBUG-onf-mk-paths),\
+	$(info ** include-once (makefile=$(1))))\
+\
+  $(foreach makefile,$(1),\
+    $(foreach seen,include-once^-^$(makefile),\
+    $(if $($(seen)),$(null),\
+\
+      $(if $(DEBUG-onf-mk-paths),\
+        $(info ** $$(eval include $(makefile)))\
+        $(info ** $$(eval $(seen) := true))\
+      )\
+\
+      $(eval include $(makefile))\
+      $(eval $(seen) := true)\
+  )))
+
+## -----------------------------------------------------------------------
+## Intent: Given a string prefix conditionally define make variables
+##   that reference derived path to a sandbox root directory.
+##
+##   A sandbox root directory is defined as the directory containing
+##   a top level [mM]akefile that includes library makefiles.
+## -----------------------------------------------------------------------
+## Given:
+##    scalar    A string prefix derived paths will be assigned to.
+##
+## Return:
+##    none
+## -----------------------------------------------------------------------
+## Pre:
+##   None
+##
+## Post: Variable defined containing derived paths:
+##   {prefix}-top    Parent directory (makefiles/ removed).
+## -----------------------------------------------------------------------
+## Usage:
+##   prefix := my-mk
+##   $(call gen-mk-paths--var-top,$(prefix))
+##   $(info sandbox root is $(my-mk-top))
+## -----------------------------------------------------------------------
+## Note: skip
+##   1) skip current makefile: library-makefiles.mk
+##   2) skip library makefile: include.mk
+##   3) skip library includes: makefiles/%
+##   [TODO: verify] $(filter-out makefiles/%) may cover everything
+## -----------------------------------------------------------------------
+gen-mk-paths--var-top =\
+  $(if $(DEBUG-onf-mk-paths),\
+	$(info ** gen-mk-paths--var-top (prefix=$(1))))\
+\
+  $(foreach prefix,$(1),\
+    $(foreach skip,$(lastword $(MAKEFILE_LIST)),\
+        $(eval skip += makefiles/)\
+        $(eval skip += %/include.mk)\
+    $(foreach parent,$(lastword \
+        $(filter-out $(skip),$(MAKEFILE_LIST))),\
+    $(foreach abs-parent,$(abspath $(parent)),\
+      $(if $(DEBUG-onf-mk-paths),\
+        $(info ** $$(eval $(prefix)-top := $(call dir-wo-slash,$(abs-parent)))))\
+      $(eval $(prefix)-top := $(call dir-wo-slash,$(abs-parent)))\
+  ))))
+
+## -----------------------------------------------------------------------
+## Intent: Given a string prefix conditionally define make variables
+##   that reference derived path to a library makefile directory.
+##
+##   A library makefile directory is defined by existence of a
+##   filesystem path that contains makefiles/include.mk.
+## -----------------------------------------------------------------------
+## Given:
+##    scalar    A string prefix derived paths will be assigned to.
+##
+## Return:
+##    none
+## -----------------------------------------------------------------------
+## Pre:
+##   None
+##
+## Post: Variables defined containing derived paths:
+##   {prefix}-abs    Absolute path to include.mk (current makefile).
+##   {prefix}-dir    makefiles/ directory containing include.mk
+##   {prefix}-top    Parent directory (makefiles/ removed).
+## -----------------------------------------------------------------------
+## Usage:
+##   prefix := my-mk
+##   $(call gen-mk-paths--var-dir,$(prefix))
+##   $(info library makefile path is $(my-mk-dir))
+## -----------------------------------------------------------------------
+gen-mk-paths--var-dir =\
+  $(if $(DEBUG-onf-mk-paths),\
+	$(info ** gen-mk-paths--var-dir (prefix=$(1))))\
+\
+$(foreach prefix,$(1),\
+\
+  $(foreach loaded,$(prefix)-dir,\
+  $(if $($(loaded)),$(null),\
+\
+  $(foreach makefile,$(lastword $(filter %/include.mk,$(MAKEFILE_LIST))),\
+    $(foreach abs-mk,$(abspath $(makefile)),\
+    $(foreach path,$(firstword $(subst /makefiles/,$(space),$(abs-mk))),\
+\
+      $(if $(DEBUG-onf-mk-paths),\
+        $(info ** $$(eval $(prefix)-dir := [$(path)/makefiles])))\
+      $(eval $(prefix)-dir := $(path)/makefiles)\
+    ))\
+  )))\
+)
+
+## -----------------------------------------------------------------------
+## Intent: Derive paths to library makefile directory and parent makefile
+##   that is including the library
+## -----------------------------------------------------------------------
+## Usage:
+##   prefix := my-mk
+##   $(call gen-mk-paths,$(prefix))
+##   $(info sandbox root is $(my-mk-top))
+##   $(info library makefile path is $(my-mk-dir))
+## -----------------------------------------------------------------------
+gen-mk-paths =\
+  $(if $(DEBUG-onf-mk-paths),\
+	$(info ** gen-mk-paths (prefix=$(1))))\
+\
+  $(foreach prefix,$(1),\
+    $(call gen-mk-paths--var-dir,$(prefix),$(1))\
+    $(call gen-mk-paths--var-top,$(prefix),$(1))\
+)
+
+## -----------------------------------------------------------------------
+## Intent: Derive paths then include top level library makefile
+## -----------------------------------------------------------------------
+## Usage:
+##   prefix := my-mk
+##   $(call gen-mk-include,$(prefix))
+##   $(info makefile loaded: $(my-mk-dir)/include.mk)
+##   $(info sandbox root is $(my-mk-top))
+##   $(info library makefile path is $(my-mk-dir))
+## -----------------------------------------------------------------------
+gen-mk-include =\
+\
+  $(if $(DEBUG-onf-mk-paths),\
+	$(info ** gen-mk-include (prefix=$(1))))\
+\
+  $(foreach prefix,$(strip $(1)),\
+    $(call gen-mk-paths,$(prefix))\
+    $(call include-once,$($(prefix)-dir)/include.mk)\
+  )
+
+$(if $(-onf-mk-paths),$(warning LEAVE))
+
+# [EOF]