# Copyright (C) 2012 The Android Open Source Project # # 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. # # Definitions of various graph-related generic functions, used by # ndk-build internally. # # Coding style note: # # All internal variables in this file begin with '_ndk_mod_' # All internal functions in this file begin with '-ndk-mod-' # # Set this to true if you want to debug the functions here. _ndk_mod_debug := $(if $(NDK_DEBUG_MODULES),true) _ndk_topo_debug := $(if $(NDK_DEBUG_TOPO),true) # Use $(call -ndk-mod-debug,<message>) to print a debug message only # if _ndk_mod_debug is set to 'true'. Useful for debugging the functions # available here. # ifeq (true,$(_ndk_mod_debug)) -ndk-mod-debug = $(info $1) else -ndk-mod-debug := $(empty) endif ifeq (true,$(_ndk_topo_debug)) -ndk-topo-debug = $(info $1) else -ndk-topo-debug = $(empty) endif ####################################################################### # Filter a list of module with a predicate function # $1: list of module names. # $2: predicate function, will be called with $(call $2,<name>), if the # result is not empty, <name> will be added to the result. # Out: subset of input list, where each item passes the predicate. ####################################################################### -ndk-mod-filter = $(strip \ $(foreach _ndk_mod_filter_n,$1,\ $(if $(call $2,$(_ndk_mod_filter_n)),$(_ndk_mod_filter_n))\ )) -test-ndk-mod-filter = \ $(eval -local-func = $$(call seq,foo,$$1))\ $(call test-expect,,$(call -ndk-mod-filter,,-local-func))\ $(call test-expect,foo,$(call -ndk-mod-filter,foo,-local-func))\ $(call test-expect,foo,$(call -ndk-mod-filter,foo bar,-local-func))\ $(call test-expect,foo foo,$(call -ndk-mod-filter,aaa foo bar foo,-local-func))\ $(eval -local-func = $$(call sne,foo,$$1))\ $(call test-expect,,$(call -ndk-mod-filter,,-local-func))\ $(call test-expect,,$(call -ndk-mod-filter,foo,-local-func))\ $(call test-expect,bar,$(call -ndk-mod-filter,foo bar,-local-func))\ $(call test-expect,aaa bar,$(call -ndk-mod-filter,aaa foo bar,-local-func)) ####################################################################### # Filter out a list of modules with a predicate function # $1: list of module names. # $2: predicate function, will be called with $(call $2,<name>), if the # result is not empty, <name> will be added to the result. # Out: subset of input list, where each item doesn't pass the predicate. ####################################################################### -ndk-mod-filter-out = $(strip \ $(foreach _ndk_mod_filter_n,$1,\ $(if $(call $2,$(_ndk_mod_filter_n)),,$(_ndk_mod_filter_n))\ )) -test-ndk-mod-filter-out = \ $(eval -local-func = $$(call seq,foo,$$1))\ $(call test-expect,,$(call -ndk-mod-filter-out,,-local-func))\ $(call test-expect,,$(call -ndk-mod-filter-out,foo,-local-func))\ $(call test-expect,bar,$(call -ndk-mod-filter-out,foo bar,-local-func))\ $(call test-expect,aaa bar,$(call -ndk-mod-filter-out,aaa foo bar foo,-local-func))\ $(eval -local-func = $$(call sne,foo,$$1))\ $(call test-expect,,$(call -ndk-mod-filter-out,,-local-func))\ $(call test-expect,foo,$(call -ndk-mod-filter-out,foo,-local-func))\ $(call test-expect,foo,$(call -ndk-mod-filter-out,foo bar,-local-func))\ $(call test-expect,foo foo,$(call -ndk-mod-filter-out,aaa foo bar foo,-local-func)) ####################################################################### # Find the first item in a list that checks a valid predicate. # $1: list of names. # $2: predicate function, will be called with $(call $2,<name>), if the # result is not empty, <name> will be added to the result. # Out: subset of input list. ####################################################################### -ndk-mod-find-first = $(firstword $(call -ndk-mod-filter,$1,$2)) -test-ndk-mod-find-first.empty = \ $(eval -local-pred = $$(call seq,foo,$$1))\ $(call test-expect,,$(call -ndk-mod-find-first,,-local-pred))\ $(call test-expect,,$(call -ndk-mod-find-first,bar,-local-pred)) -test-ndk-mod-find-first.simple = \ $(eval -local-pred = $$(call seq,foo,$$1))\ $(call test-expect,foo,$(call -ndk-mod-find-first,foo,-local-pred))\ $(call test-expect,foo,$(call -ndk-mod-find-first,aaa foo bar,-local-pred))\ $(call test-expect,foo,$(call -ndk-mod-find-first,aaa foo foo bar,-local-pred)) ######################################################################## # Many tree walking operations require setting a 'visited' flag on # specific graph nodes. The following helper functions help implement # this while hiding details to the callers. # # Technical note: # _ndk_mod_tree_visited.<name> will be 'true' if the node was visited, # or empty otherwise. # # _ndk_mod_tree_visitors lists all visited nodes, used to clean all # _ndk_mod_tree_visited.<name> variables in -ndk-mod-tree-setup-visit. # ####################################################################### # Call this before tree traversal. -ndk-mod-tree-setup-visit = \ $(foreach _ndk_mod_tree_visitor,$(_ndk_mod_tree_visitors),\ $(eval _ndk_mod_tree_visited.$$(_ndk_mod_tree_visitor) :=))\ $(eval _ndk_mod_tree_visitors :=) # Returns non-empty if a node was visited. -ndk-mod-tree-is-visited = \ $(_ndk_mod_tree_visited.$1) # Set the visited state of a node to 'true' -ndk-mod-tree-set-visited = \ $(eval _ndk_mod_tree_visited.$1 := true)\ $(eval _ndk_mod_tree_visitors += $1) ######################################################################## # Many graph walking operations require a work queue and computing # dependencies / children nodes. Here are a few helper functions that # can be used to make their code clearer. This uses a few global # variables that should be defined as follows during the operation: # # _ndk_mod_module current graph node name. # _ndk_mod_wq current node work queue. # _ndk_mod_list current result (list of nodes). # _ndk_mod_depends current graph node's children. # you must call -ndk-mod-get-depends to set this. # ####################################################################### # Pop first item from work-queue into _ndk_mod_module. -ndk-mod-pop-first = \ $(eval _ndk_mod_module := $$(call first,$$(_ndk_mod_wq)))\ $(eval _ndk_mod_wq := $$(call rest,$$(_ndk_mod_wq))) -test-ndk-mod-pop-first = \ $(eval _ndk_mod_wq := A B C)\ $(call -ndk-mod-pop-first)\ $(call test-expect,A,$(_ndk_mod_module))\ $(call test-expect,B C,$(_ndk_mod_wq))\ # Push list of items at the back of the work-queue. -ndk-mod-push-back = \ $(eval _ndk_mod_wq := $(strip $(_ndk_mod_wq) $1)) -test-ndk-mod-push-back = \ $(eval _ndk_mod_wq := A B C)\ $(call -ndk-mod-push-back, D E)\ $(call test-expect,A B C D E,$(_ndk_mod_wq)) # Set _ndk_mod_depends to the direct dependencies of _ndk_mod_module -ndk-mod-get-depends = \ $(eval _ndk_mod_depends := $$(call $$(_ndk_mod_deps_func),$$(_ndk_mod_module))) # Set _ndk_mod_depends to the direct dependencies of _ndk_mod_module that # are not already in _ndk_mod_list. -ndk-mod-get-new-depends = \ $(call -ndk-mod-get-depends)\ $(eval _ndk_mod_depends := $$(filter-out $$(_ndk_mod_list),$$(_ndk_mod_depends))) ########################################################################## # Compute the transitive closure # $1: list of modules. # $2: dependency function, $(call $2,<module>) should return all the # module that <module> depends on. # Out: transitive closure of all modules from those in $1. Always includes # the modules in $1. Order is random. # # Implementation note: # we use the -ndk-mod-tree-xxx functions to flag 'visited' nodes # in the graph. A node is visited once it has been put into the work # queue. For each item in the work queue, get the dependencies and # append all those that were not visited yet. ####################################################################### -ndk-mod-get-closure = $(strip \ $(eval _ndk_mod_wq :=)\ $(eval _ndk_mod_list :=)\ $(eval _ndk_mod_deps_func := $2)\ $(call -ndk-mod-tree-setup-visit)\ $(foreach _ndk_mod_module,$1,\ $(call -ndk-mod-closure-visit,$(_ndk_mod_module))\ )\ $(call -ndk-mod-closure-recursive)\ $(eval _ndk_mod_deps :=)\ $(_ndk_mod_list)\ ) # Used internally to visit a new node during -ndk-mod-get-closure. # This appends the node to the work queue, and set its 'visit' flag. -ndk-mod-closure-visit = \ $(call -ndk-mod-push-back,$1)\ $(call -ndk-mod-tree-set-visited,$1) -ndk-mod-closure-recursive = \ $(call -ndk-mod-pop-first)\ $(eval _ndk_mod_list += $$(_ndk_mod_module))\ $(call -ndk-mod-get-depends)\ $(foreach _ndk_mod_dep,$(_ndk_mod_depends),\ $(if $(call -ndk-mod-tree-is-visited,$(_ndk_mod_dep)),,\ $(call -ndk-mod-closure-visit,$(_ndk_mod_dep))\ )\ )\ $(if $(_ndk_mod_wq),$(call -ndk-mod-closure-recursive)) -test-ndk-mod-get-closure.empty = \ $(eval -local-deps = $$($$1_depends))\ $(call test-expect,,$(call -ndk-mod-get-closure,,-local-deps)) -test-ndk-mod-get-closure.single = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends :=)\ $(call test-expect,A,$(call -ndk-mod-get-closure,A,-local-deps)) -test-ndk-mod-get-closure.double = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends :=)\ $(call test-expect,A B,$(call -ndk-mod-get-closure,A,-local-deps)) -test-ndk-mod-get-closure.circular-deps = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends := C)\ $(eval C_depends := A)\ $(call test-expect,A B C,$(call -ndk-mod-get-closure,A,-local-deps)) -test-ndk-mod-get-closure.ABCDE = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends := D)\ $(eval C_depends := D E)\ $(eval D_depends :=)\ $(eval E_depends :=)\ $(call test-expect,A B C D E,$(call -ndk-mod-get-closure,A,-local-deps)) ######################################################################### # For topological sort, we need to count the number of incoming edges # in each graph node. The following helper functions implement this and # hide implementation details. # # Count the number of incoming edges for each node during topological # sort with a string of xxxxs. I.e.: # 0 edge -> '' # 1 edge -> 'x' # 2 edges -> 'xx' # 3 edges -> 'xxx' # etc. ######################################################################### # zero the incoming edge counter for module $1 -ndk-mod-topo-zero-incoming = \ $(eval _ndk_mod_topo_incoming.$1 :=) # increment the incoming edge counter for module $1 -ndk-mod-topo-increment-incoming = \ $(eval _ndk_mod_topo_incoming.$1 := $$(_ndk_mod_topo_incoming.$1)x) # decrement the incoming edge counter for module $1 -ndk-mod-topo-decrement-incoming = \ $(eval _ndk_mod_topo_incoming.$1 := $$(_ndk_mod_topo_incoming.$1:%x=%)) # return non-empty if the module $1's incoming edge counter is > 0 -ndk-mod-topo-has-incoming = $(_ndk_mod_topo_incoming.$1) # Find first node in a list that has zero incoming edges. # $1: list of nodes # Out: first node that has zero incoming edges, or empty. -ndk-mod-topo-find-first-zero-incoming = $(firstword $(call -ndk-mod-filter-out,$1,-ndk-mod-topo-has-incoming)) # Only use for debugging: -ndk-mod-topo-dump-count = \ $(foreach _ndk_mod_module,$1,\ $(info .. $(_ndk_mod_module) incoming='$(_ndk_mod_topo_incoming.$(_ndk_mod_module))')) ######################################################################### # Return the topologically ordered closure of all nodes from a top-level # one. This means that a node A, in the result, will always appear after # node B if A depends on B. Assumes that the graph is a DAG (if there are # circular dependencies, this property cannot be guaranteed, but at least # the function should not loop infinitely). # # $1: top-level node name. # $2: dependency function, i.e. $(call $2,<name>) returns the children # nodes for <name>. # Return: list of nodes, include $1, which will always be the first. ######################################################################### -ndk-mod-get-topo-list = $(strip \ $(eval _ndk_mod_top_module := $1)\ $(eval _ndk_mod_deps_func := $2)\ $(eval _ndk_mod_nodes := $(call -ndk-mod-get-closure,$1,$2))\ $(call -ndk-mod-topo-count,$(_ndk_mod_nodes))\ $(eval _ndk_mod_list :=)\ $(eval _ndk_mod_wq := $(call -ndk-mod-topo-find-first-zero-incoming,$(_ndk_mod_nodes)))\ $(call -ndk-mod-topo-sort)\ $(_ndk_mod_list) $(_ndk_mod_nodes)\ ) # Given a closure list of nodes, count their incoming edges. # $1: list of nodes, must be a graph closure. -ndk-mod-topo-count = \ $(foreach _ndk_mod_module,$1,\ $(call -ndk-mod-topo-zero-incoming,$(_ndk_mod_module)))\ $(foreach _ndk_mod_module,$1,\ $(call -ndk-mod-get-depends)\ $(foreach _ndk_mod_dep,$(_ndk_mod_depends),\ $(call -ndk-mod-topo-increment-incoming,$(_ndk_mod_dep))\ )\ ) -ndk-mod-topo-sort = \ $(call -ndk-topo-debug,-ndk-mod-topo-sort: wq='$(_ndk_mod_wq)' list='$(_ndk_mod_list)')\ $(call -ndk-mod-pop-first)\ $(if $(_ndk_mod_module),\ $(eval _ndk_mod_list += $(_ndk_mod_module))\ $(eval _ndk_mod_nodes := $(filter-out $(_ndk_mod_module),$(_ndk_mod_nodes)))\ $(call -ndk-mod-topo-decrement-incoming,$(_ndk_mod_module))\ $(call -ndk-mod-get-depends)\ $(call -ndk-topo-debug,-ndk-mod-topo-sort: deps='$(_ndk_mod_depends)')\ $(foreach _ndk_mod_dep,$(_ndk_mod_depends),\ $(call -ndk-mod-topo-decrement-incoming,$(_ndk_mod_dep))\ $(if $(call -ndk-mod-topo-has-incoming,$(_ndk_mod_dep)),,\ $(call -ndk-mod-push-back,$(_ndk_mod_dep))\ )\ )\ $(call -ndk-mod-topo-sort)\ ) -test-ndk-mod-get-topo-list.empty = \ $(eval -local-deps = $$($$1_depends))\ $(call test-expect,,$(call -ndk-mod-get-topo-list,,-local-deps)) -test-ndk-mod-get-topo-list.single = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends :=)\ $(call test-expect,A,$(call -ndk-mod-get-topo-list,A,-local-deps)) -test-ndk-mod-get-topo-list.no-infinite-loop = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends := C)\ $(eval C_depends := A)\ $(call test-expect,A B C,$(call -ndk-mod-get-topo-list,A,-local-deps)) -test-ndk-mod-get-topo-list.ABC = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends :=)\ $(eval C_depends := B)\ $(call test-expect,A C B,$(call -ndk-mod-get-topo-list,A,-local-deps)) -test-ndk-mod-get-topo-list.ABCD = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends := D)\ $(eval C_depends := B)\ $(eval D_depends :=)\ $(call test-expect,A C B D,$(call -ndk-mod-get-topo-list,A,-local-deps)) -test-ndk-mod-get-topo-list.ABC.circular = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends := C)\ $(eval C_depends := B)\ $(call test-expect,A B C,$(call -ndk-mod-get-topo-list,A,-local-deps)) ######################################################################### # Return the topologically ordered closure of all dependencies from a # top-level node. # # $1: top-level node name. # $2: dependency function, i.e. $(call $2,<name>) returns the children # nodes for <name>. # Return: list of nodes, include $1, which will never be included. ######################################################################### -ndk-mod-get-topological-depends = $(call rest,$(call -ndk-mod-get-topo-list,$1,$2)) -test-ndk-mod-get-topological-depends.simple = \ $(eval -local-get-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends :=)\ $(eval topo_deps := $$(call -ndk-mod-get-topological-depends,A,-local-get-deps))\ $(call test-expect,B,$(topo_deps),topo dependencies) -test-ndk-mod-get-topological-depends.ABC = \ $(eval -local-get-deps = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends :=)\ $(eval C_depends := B)\ $(eval bfs_deps := $$(call -ndk-mod-get-bfs-depends,A,-local-get-deps))\ $(eval topo_deps := $$(call -ndk-mod-get-topological-depends,A,-local-get-deps))\ $(call test-expect,B C,$(bfs_deps),dfs dependencies)\ $(call test-expect,C B,$(topo_deps),topo dependencies) -test-ndk-mod-get-topological-depends.circular = \ $(eval -local-get-deps = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends := C)\ $(eval C_depends := B)\ $(eval bfs_deps := $$(call -ndk-mod-get-bfs-depends,A,-local-get-deps))\ $(eval topo_deps := $$(call -ndk-mod-get-topological-depends,A,-local-get-deps))\ $(call test-expect,B C,$(bfs_deps),dfs dependencies)\ $(call test-expect,B C,$(topo_deps),topo dependencies) ######################################################################### # Return breadth-first walk of a graph, starting from an arbitrary # node. # # This performs a breadth-first walk of the graph and will return a # list of nodes. Note that $1 will always be the first in the list. # # $1: root node name. # $2: dependency function, i.e. $(call $2,<name>) returns the nodes # that <name> depends on. # Result: list of dependent modules, $1 will be part of it. ######################################################################### -ndk-mod-get-bfs-list = $(strip \ $(eval _ndk_mod_wq := $(call strip-lib-prefix,$1)) \ $(eval _ndk_mod_deps_func := $2)\ $(eval _ndk_mod_list :=)\ $(call -ndk-mod-tree-setup-visit)\ $(call -ndk-mod-tree-set-visited,$(_ndk_mod_wq))\ $(call -ndk-mod-bfs-recursive) \ $(_ndk_mod_list)) # Recursive function used to perform a depth-first scan. # Must initialize _ndk_mod_list, _ndk_mod_field, _ndk_mod_wq # before calling this. -ndk-mod-bfs-recursive = \ $(call -ndk-mod-debug,-ndk-mod-bfs-recursive wq='$(_ndk_mod_wq)' list='$(_ndk_mod_list)' visited='$(_ndk_mod_tree_visitors)')\ $(call -ndk-mod-pop-first)\ $(eval _ndk_mod_list += $$(_ndk_mod_module))\ $(call -ndk-mod-get-depends)\ $(call -ndk-mod-debug,. node='$(_ndk_mod_module)' deps='$(_ndk_mod_depends)')\ $(foreach _ndk_mod_child,$(_ndk_mod_depends),\ $(if $(call -ndk-mod-tree-is-visited,$(_ndk_mod_child)),,\ $(call -ndk-mod-tree-set-visited,$(_ndk_mod_child))\ $(call -ndk-mod-push-back,$(_ndk_mod_child))\ )\ )\ $(if $(_ndk_mod_wq),$(call -ndk-mod-bfs-recursive)) -test-ndk-mod-get-bfs-list.empty = \ $(eval -local-deps = $$($$1_depends))\ $(call test-expect,,$(call -ndk-mod-get-bfs-list,,-local-deps)) -test-ndk-mod-get-bfs-list.A = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends :=)\ $(call test-expect,A,$(call -ndk-mod-get-bfs-list,A,-local-deps)) -test-ndk-mod-get-bfs-list.ABCDEF = \ $(eval -local-deps = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends := D E)\ $(eval C_depends := F E)\ $(eval D_depends :=)\ $(eval E_depends :=)\ $(eval F_depends :=)\ $(call test-expect,A B C D E F,$(call -ndk-mod-get-bfs-list,A,-local-deps)) ######################################################################### # Return breadth-first walk of a graph, starting from an arbitrary # node. # # This performs a breadth-first walk of the graph and will return a # list of nodes. Note that $1 will _not_ be part of the list. # # $1: root node name. # $2: dependency function, i.e. $(call $2,<name>) returns the nodes # that <name> depends on. # Result: list of dependent modules, $1 will not be part of it. ######################################################################### -ndk-mod-get-bfs-depends = $(call rest,$(call -ndk-mod-get-bfs-list,$1,$2)) -test-ndk-mod-get-bfs-depends.simple = \ $(eval -local-deps-func = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends :=)\ $(eval deps := $$(call -ndk-mod-get-bfs-depends,A,-local-deps-func))\ $(call test-expect,B,$(deps)) -test-ndk-mod-get-bfs-depends.ABC = \ $(eval -local-deps-func = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends :=)\ $(eval C_depends := B)\ $(eval deps := $$(call -ndk-mod-get-bfs-depends,A,-local-deps-func))\ $(call test-expect,B C,$(deps))\ -test-ndk-mod-get-bfs-depends.ABCDE = \ $(eval -local-deps-func = $$($$1_depends))\ $(eval A_depends := B C)\ $(eval B_depends := D)\ $(eval C_depends := D E F)\ $(eval D_depends :=)\ $(eval E_depends :=)\ $(eval F_depends :=)\ $(eval deps := $$(call -ndk-mod-get-bfs-depends,A,-local-deps-func))\ $(call test-expect,B C D E F,$(deps))\ -test-ndk-mod-get-bfs-depends.loop = \ $(eval -local-deps-func = $$($$1_depends))\ $(eval A_depends := B)\ $(eval B_depends := A)\ $(eval deps := $$(call -ndk-mod-get-bfs-depends,A,-local-deps-func))\ $(call test-expect,B,$(deps))