Painless non-recursive Make

[article]

$^ -o'$@'

$(_MODULE_NAME)_DEFINED := T

endif

endif

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:

sp :=

sp +=

_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))

include $(_ROOT)/root.mak

$(call DEPENDS_ON,library)

include $(_ROOT)/top.mak

SRCS := foo.c bar.c

BINARY := exec

BINARY_EXT := $(_EXEEXT)

DEPS := $(library_BINARY)

include $(_ROOT)/bottom.mak

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.

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.

DEPENDS_ON_NO_BUILD
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

About the author

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!

Upcoming Events

Nov 09
Nov 09
Apr 13
May 03