Merge "Enable plugin for GCC 4.6"
diff --git a/build/core/build-binary.mk b/build/core/build-binary.mk
index 66ff928..1fa3021 100644
--- a/build/core/build-binary.mk
+++ b/build/core/build-binary.mk
@@ -282,21 +282,6 @@
 #
 # Handle the static and shared libraries this module depends on
 #
-LOCAL_STATIC_LIBRARIES       := $(call strip-lib-prefix,$(LOCAL_STATIC_LIBRARIES))
-LOCAL_WHOLE_STATIC_LIBRARIES := $(call strip-lib-prefix,$(LOCAL_WHOLE_STATIC_LIBRARIES))
-LOCAL_SHARED_LIBRARIES       := $(call strip-lib-prefix,$(LOCAL_SHARED_LIBRARIES))
-
-# Transitive closure of static libraries
-LOCAL_STATIC_LIBRARIES       := $(call module-get-depends,$(LOCAL_STATIC_LIBRARIES),STATIC_LIBRARIES)
-LOCAL_WHOLE_STATIC_LIBRARIES := $(call module-get-depends,$(LOCAL_WHOLE_STATIC_LIBRARIES),WHOLE_STATIC_LIBRARIES)
-
-static_libraries       := $(call map,module-get-built,$(LOCAL_STATIC_LIBRARIES))
-whole_static_libraries := $(call map,module-get-built,$(LOCAL_WHOLE_STATIC_LIBRARIES))
-
-shared_libraries := $(call map,module-get-built,$(LOCAL_SHARED_LIBRARIES))\
-                    $(TARGET_PREBUILT_SHARED_LIBRARIES)
-
-$(LOCAL_BUILT_MODULE): $(static_libraries) $(whole_static_libraries) $(shared_libraries)
 
 # If LOCAL_LDLIBS contains anything like -l<library> then
 # prepend a -L$(SYSROOT)/usr/lib to it to ensure that the linker
@@ -306,20 +291,6 @@
     LOCAL_LDLIBS := -L$(call host-path,$(SYSROOT)/usr/lib) $(LOCAL_LDLIBS)
 endif
 
-# The list of object/static/shared libraries passed to the linker when
-# building shared libraries and executables. order is important.
-#
-# Cannot use immediate evaluation because PRIVATE_LIBGCC may not be defined at this point.
-linker_objects_and_libraries = $(strip $(call TARGET-get-linker-objects-and-libraries,\
-    $(LOCAL_OBJECTS), \
-    $(static_libraries), \
-    $(whole_static_libraries), \
-    $(shared_libraries)))
-
-# The list of object files sent to 'ar' when building static libraries
-#
-ar_objects := $(call host-path,$(LOCAL_OBJECTS))
-
 # When LOCAL_SHORT_COMMANDS is defined to 'true' we are going to write the
 # list of all object files and/or static/shared libraries that appear on the
 # command line to a file, then use the @<listfile> syntax to invoke it.
@@ -331,39 +302,10 @@
 ifndef LOCAL_SHORT_COMMANDS
     LOCAL_SHORT_COMMANDS := $(strip $(NDK_APP_SHORT_COMMANDS))
 endif
-ifeq ($(LOCAL_SHORT_COMMANDS),true)
-    # For static and whole static libraries
-    ifneq (,$(filter STATIC_LIBRARY WHOLE_STATIC_LIBRARY,$(call module-get-class,$(LOCAL_MODULE))))
-        $(call ndk_log,Building static library module '$(LOCAL_MODULE)' with linker list file)
-        ar_options   := $(ar_objects)
-        ar_list_file := $(LOCAL_OBJS_DIR)/archiver.list
-        ar_objects   := @$(call host-path,$(ar_list_file))
-        $(call generate-list-file,$(ar_options),$(ar_list_file))
-
-        $(LOCAL_BUILT_MODULE): $(ar_list_file)
-    endif
-
-    # For shared libraries and executables
-    ifneq (,$(filter SHARED_LIBRARY EXECUTABLE,$(call module-get-class,$(LOCAL_MODULE))))
-        $(call ndk_log,Building ELF binary module '$(LOCAL_MODULE)' with linker list file)
-        linker_options   := $(linker_objects_and_libraries)
-        linker_list_file := $(LOCAL_OBJS_DIR)/linker.list
-        linker_objects_and_libraries := @$(call host-path,$(linker_list_file))
-
-        $(call generate-list-file,$(linker_options),$(linker_list_file))
-
-        $(LOCAL_BUILT_MODULE): $(linker_list_file)
-    endif
-
-endif
 
 $(call generate-file-dir,$(LOCAL_BUILT_MODULE))
 
-$(LOCAL_BUILT_MODULE): PRIVATE_STATIC_LIBRARIES := $(static_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_WHOLE_STATIC_LIBRARIES := $(whole_static_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBRARIES := $(shared_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_OBJECTS          := $(LOCAL_OBJECTS)
-$(LOCAL_BUILT_MODULE): PRIVATE_LINKER_OBJECTS_AND_LIBRARIES := $(linker_objects_and_libraries)
+$(LOCAL_BUILT_MODULE): PRIVATE_OBJECTS := $(LOCAL_OBJECTS)
 $(LOCAL_BUILT_MODULE): PRIVATE_LIBGCC := $(TARGET_LIBGCC)
 
 $(LOCAL_BUILT_MODULE): PRIVATE_LD := $(TARGET_LD)
@@ -373,29 +315,137 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_NAME := $(notdir $(LOCAL_BUILT_MODULE))
 $(LOCAL_BUILT_MODULE): PRIVATE_CXX := $(TARGET_CXX)
 $(LOCAL_BUILT_MODULE): PRIVATE_CC := $(TARGET_CC)
-$(LOCAL_BUILT_MODULE): PRIVATE_AR := $(TARGET_AR) $(TARGET_ARFLAGS)
-$(LOCAL_BUILT_MODULE): PRIVATE_AR_OBJECTS := $(ar_objects)
 $(LOCAL_BUILT_MODULE): PRIVATE_SYSROOT := $(SYSROOT)
-$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_SHARED_LIB := $(cmd-build-shared-library)
-$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_STATIC_LIB := $(cmd-build-static-library)
-$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_EXECUTABLE := $(cmd-build-executable)
+
+ifeq ($(call module-get-class,$(LOCAL_MODULE)),STATIC_LIBRARY)
 
 #
-# If this is a static library module
+# This is a static library module, things are very easy. We only need
+# to build the object files and archive them with 'ar'. Note that module
+# dependencies can be ignored here, i.e. if the module depends on other
+# static or shared libraries, there is no need to actually build them
+# before, so don't add Make dependencies to them.
 #
-ifeq ($(call module-get-class,$(LOCAL_MODULE)),STATIC_LIBRARY)
+# In other words, consider the following graph:
+#
+#     libfoo.so -> libA.a ->libB.a
+#
+# then libA.a and libB.a can be built in parallel, only linking libfoo.so
+# depends on their completion.
+#
+
+ar_objects := $(call host-path,$(LOCAL_OBJECTS))
+
+ifeq ($(LOCAL_SHORT_COMMANDS),true)
+    $(call ndk_log,Building static library module '$(LOCAL_MODULE)' with linker list file)
+    ar_list_file := $(LOCAL_OBJS_DIR)/archiver.list
+    $(call generate-list-file,$(ar_objects),$(ar_list_file))
+    ar_objects   := @$(call host-path,$(ar_list_file))
+    $(LOCAL_BUILT_MODULE): $(ar_list_file)
+endif
+
+$(LOCAL_BUILT_MODULE): PRIVATE_AR := $(TARGET_AR) $(TARGET_ARFLAGS)
+$(LOCAL_BUILT_MODULE): PRIVATE_AR_OBJECTS := $(ar_objects)
+$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_STATIC_LIB := $(cmd-build-static-library)
+
 $(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
 	@ $(HOST_ECHO) "StaticLibrary  : $(PRIVATE_NAME)"
 	$(hide) $(call host-rm,$@)
 	$(hide) $(PRIVATE_BUILD_STATIC_LIB)
 
 ALL_STATIC_LIBRARIES += $(LOCAL_BUILT_MODULE)
+
+endif
+
+ifneq (,$(filter SHARED_LIBRARY EXECUTABLE,$(call module-get-class,$(LOCAL_MODULE))))
+
+#
+# This is a shared library or an executable, so computing dependencies properly is
+# crucial. The general rule to apply is the following:
+#
+#   - collect the list of all static libraries that need to be part
+#     of the link, and in the right order. To do so, get the transitive
+#     closure of LOCAL_STATIC_LIBRARIES and LOCAL_WHOLE_STATIC_LIBRARIES
+#     and ensure they are ordered topologically.
+#
+#  - collect the list of all shared libraries that need to be part of
+#    the link. This is the transitive closure of the list of
+#    LOCAL_SHARED_LIBRARIES for the module and all its dependent static
+#    libraries identified in the step above. Of course, need to be
+#    ordered topologically too.
+#
+#  - add Make dependencies to ensure that all these libs are built
+#    before the module itself too.
+#
+# A few quick examples:
+#
+#    main.exe -> libA.a -> libB.a -> libfoo.so -> libC.a
+#
+#      static_libs(main.exe) = libA.a libB.a  (i.e. no libC.a)
+#      shared_libs(main.exe) = libfoo.so
+#      static_libs(libfoo.so) = libC.a
+#
+#    main.exe -> libA.a ---> libB.a
+#                  |           ^
+#                  v           |
+#                libC.a  ------
+#
+#      static_libs(main.exe) = libA.a libC.a libB.a
+#             (i.e. libB.a must appear after all libraries that depend on it).
+#
+all_libs := $(call module-get-link-libs,$(LOCAL_MODULE))
+shared_libs := $(call module-filter-shared-libraries,$(all_libs))
+static_libs := $(call module-filter-static-libraries,$(all_libs))
+whole_static_libs := $(call module-extract-whole-static-libs,$(LOCAL_MODULE),$(static_libs))
+static_libs := $(filter-out $(whole_static_libs),$(static_libs))
+
+$(call -ndk-mod-debug,module $(LOCAL_MODULE) [$(LOCAL_BUILT_MODULE)])
+$(call -ndk-mod-debug,.  all_libs='$(all_libs)')
+$(call -ndk-mod-debug,.  shared_libs='$(shared_libs)')
+$(call -ndk-mod-debug,.  static_libs='$(static_libs)')
+$(call -ndk-mod-debug,.  whole_static_libs='$(whole_static_libs)')
+
+shared_libs       := $(call map,module-get-built,$(shared_libs))\
+                     $(TARGET_PREBUILT_SHARED_LIBRARIES)
+static_libs       := $(call map,module-get-built,$(static_libs))
+whole_static_libs := $(call map,module-get-built,$(whole_static_libs))
+
+$(call -ndk-mod-debug,.  built_shared_libs='$(shared_libs)')
+$(call -ndk-mod-debug,.  built_static_libs='$(static_libs)')
+$(call -ndk-mod-debug,.  built_whole_static_libs='$(whole_static_libs)')
+
+ifeq ($(LOCAL_SHORT_COMMANDS),true)
+    $(call ndk_log,Building ELF binary module '$(LOCAL_MODULE)' with linker list file)
+    linker_options   := $(linker_objects_and_libraries)
+    linker_list_file := $(LOCAL_OBJS_DIR)/linker.list
+    linker_objects_and_libraries := @$(call host-path,$(linker_list_file))
+    $(call generate-list-file,$(linker_options),$(linker_list_file))
+    $(LOCAL_BUILT_MODULE): $(linker_list_file)
+endif
+
+# The list of object/static/shared libraries passed to the linker when
+# building shared libraries and executables. order is important.
+#
+# Cannot use immediate evaluation because PRIVATE_LIBGCC may not be defined at this point.
+linker_objects_and_libraries = $(strip $(call TARGET-get-linker-objects-and-libraries,\
+    $(LOCAL_OBJECTS), \
+    $(static_libs), \
+    $(whole_static_libs), \
+    $(shared_libs)))
+
+$(LOCAL_BUILT_MODULE): $(shared_libs) $(static_libs) $(whole_static_libs)
+$(LOCAL_BUILT_MODULE): PRIVATE_LINKER_OBJECTS_AND_LIBRARIES := $(linker_objects_and_libraries)
+$(LOCAL_BUILT_MODULE): PRIVATE_STATIC_LIBRARIES := $(static_libs)
+$(LOCAL_BUILT_MODULE): PRIVATE_WHOLE_STATIC_LIBRARIES := $(whole_static_libs))
+$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBRARIES := $(shared_libs))
+
 endif
 
 #
 # If this is a shared library module
 #
 ifeq ($(call module-get-class,$(LOCAL_MODULE)),SHARED_LIBRARY)
+$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_SHARED_LIB := $(cmd-build-shared-library)
 $(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
 	@ $(HOST_ECHO) "SharedLibrary  : $(PRIVATE_NAME)"
 	$(hide) $(PRIVATE_BUILD_SHARED_LIB)
@@ -407,6 +457,7 @@
 # If this is an executable module
 #
 ifeq ($(call module-get-class,$(LOCAL_MODULE)),EXECUTABLE)
+$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_EXECUTABLE := $(cmd-build-executable)
 $(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
 	@ $(HOST_ECHO) "Executable     : $(PRIVATE_NAME)"
 	$(hide) $(PRIVATE_BUILD_EXECUTABLE)
diff --git a/build/core/definitions-graph.mk b/build/core/definitions-graph.mk
new file mode 100644
index 0000000..6652a1f
--- /dev/null
+++ b/build/core/definitions-graph.mk
@@ -0,0 +1,523 @@
+# 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)))\
+    $(if $(_ndk_mod_wq),\
+        $(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))\
+        $(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))
+
+#########################################################################
+# 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)
+
+#########################################################################
+# 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))
diff --git a/build/core/definitions-host.mk b/build/core/definitions-host.mk
new file mode 100644
index 0000000..c215687
--- /dev/null
+++ b/build/core/definitions-host.mk
@@ -0,0 +1,136 @@
+# Copyright (C) 2009 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.
+#
+
+# These definitions contain a few host-specific functions. I.e. they are
+# typically used to generate shell commands during the build and their
+# implementation will depend on the value of the HOST_OS variable.
+#
+
+# -----------------------------------------------------------------------------
+# Function : host-path
+# Arguments: 1: file path
+# Returns  : file path, as understood by the host file system
+# Usage    : $(call host-path,<path>)
+# Rationale: This function is used to translate Cygwin paths into
+#            Cygwin-specific ones. On other platforms, it will just
+#            return its argument.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),cygwin)
+host-path = $(if $(strip $1),$(call cygwin-to-host-path,$1))
+else
+host-path = $1
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-rm
+# Arguments: 1: list of files
+# Usage    : $(call host-rm,<files>)
+# Rationale: This function expands to the host-specific shell command used
+#            to remove some files.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-rm = \
+    $(eval __host_rm_files := $(foreach __host_rm_file,$1,$(subst /,\,$(wildcard $(__host_rm_file)))))\
+    $(if $(__host_rm_files),del /f/q $(__host_rm_files) >NUL 2>NUL)
+else
+host-rm = rm -f $1
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-rmdir
+# Arguments: 1: list of files or directories
+# Usage    : $(call host-rm,<files>)
+# Rationale: This function expands to the host-specific shell command used
+#            to remove some files _and_ directories.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-rmdir = \
+    $(eval __host_rmdir_files := $(foreach __host_rmdir_file,$1,$(subst /,\,$(wildcard $(__host_rmdir_file)))))\
+    $(if $(__host_rmdir_files),del /f/s/q $(__host_rmdir_files) >NUL 2>NUL)
+else
+host-rmdir = rm -rf $1
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-mkdir
+# Arguments: 1: directory path
+# Usage    : $(call host-mkdir,<path>
+# Rationale: This function expands to the host-specific shell command used
+#            to create a path if it doesn't exist.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-mkdir = md $(subst /,\,"$1") >NUL 2>NUL || rem
+else
+host-mkdir = mkdir -p $1
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-cp
+# Arguments: 1: source file
+#            2: target file
+# Usage    : $(call host-cp,<src-file>,<dst-file>)
+# Rationale: This function expands to the host-specific shell command used
+#            to copy a single file
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-cp = copy /b/y $(subst /,\,"$1" "$2") > NUL
+else
+host-cp = cp -f $1 $2
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-install
+# Arguments: 1: source file
+#            2: target file
+# Usage    : $(call host-install,<src-file>,<dst-file>)
+# Rationale: This function expands to the host-specific shell command used
+#            to install a file or directory, while preserving its timestamps
+#            (if possible).
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-install = copy /b/y $(subst /,\,"$1" "$2") > NUL
+else
+host-install = install -p $1 $2
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-c-includes
+# Arguments: 1: list of file paths (e.g. "foo bar")
+# Returns  : list of include compiler options (e.g. "-Ifoo -Ibar")
+# Usage    : $(call host-c-includes,<paths>)
+# Rationale: This function is used to translate Cygwin paths into
+#            Cygwin-specific ones. On other platforms, it will just
+#            return its argument.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),cygwin)
+host-c-includes = $(patsubst %,-I%,$(call host-path,$1))
+else
+host-c-includes = $(1:%=-I%)
+endif
+
+# -----------------------------------------------------------------------------
+# Function : host-copy-if-differ
+# Arguments: 1: source file
+#            2: destination file
+# Usage    : $(call host-copy-if-differ,<src-file>,<dst-file>)
+# Rationale: This function copy source file to destination file if contents are
+#            different.
+# -----------------------------------------------------------------------------
+ifeq ($(HOST_OS),windows)
+host-copy-if-differ = $(HOST_CMP) -s $1 $2 > NUL || copy /b/y $(subst /,\,"$1" "$2") > NUL
+else
+host-copy-if-differ = $(HOST_CMP) -s $1 $2 > /dev/null 2>&1 || cp -f $1 $2
+endif
+
diff --git a/build/core/definitions-tests.mk b/build/core/definitions-tests.mk
new file mode 100644
index 0000000..074036b
--- /dev/null
+++ b/build/core/definitions-tests.mk
@@ -0,0 +1,106 @@
+# 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 for the Android NDK build system's internal unit tests.
+#
+
+#
+# A function which names begin with -test- (e.g. -test-foo) is assumed
+# to be an internal unit test. It will be run automatically by ndk-build
+# if NDK_UNIT_TESTS is defined in your environment.
+#
+# Each test should call one of the following functions that will
+# register a failure:
+#
+#   $(call test-expect,<expected-value>,<actual-value>)
+#
+#      This will check that <actual-value> is equal to <expected-value>.
+#      If not, this will print an error message and increment the failure
+#      counter.
+#
+#   $(call test-assert,<expected-value>,<actual-value>)
+#
+#      This is similar to test-expect, though it will abort the program
+#      immediately after displaying an error message.
+#
+# Here's an example that checks that the 'filter' function works correctly:
+#
+#   -test-filter = \
+#     $(call test-expect,foo,$(filter bar,foo bar))
+#
+#
+
+-ndk-test-start = \
+  $(eval _test_name := $1)\
+  $(eval _test_list += $1)\
+  $(eval _test_failed :=)\
+  $(info [$1  RUN])
+
+# End current unit test.
+#
+-ndk-test-end = \
+  $(if $(_test_failed),\
+    $(info [$(_test_name) FAIL])$(error Aborting)\
+    $(eval _test_failures += $$(_test_name))\
+  ,\
+    $(info [$(_test_name)   OK])\
+  )
+
+# Define NDK_UNIT_TESTS to 2 to dump each test-expect/assert check.
+#
+ifeq (2,$(NDK_UNIT_TESTS))
+-ndk-test-log = $(info .  $(_test_name): $1)
+else
+-ndk-test-log = $(empty)
+endif
+
+test-expect = \
+  $(call -ndk-test-log,expect '$2' == '$1')\
+  $(if $(call sne,$1,$2),\
+    $(info ERROR <$(_test_name)>:$3)\
+    $(info .  expected value:'$1')\
+    $(info .  actual value:  '$2')\
+    $(eval _test_failed := true)\
+  )
+
+test-assert = \
+  $(call -ndk-test-log,assert '$2' == '$1')\
+  $(if $(call sne,$1,$2),\
+    $(info ASSERT <$(_test_name)>:$3)\
+    $(info .  expected value:'$1')\
+    $(info .  actual value:  '$2')\
+    $(eval _test_failed := true)\
+    $(error Aborting.)\
+  )
+
+# Run all the tests, i.e. all functions that are defined with a -test-
+# prefix will be called now in succession.
+ndk-run-all-tests = \
+  $(info ================= STARTING NDK-BUILD UNIT TESTS =================)\
+  $(eval _test_list :=)\
+  $(eval _test_failures :=)\
+  $(foreach _test,$(filter -test-%,$(.VARIABLES)),\
+    $(call -ndk-test-start,$(_test))\
+    $(call $(_test))\
+    $(call -ndk-test-end)\
+  )\
+  $(eval _test_count := $$(words $$(_test_list)))\
+  $(eval _test_fail_count := $$(words $$(_test_failures)))\
+  $(if $(_test_failures),\
+    $(info @@@@@@@@@@@ FAILED $(_test_fail_count) of $(_test_count) NDK-BUILD UNIT TESTS @@@@@@@)\
+    $(foreach _test_name,$(_test_failures),\
+      $(info .  $(_test_name)))\
+  ,\
+    $(info =================== PASSED $(_test_count) NDK-BUILD UNIT TESTS =================)\
+  )
diff --git a/build/core/definitions-utils.mk b/build/core/definitions-utils.mk
new file mode 100644
index 0000000..0f24404
--- /dev/null
+++ b/build/core/definitions-utils.mk
@@ -0,0 +1,170 @@
+# Copyright (C) 2009 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.
+#
+
+# Common utility functions.
+#
+# NOTE: All the functions here should be purely functional, i.e. avoid
+#       using global variables or depend on the file system / environment
+#       variables. This makes testing easier.
+
+# -----------------------------------------------------------------------------
+# Macro    : empty
+# Returns  : an empty macro
+# Usage    : $(empty)
+# -----------------------------------------------------------------------------
+empty :=
+
+# -----------------------------------------------------------------------------
+# Macro    : space
+# Returns  : a single space
+# Usage    : $(space)
+# -----------------------------------------------------------------------------
+space  := $(empty) $(empty)
+
+space4 := $(space)$(space)$(space)$(space)
+
+# -----------------------------------------------------------------------------
+# Function : remove-duplicates
+# Arguments: a list
+# Returns  : the list with duplicate items removed, order is preserved.
+# Usage    : $(call remove-duplicates, <LIST>)
+# Note     : This is equivalent to the 'uniq' function provided by GMSL,
+#            however this implementation is non-recursive and *much*
+#            faster. It will also not explode the stack with a lot of
+#            items like 'uniq' does.
+# -----------------------------------------------------------------------------
+remove-duplicates = $(strip \
+  $(eval __uniq_ret :=) \
+  $(foreach __uniq_item,$1,\
+    $(if $(findstring $(__uniq_item),$(__uniq_ret)),,\
+      $(eval __uniq_ret += $(__uniq_item))\
+    )\
+  )\
+  $(__uniq_ret))
+
+-test-remove-duplicates = \
+  $(call test-expect,,$(call remove-duplicates))\
+  $(call test-expect,foo bar,$(call remove-duplicates,foo bar))\
+  $(call test-expect,foo bar,$(call remove-duplicates,foo bar foo bar))\
+  $(call test-expect,foo bar,$(call remove-duplicates,foo foo bar bar bar))
+
+# -----------------------------------------------------------------------------
+# Function : clear-vars
+# Arguments: 1: list of variable names
+#            2: file where the variable should be defined
+# Returns  : None
+# Usage    : $(call clear-vars, VAR1 VAR2 VAR3...)
+# Rationale: Clears/undefines all variables in argument list
+# -----------------------------------------------------------------------------
+clear-vars = $(foreach __varname,$1,$(eval $(__varname) := $(empty)))
+
+# -----------------------------------------------------------------------------
+# Function : filter-by
+# Arguments: 1: list
+#            2: predicate function, will be called as $(call $2,<name>)
+#               and it this returns a non-empty value, then <name>
+#               will be appended to the result.
+# Returns  : elements of $1 that satisfy the predicate function $2
+# -----------------------------------------------------------------------------
+filter-by = $(strip \
+  $(foreach __filter_by_n,$1,\
+    $(if $(call $2,$(__filter_by_n)),$(__filter_by_n))))
+
+-test-filter-by = \
+    $(eval -local-func = $$(call seq,foo,$$1))\
+    $(call test-expect,,$(call filter-by,,-local-func))\
+    $(call test-expect,foo,$(call filter-by,foo,-local-func))\
+    $(call test-expect,foo,$(call filter-by,foo bar,-local-func))\
+    $(call test-expect,foo foo,$(call filter-by,aaa foo bar foo,-local-func))\
+    $(eval -local-func = $$(call sne,foo,$$1))\
+    $(call test-expect,,$(call filter-by,,-local-func))\
+    $(call test-expect,,$(call filter-by,foo,-local-func))\
+    $(call test-expect,bar,$(call filter-by,foo bar,-local-func))\
+    $(call test-expect,aaa bar,$(call filter-by,aaa foo bar,-local-func))
+
+# -----------------------------------------------------------------------------
+# Function : filter-out-by
+# Arguments: 1: list
+#            2: predicate function, will be called as $(call $2,<name>)
+#               and it this returns an empty value, then <name>
+#               will be appended to the result.
+# Returns  : elements of $1 that do not satisfy the predicate function $2
+# -----------------------------------------------------------------------------
+filter-out-by = $(strip \
+  $(foreach __filter_out_by_n,$1,\
+    $(if $(call $2,$(__filter_out_by_n)),,$(__filter_out_by_n))))
+
+-test-filter-out-by = \
+    $(eval -local-func = $$(call seq,foo,$$1))\
+    $(call test-expect,,$(call filter-out-by,,-local-func))\
+    $(call test-expect,,$(call filter-out-by,foo,-local-func))\
+    $(call test-expect,bar,$(call filter-out-by,foo bar,-local-func))\
+    $(call test-expect,aaa bar,$(call filter-out-by,aaa foo bar foo,-local-func))\
+    $(eval -local-func = $$(call sne,foo,$$1))\
+    $(call test-expect,,$(call filter-out-by,,-local-func))\
+    $(call test-expect,foo,$(call filter-out-by,foo,-local-func))\
+    $(call test-expect,foo,$(call filter-out-by,foo bar,-local-func))\
+    $(call test-expect,foo foo,$(call filter-out-by,aaa foo bar foo,-local-func))
+
+# -----------------------------------------------------------------------------
+# Function : find-first
+# Arguments: 1: list
+#            2: predicate function, will be called as $(call $2,<name>).
+# Returns  : the first item of $1 that satisfies the predicate.
+# -----------------------------------------------------------------------------
+find-first = $(firstword $(call filter-by,$1,$2))
+
+-testfind-first.empty = \
+    $(eval -local-pred = $$(call seq,foo,$$1))\
+    $(call test-expect,,$(call find-first,,-local-pred))\
+    $(call test-expect,,$(call find-first,bar,-local-pred))
+
+-testfind-first.simple = \
+    $(eval -local-pred = $$(call seq,foo,$$1))\
+    $(call test-expect,foo,$(call find-first,foo,-local-pred))\
+    $(call test-expect,foo,$(call find-first,aaa foo bar,-local-pred))\
+    $(call test-expect,foo,$(call find-first,aaa foo foo bar,-local-pred))
+
+# -----------------------------------------------------------------------------
+# Function : parent-dir
+# Arguments: 1: path
+# Returns  : Parent dir or path of $1, with final separator removed.
+# -----------------------------------------------------------------------------
+parent-dir = $(patsubst %/,%,$(dir $(1:%/=%)))
+
+-test-parent-dir = \
+  $(call test-expect,,$(call parent-dir))\
+  $(call test-expect,.,$(call parent-dir,foo))\
+  $(call test-expect,foo,$(call parent-dir,foo/bar))\
+  $(call test-expect,foo,$(call parent-dir,foo/bar/))
+
+# -----------------------------------------------------------------------------
+# Strip any 'lib' prefix in front of a given string.
+#
+# Function : strip-lib-prefix
+# Arguments: 1: module name
+# Returns  : module name, without any 'lib' prefix if any
+# Usage    : $(call strip-lib-prefix,$(LOCAL_MODULE))
+# -----------------------------------------------------------------------------
+strip-lib-prefix = $(1:lib%=%)
+
+-test-strip-lib-prefix = \
+  $(call test-expect,,$(call strip-lib-prefix,))\
+  $(call test-expect,foo,$(call strip-lib-prefix,foo))\
+  $(call test-expect,foo,$(call strip-lib-prefix,libfoo))\
+  $(call test-expect,nolibfoo,$(call strip-lib-prefix,nolibfoo))\
+  $(call test-expect,foolib,$(call strip-lib-prefix,foolib))\
+  $(call test-expect,foo bar,$(call strip-lib-prefix,libfoo libbar))
+
diff --git a/build/core/definitions.mk b/build/core/definitions.mk
index 8e3aaf0..f139aea 100644
--- a/build/core/definitions.mk
+++ b/build/core/definitions.mk
@@ -18,69 +18,10 @@
 # We use the GNU Make Standard Library
 include $(NDK_ROOT)/build/gmsl/gmsl
 
-# If NDK_TRACE is enabled then calls to the library functions are
-# traced to stdout using warning messages with their arguments
-
-ifdef NDK_TRACE
-__ndk_tr1 = $(warning $0('$1'))
-__ndk_tr2 = $(warning $0('$1','$2'))
-__ndk_tr3 = $(warning $0('$1','$2','$3'))
-else
-__ndk_tr1 :=
-__ndk_tr2 :=
-__ndk_tr3 :=
-endif
-
-# -----------------------------------------------------------------------------
-# Macro    : empty
-# Returns  : an empty macro
-# Usage    : $(empty)
-# -----------------------------------------------------------------------------
-empty :=
-
-# -----------------------------------------------------------------------------
-# Macro    : space
-# Returns  : a single space
-# Usage    : $(space)
-# -----------------------------------------------------------------------------
-space  := $(empty) $(empty)
-
-space4 := $(space)$(space)$(space)$(space)
-
-# -----------------------------------------------------------------------------
-# Function : last2
-# Arguments: a list
-# Returns  : the penultimate (next-to-last) element of a list
-# Usage    : $(call last2, <LIST>)
-# -----------------------------------------------------------------------------
-last2 = $(word $(words $1), x $1)
-
-# -----------------------------------------------------------------------------
-# Function : last3
-# Arguments: a list
-# Returns  : the antepenultimate (second-next-to-last) element of a list
-# Usage    : $(call last3, <LIST>)
-# -----------------------------------------------------------------------------
-last3 = $(word $(words $1), x x $1)
-
-# -----------------------------------------------------------------------------
-# Function : remove-duplicates
-# Arguments: a list
-# Returns  : the list with duplicate items removed, order is preserved.
-# Usage    : $(call remove-duplicates, <LIST>)
-# Note     : This is equivalent to the 'uniq' function provided by GMSL,
-#            however this implementation is non-recursive and *much*
-#            faster. It will also not explode the stack with a lot of
-#            items like 'uniq' does.
-# -----------------------------------------------------------------------------
-remove-duplicates = $(strip \
-  $(eval __uniq_ret :=) \
-  $(foreach __uniq_item,$1,\
-    $(if $(findstring $(__uniq_item),$(__uniq_ret)),,\
-      $(eval __uniq_ret += $(__uniq_item))\
-    )\
-  )\
-  $(__uniq_ret))
+include $(BUILD_SYSTEM)/definitions-tests.mk
+include $(BUILD_SYSTEM)/definitions-utils.mk
+include $(BUILD_SYSTEM)/definitions-host.mk
+include $(BUILD_SYSTEM)/definitions-graph.mk
 
 # -----------------------------------------------------------------------------
 # Macro    : this-makefile
@@ -111,16 +52,6 @@
 )
 
 # -----------------------------------------------------------------------------
-# Function : clear-vars
-# Arguments: 1: list of variable names
-#            2: file where the variable should be defined
-# Returns  : None
-# Usage    : $(call clear-vars, VAR1 VAR2 VAR3...)
-# Rationale: Clears/undefines all variables in argument list
-# -----------------------------------------------------------------------------
-clear-vars = $(foreach __varname,$1,$(eval $(__varname) := $(empty)))
-
-# -----------------------------------------------------------------------------
 # Function : check-required-vars
 # Arguments: 1: list of variable names
 #            2: file where the variable(s) should be defined
@@ -140,122 +71,6 @@
 default-c++-extensions := .cc .cp .cxx .cpp .CPP .c++ .C
 
 # -----------------------------------------------------------------------------
-# Function : host-path
-# Arguments: 1: file path
-# Returns  : file path, as understood by the host file system
-# Usage    : $(call host-path,<path>)
-# Rationale: This function is used to translate Cygwin paths into
-#            Cygwin-specific ones. On other platforms, it will just
-#            return its argument.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),cygwin)
-host-path = $(if $(strip $1),$(call cygwin-to-host-path,$1))
-else
-host-path = $1
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-rm
-# Arguments: 1: list of files
-# Usage    : $(call host-rm,<files>)
-# Rationale: This function expands to the host-specific shell command used
-#            to remove some files.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-host-rm = \
-    $(eval __host_rm_files := $(foreach __host_rm_file,$1,$(subst /,\,$(wildcard $(__host_rm_file)))))\
-    $(if $(__host_rm_files),del /f/q $(__host_rm_files) >NUL 2>NUL)
-else
-host-rm = rm -f $1
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-rmdir
-# Arguments: 1: list of files or directories
-# Usage    : $(call host-rm,<files>)
-# Rationale: This function expands to the host-specific shell command used
-#            to remove some files _and_ directories.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-host-rmdir = \
-    $(eval __host_rmdir_files := $(foreach __host_rmdir_file,$1,$(subst /,\,$(wildcard $(__host_rmdir_file)))))\
-    $(if $(__host_rmdir_files),del /f/s/q $(__host_rmdir_files) >NUL 2>NUL)
-else
-host-rmdir = rm -rf $1
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-mkdir
-# Arguments: 1: directory path
-# Usage    : $(call host-mkdir,<path>
-# Rationale: This function expands to the host-specific shell command used
-#            to create a path if it doesn't exist.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-host-mkdir = md $(subst /,\,"$1") >NUL 2>NUL || rem
-else
-host-mkdir = mkdir -p $1
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-cp
-# Arguments: 1: source file
-#            2: target file
-# Usage    : $(call host-cp,<src-file>,<dst-file>)
-# Rationale: This function expands to the host-specific shell command used
-#            to copy a single file
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-host-cp = copy /b/y $(subst /,\,"$1" "$2") > NUL
-else
-host-cp = cp -f $1 $2
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-install
-# Arguments: 1: source file
-#            2: target file
-# Usage    : $(call host-install,<src-file>,<dst-file>)
-# Rationale: This function expands to the host-specific shell command used
-#            to install a file or directory, while preserving its timestamps
-#            (if possible).
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-host-install = copy /b/y $(subst /,\,"$1" "$2") > NUL
-else
-host-install = install -p $1 $2
-endif
-
-# -----------------------------------------------------------------------------
-# Function : host-c-includes
-# Arguments: 1: list of file paths (e.g. "foo bar")
-# Returns  : list of include compiler options (e.g. "-Ifoo -Ibar")
-# Usage    : $(call host-c-includes,<paths>)
-# Rationale: This function is used to translate Cygwin paths into
-#            Cygwin-specific ones. On other platforms, it will just
-#            return its argument.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),cygwin)
-host-c-includes = $(patsubst %,-I%,$(call host-path,$1))
-else
-host-c-includes = $(1:%=-I%)
-endif
-
-# -----------------------------------------------------------------------------
-# Function : copy-if-differ
-# Arguments: 1: source file
-#            2: destination file
-# Usage    : $(call copy-if-differ,<src-file>,<dst-file>)
-# Rationale: This function copy source file to destination file if contents are
-#            different.
-# -----------------------------------------------------------------------------
-ifeq ($(HOST_OS),windows)
-copy-if-differ = $(HOST_CMP) -s $1 $2 > NUL || copy /b/y $(subst /,\,"$1" "$2") > NUL
-else
-copy-if-differ = $(HOST_CMP) -s $1 $2 > /dev/null 2>&1 || cp -f $1 $2
-endif
-
-# -----------------------------------------------------------------------------
 # Function : generate-dir
 # Arguments: 1: directory path
 # Returns  : Generate a rule, but not dependency, to create a directory with
@@ -320,9 +135,27 @@
 index-is-zero = $(filter 0 00 000 0000 00000 000000 0000000,$1)
 bump-0-to-1 = $(if $(call index-is-zero,$1),1,$1)
 
+-test-bump-0-to-1 = \
+  $(call test-expect,$(call bump-0-to-1))\
+  $(call test-expect,1,$(call bump-0-to-1,0))\
+  $(call test-expect,1,$(call bump-0-to-1,1))\
+  $(call test-expect,2,$(call bump-0-to-1,2))\
+  $(call test-expect,1,$(call bump-0-to-1,00))\
+  $(call test-expect,1,$(call bump-0-to-1,000))\
+  $(call test-expect,1,$(call bump-0-to-1,0000))\
+  $(call test-expect,1,$(call bump-0-to-1,00000))\
+  $(call test-expect,1,$(call bump-0-to-1,000000))\
+  $(call test-expect,10,$(call bump-0-to-1,10))\
+  $(call test-expect,100,$(call bump-0-to-1,100))
+
 # Same as $(wordlist ...) except the start index, if 0, is bumped to 1
 index-word-list = $(wordlist $(call bump-0-to-1,$1),$2,$3)
 
+-test-index-word-list = \
+  $(call test-expect,,$(call index-word-list,1,1))\
+  $(call test-expect,a b,$(call index-word-list,0,2,a b c d))\
+  $(call test-expect,b c,$(call index-word-list,2,3,a b c d))\
+
 # NOTE: With GNU Make $1 and $(1) are equivalent, which means
 #       that $10 is equivalent to $(1)0, and *not* $(10).
 
@@ -466,7 +299,7 @@
 $(call list-file-maybe-gen-1000,5,$1)
 
 $$(__list_file): $$(__list_file).tmp
-	$$(hide) $$(call copy-if-differ,$$@.tmp,$$@)
+	$$(hide) $$(call host-copy-if-differ,$$@.tmp,$$@)
 	$$(hide) $$(call host-rm,$$@.tmp)
 
 endef
@@ -487,6 +320,13 @@
 link-whole-archives = $(if $(strip $1),$(call link-whole-archive-flags,$1))
 link-whole-archive-flags = -Wl,--whole-archive $(call host-path,$1) -Wl,--no-whole-archive
 
+-test-link-whole-archive = \
+  $(call test-expect,,$(call link-whole-archives))\
+  $(eval _start := -Wl,--whole-archive)\
+  $(eval _end := -Wl,--no-whole-archive)\
+  $(call test-expect,$(_start) foo $(_end),$(call link-whole-archives,foo))\
+  $(call test-expect,$(_start) foo bar $(_end),$(call link-whole-archives,foo bar))
+
 # =============================================================================
 #
 # Modules database
@@ -641,6 +481,8 @@
   $(if $(call module-class-is-installable,$(LOCAL_MODULE_CLASS)),\
     $(eval LOCAL_INSTALLED := $(NDK_APP_DST_DIR)/$(notdir $(LOCAL_BUILT_MODULE)))\
   )\
+  $(foreach __field,STATIC_LIBRARIES WHOLE_STATIC_LIBRARIES SHARED_LIBRARIES,\
+    $(eval LOCAL_$(__field) := $(call strip-lib-prefix,$(LOCAL_$(__field)))))\
   $(foreach __local,$(modules-LOCALS),\
     $(eval __ndk_modules.$1.$(__local) := $(LOCAL_$(__local)))\
   )\
@@ -704,7 +546,7 @@
 
 # Dump all module information. Only use this for debugging
 modules-dump-database = \
-    $(info Modules: $(__ndk_modules)) \
+    $(info Modules [$(TARGET_ARCH_ABI)]: $(__ndk_modules)) \
     $(foreach __mod,$(__ndk_modules),\
         $(info $(space4)$(__mod):)\
         $(foreach __field,$(modules-fields),\
@@ -751,7 +593,146 @@
 # NOTE: this function must not modify the existing dependency order when new depends are added.
 #
 module-add-depends-any = \
-    $(eval __ndk_modules.$1.$3 += $(filter-out $(__ndk_modules.$1.$3),$(call strip-lib-prefix,$2)))
+    $(eval __ndk_modules.$1.$3 += $(filter-out $(__ndk_modules.$1.$3),$2))
+
+
+# -----------------------------------------------------------------------------
+# Returns non-empty if a module is a static library
+# Arguments: 1: module name
+# Returns     : non-empty iff the module is a static library.
+# Usage       : $(if $(call module-is-static-library,<name>),...)
+# -----------------------------------------------------------------------------
+module-is-static-library = $(strip \
+  $(filter STATIC_LIBRARY PREBUILT_STATIC_LIBRARY,\
+    $(call module-get-class,$1)))
+
+# -----------------------------------------------------------------------------
+# Returns non-empty if a module is a shared library
+# Arguments: 1: module name
+# Returns     : non-empty iff the module is a shared library.
+# Usage       : $(if $(call module-is-shared-library,<name>),...)
+# -----------------------------------------------------------------------------
+module-is-shared-library = $(strip \
+  $(filter SHARED_LIBRARY PREBUILT_SHARED_LIBRARY,\
+    $(call module-get-class,$1)))
+
+# -----------------------------------------------------------------------------
+# Filter a list of module names to retain only the static libraries.
+# Arguments: 1: module name list
+# Returns     : input list modules which are static libraries.
+# -----------------------------------------------------------------------------
+module-filter-static-libraries = $(call filter-by,$1,module-is-static-library)
+
+# -----------------------------------------------------------------------------
+# Filter a list of module names to retain only the shared libraries.
+# Arguments: 1: module name list
+# Returns     : input list modules which are shared libraries.
+# -----------------------------------------------------------------------------
+module-filter-shared-libraries = $(call filter-by,$1,module-is-shared-library)
+
+# -----------------------------------------------------------------------------
+# Return the LOCAL_STATIC_LIBRARIES for a given module.
+# Arguments: 1: module name
+# Returns     : List of static library modules.
+# -----------------------------------------------------------------------------
+module-get-static-libs = $(__ndk_modules.$1.STATIC_LIBRARIES)
+
+# -----------------------------------------------------------------------------
+# Return the LOCAL_WHOLE_STATIC_LIBRARIES for a given module.
+# Arguments: 1: module name
+# Returns     : List of whole static library modules.
+# -----------------------------------------------------------------------------
+module-get-whole-static-libs = $(__ndk_modules.$1.WHOLE_STATIC_LIBRARIES)
+
+# -----------------------------------------------------------------------------
+# Return all static libraries for a given module.
+# Arguments: 1: module name
+# Returns     : List of static library modules (whole or not).
+# -----------------------------------------------------------------------------
+module-get-all-static-libs = $(strip \
+  $(__ndk_modules.$1.STATIC_LIBRARIES) \
+  $(__ndk_modules.$1.WHOLE_STATIC_LIBRARIES))
+
+# -----------------------------------------------------------------------------
+# Return the list of LOCAL_SHARED_LIBRARIES for a given module.
+# Arguments: 1: module name
+# Returns     : List of shared library modules.
+# -----------------------------------------------------------------------------
+module-get-shared-libs = $(__ndk_modules.$1.SHARED_LIBRARIES)
+
+# -----------------------------------------------------------------------------
+# Return the list of all libraries a modules depends directly on.
+# This is the concatenation of its LOCAL_STATIC_LIBRARIES,
+# LOCAL_WHOLE_STATIC_LIBRARIES, and LOCAL_SHARED_LIBRARIES variables.
+# Arguments: 1: module name
+# Returns     : List of library modules (static or shared).
+# -----------------------------------------------------------------------------
+module-get-direct-libs = $(strip \
+  $(__ndk_modules.$1.STATIC_LIBRARIES) \
+  $(__ndk_modules.$1.WHOLE_STATIC_LIBRARIES) \
+  $(__ndk_modules.$1.SHARED_LIBRARIES))
+
+
+# -----------------------------------------------------------------------------
+# Computes the full closure of a module and its dependencies. Order is
+# defined by a breadth-first walk of the graph.
+# $1 will be the first item in the result.
+#
+# Arguments: 1: module name
+# Returns     : List of all modules $1 depends on.
+#
+# Note: Do not use this to determine build dependencies. The returned list
+#       is much too large for this. For example consider the following
+#       dependency graph:
+#
+#   main.exe -> libA.a -> libfoo.so -> libB.a
+#
+#       This function will return all four modules in the result, while
+#       at link time building main.exe only requires the first three.
+#
+# -----------------------------------------------------------------------------
+module-get-all-dependencies = $(call -ndk-mod-get-closure,$1,module-get-depends)
+
+# -----------------------------------------------------------------------------
+# Compute the list of all static and shared libraries required to link a
+# given module.
+#
+# Note that the result is topologically ordered, i.e. if library A depends
+# on library B, then A will always appear after B in the result.
+#
+# Arguments: 1: module name
+# Returns     : List of all library $1 depends at link time.
+#
+# Note: This doesn't differentiate between regular and whole static
+#       libraries. Use module-extract-whole-static-libs to filter the
+#       result returned by this function.
+# -----------------------------------------------------------------------------
+module-get-link-libs = $(strip \
+  $(eval _ndk_mod_link_module := $1) \
+  $(call -ndk-mod-get-topological-depends,$1,-ndk-mod-link-deps))
+
+# Special dependency function used by nodule-get-link-libs.
+# The rules to follow are the following:
+#  - if $1 is the link module, or if it is a static library, then all
+#    direct dependencies.
+#  - otherwise, the module is a shared library, don't add build deps.
+-ndk-mod-link-deps = \
+  $(if $(call seq,$1,$(_ndk_mod_link_module))$(call module-is-static-library,$1),\
+    $(call module-get-direct-libs,$1))
+
+# -----------------------------------------------------------------------------
+# This function is used to extract the list of static libraries that need
+# to be linked as whole, i.e. placed in a special section on the final
+# link command.
+# Arguments: $1: module name.
+#            $2: list of all static link-time libraries (regular or whole).
+# Returns  : list of static libraries from '$2' that need to be linked
+#            as whole.
+# -----------------------------------------------------------------------------
+module-extract-whole-static-libs = $(strip \
+  $(eval _ndk_mod_whole_all := $(call map,module-get-whole-static-libs,$1 $2))\
+  $(eval _ndk_mod_whole_result := $(filter $(_ndk_mod_whole_all),$2))\
+  $(_ndk_mod_whole_result))
 
 # Used to recompute all dependencies once all module information has been recorded.
 #
@@ -767,47 +748,7 @@
 
 module-get-installed = $(__ndk_modules.$1.INSTALLED)
 
-# -----------------------------------------------------------------------------
-# Function : modules-get-all-dependencies
-# Arguments: 1: list of module names
-# Returns  : List of all the modules $1 depends on transitively.
-# Usage    : $(call modules-all-get-dependencies,<list of module names>)
-# Rationale: This computes the closure of all module dependencies starting from $1
-# -----------------------------------------------------------------------------
-module-get-all-dependencies = $(strip \
-    $(call modules-get-closure,$1,depends))
-
-modules-get-closure = \
-    $(eval __closure_deps  := $(strip $(call strip-lib-prefix,$1))) \
-    $(eval __closure_wq    := $(__closure_deps)) \
-    $(eval __closure_field := $(strip $2)) \
-    $(call modules-closure)\
-    $(__closure_deps)
-
-# Used internally by modules-get-all-dependencies
-# Note the tricky use of conditional recursion to work around the fact that
-# the GNU Make language does not have any conditional looping construct
-# like 'while'.
-#
-modules-closure = \
-    $(eval __closure_mod := $(call first,$(__closure_wq))) \
-    $(eval __closure_wq  := $(call rest,$(__closure_wq))) \
-    $(eval __closure_val := $(call strip-lib-prefix,$(__ndk_modules.$(__closure_mod).$(__closure_field)))) \
-    $(eval __closure_new := $(filter-out $(__closure_deps),$(__closure_val)))\
-    $(eval __closure_deps += $(__closure_new)) \
-    $(eval __closure_wq   := $(strip $(__closure_wq) $(__closure_new)))\
-    $(if $(__closure_wq),$(call modules-closure)) \
-
-# -----------------------------------------------------------------------------
-# Function : module-get-depends
-# Arguments: 1: list of module names
-#            2: local module type (e.g. SHARED_LIBRARIES)
-# Returns  : List all the <local-type> modules $1 depends on transitively.
-# Usage    : $(call module-get-depends,<list of module names>,<local-type>)
-# Rationale: This computes the closure of all local module dependencies starting from $1
-# -----------------------------------------------------------------------------
-module-get-depends = $(strip $(call modules-get-closure,$1,$2))
-
+module-get-depends = $(__ndk_modules.$1.depends)
 
 # -----------------------------------------------------------------------------
 # Function : modules-get-all-installable
@@ -818,7 +759,7 @@
 # -----------------------------------------------------------------------------
 # For now, only the closure of LOCAL_SHARED_LIBRARIES is enough
 modules-get-all-installable = $(strip \
-    $(foreach __alldep,$(call module-get-depends,$1,depends),\
+    $(foreach __alldep,$(call module-get-all-dependencies,$1),\
         $(if $(call module-is-installable,$(__alldep)),$(__alldep))\
     ))
 
@@ -945,14 +886,6 @@
 # =============================================================================
 
 # -----------------------------------------------------------------------------
-# Function : parent-dir
-# Arguments: 1: path
-# Returns  : Parent dir or path of $1, with final separator removed.
-# -----------------------------------------------------------------------------
-parent-dir = $(patsubst %/,%,$(dir $1))
-
-
-# -----------------------------------------------------------------------------
 # Function : pretty-dir
 # Arguments: 1: path
 # Returns  : Remove NDK_PROJECT_PATH prefix from a given path. This can be
@@ -961,6 +894,15 @@
 pretty-dir = $(patsubst $(NDK_ROOT)/%,<NDK>/%,\
                  $(patsubst $(NDK_PROJECT_PATH)/%,%,$1))
 
+# Note: NDK_PROJECT_PATH is typically defined after this test is run.
+-test-pretty-dir = \
+  $(eval NDK_PROJECT_PATH ?= .)\
+  $(call test-expect,foo,$(call pretty-dir,foo))\
+  $(call test-expect,foo,$(call pretty-dir,$(NDK_PROJECT_PATH)/foo))\
+  $(call test-expect,foo/bar,$(call pretty-dir,$(NDK_PROJECT_PATH)/foo/bar))\
+  $(call test-expect,<NDK>/foo,$(call pretty-dir,$(NDK_ROOT)/foo))\
+  $(call test-expect,<NDK>/foo/bar,$(call pretty-dir,$(NDK_ROOT)/foo/bar))
+
 # -----------------------------------------------------------------------------
 # Function : check-user-define
 # Arguments: 1: name of variable that must be defined by the user
@@ -1087,16 +1029,6 @@
     $(eval LOCAL_OBJS_DIR     := $(TARGET_OBJS)/$(LOCAL_MODULE))
 
 # -----------------------------------------------------------------------------
-# Strip any 'lib' prefix in front of a given string.
-#
-# Function : strip-lib-prefix
-# Arguments: 1: module name
-# Returns  : module name, without any 'lib' prefix if any
-# Usage    : $(call strip-lib-prefix,$(LOCAL_MODULE))
-# -----------------------------------------------------------------------------
-strip-lib-prefix = $(1:lib%=%)
-
-# -----------------------------------------------------------------------------
 # Compute the real path of a prebuilt file.
 #
 # Function : local-prebuilt-path
@@ -1338,6 +1270,13 @@
         $(__obj)\
     ))
 
+-test-get-object-name = \
+  $(eval LOCAL_CPP_EXTENSION ?= .cpp)\
+  $(call test-expect,foo.o,$(call get-object-name,foo.c))\
+  $(call test-expect,bar.o,$(call get-object-name,bar.s))\
+  $(call test-expect,zoo.o,$(call get-object-name,zoo.S))\
+  $(call test-expect,tot.o,$(call get-object-name,tot.cpp))
+
 # -----------------------------------------------------------------------------
 # Macro    : hide
 # Returns  : nothing
@@ -1762,8 +1701,8 @@
     $(eval __ndk_stl := $(strip $1)) \
     $(eval NDK_STL_LIST += $(__ndk_stl)) \
     $(eval NDK_STL.$(__ndk_stl).IMPORT_MODULE := $(strip $2)) \
-    $(eval NDK_STL.$(__ndk_stl).STATIC_LIBS := $(strip $3)) \
-    $(eval NDK_STL.$(__ndk_stl).SHARED_LIBS := $(strip $4))
+    $(eval NDK_STL.$(__ndk_stl).STATIC_LIBS := $(strip $(call strip-lib-prefix,$3))) \
+    $(eval NDK_STL.$(__ndk_stl).SHARED_LIBS := $(strip $(call strip-lib-prefix,$4)))
 
 # Called to check that the value of APP_STL is a valid one.
 # $1: STL name as it apperas in APP_STL (e.g. 'system')
@@ -1857,3 +1796,7 @@
     none,\
     cxx-stl/system,\
     )
+
+ifneq (,$(NDK_UNIT_TESTS))
+$(call ndk-run-all-tests)
+endif
diff --git a/tests/build/ndk-build-unit-tests/build.sh b/tests/build/ndk-build-unit-tests/build.sh
new file mode 100755
index 0000000..a1f0bb9
--- /dev/null
+++ b/tests/build/ndk-build-unit-tests/build.sh
@@ -0,0 +1,5 @@
+# This is used to check that the internal unit tests of ndk-build
+# work properly. Note that these only check internal Make functions
+# within the build system, not anything that tries to build something.
+cd $(dirname "$0")
+$NDK/ndk-build NDK_UNIT_TESTS=1 clean
diff --git a/tests/build/topological-sort/BROKEN_BUILD b/tests/build/ndk-build-unit-tests/jni/Android.mk
similarity index 100%
rename from tests/build/topological-sort/BROKEN_BUILD
rename to tests/build/ndk-build-unit-tests/jni/Android.mk
diff --git a/tests/build/topological-sort/jni/main.c b/tests/build/topological-sort/jni/main.c
index dcc85dc..3f20987 100644
--- a/tests/build/topological-sort/jni/main.c
+++ b/tests/build/topological-sort/jni/main.c
@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "foo.h"
 #include "bar.h"