$(_MODULE_NAME)_DEFINED := T
The first thing bottom.mak does is set up two macros with computed names: $(_MODULE_NAME)_OBJS (which is the list of object files in the module computed from the SRCS macro by transforming the extension) and $(_MODULE_NAME)_BINARY (which is the name of the binary file created by the module; this would typically be the library or executable being built).
The $(_MODULE_NAME)_OBJS also includes any object files that are needed by the module but not built by it by including the DEPS macro. We'll see later how this is used to define a dependency between the library and executable in the example.
Next, if rules for this module have not previously been set up (controlled by the $(_MODULE_NAME)_DEFINED macro) and have not been explicitely disabled by the _NO_RULES macro the actual rules to build the module are defined.
In this example I show rules for Linux, this is where you'd change this example for another operating system.
First, all has the current binary (from $(_MODULE_NAME)_BINARY)
added as a prerequisite so that the module gets built when a full build
is done. Then there's a rule that associates the module name with
the module binrary so that it's possible to type something like make library at the top level of the build to build just the library.
Then there's a general clean rule and a module specific clean (for the library module there's a rule to just clean its objects called clean-library). Clean is implemented as a simple rm -rf since all the output is organized in a specific directory of _OUTTOP.
After that a $(shell) is used to setup up the directory into which the module's output will
go. Finally, specific rules associated the object files in this module's output directory and source files in this module's source directory.
So, with all that infrastructure in place we can finally come to the Makefile in the executable directory:
_walk = $(if $1,$(wildcard /$(subst $(sp),/,$1)/$2) $(call _walk,$(wordlist 2,$(words $1),x $1),$2))
_find = $(firstword $(call _walk,$(strip $(subst /, ,$1)),$2))
_ROOT := $(patsubst %/root.mak,%,$(call _find,$(CURDIR),root.mak))
SRCS := foo.c bar.c
BINARY := exec
BINARY_EXT := $(_EXEEXT)
DEPS := $(library_BINARY)
It looks very like the Makefile for the library, but there are a couple of differences. Since the executable needs the library the DEPS line specifies that the executable depends on the binary file created by the library. Since each module has unique macros for objects and
binaries, it's easy to define that dependency just by referring to $(library_BINARY) which will expand to the full path to the library file created by the library module.
To actually ensure that $(library_BINARY) is defined its necessary to include the Makefile from the library directory. The root.mak file provides two functions that make this trivial: DEPENDS_ON and DEPENDS_ON_NO_BUILD.
just sets up the macros for the specified module so that they can be used in the Makefile. If that function were used in the executable Makefile then the library (lib.a) would have to exist for the executable to build successfully. On the other hand, of DEPENDS_ON is used this ensures that the library will get built if necessary.
provides functionality similar to a classic recursive build which doesn't know how to build that library, but depends on it anyway. DEPENDS_ON is more flexible since without recursion you can specify a relationship and make sure that code is built.
OK, so what can we do with this?
This system provides a great deal of flexibility. Here are just a few examples that illustrate that the non-recursive Make system is just as flexible as a recursive one