Painless non-recursive Make

[article]

(and more so!):

Building everything from the top-level is a simple Make (in all these examples I do a make -n so that the commands are clearly shown):

$ cd /src

$ make -n

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

cc    -c -o '/tmp/out/executable/foo.o' '/home/jgc/doc/nonrecursive/executable/foo.c'

cc    -c -o '/tmp/out/executable/bar.o' '/home/jgc/doc/nonrecursive/executable/bar.c'

g++     /tmp/out/executable/foo.o /tmp/out/executable/bar.o /tmp/out/library/lib.a -o'/tmp/out/executable/exec'

As is cleaning everything:

$ cd /src

$ make -n clean

rm -rf /tmp/out/library

rm -rf /tmp/out/executable

From the top-level directory its possible to ask for any individual module to be built or cleaned:

$ cd /src

$ make -n clean-library

rm -rf /tmp/out/library

$ make -n library

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

And if we ask that the executable module be built the library gets built at the same time because of the dependency:

$ cd /src

$ make -n executable

cc    -c -o '/tmp/out/executable/foo.o' '/home/jgc/doc/nonrecursive/executable/foo.c'

cc    -c -o '/tmp/out/executable/bar.o' '/home/jgc/doc/nonrecursive/executable/bar.c'

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

g++     /tmp/out/executable/foo.o /tmp/out/executable/bar.o /tmp/out/library/lib.a -o'/tmp/out/executable/exec'

OK, so much for the top-level; if we pop down into the library module we can build or clean it just as easily:

$ cd /src/library

$ make -n clean

rm -rf /tmp/out/library

$ make -n

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

and, of course, doing this in the executable directory will build the library as well:

$ cd /src/executable

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

cc    -c -o '/tmp/out/executable/foo.o' '/home/jgc/doc/nonrecursive/executable/foo.c'

cc    -c -o '/tmp/out/executable/bar.o' '/home/jgc/doc/nonrecursive/executable/bar.c'

g++     /tmp/out/executable/foo.o /tmp/out/executable/bar.o /tmp/out/library/lib.a -o'/tmp/out/executable/exec'

What sub-modules?

Suppose, that the source tree was actually:

/src/

/src/library/

/src/library/sublibrary

/src/executable/

where there's an additional sublibrary underneath the library which builds slib.a from slib1.c and slib2.c using the following Makefile:

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

include $(_ROOT)/top.mak

SRCS := slib1.c slib2.c

BINARY := slib

BINARY_EXT := $(_LIBEXT)

include $(_ROOT)/bottom.mak

To define it as a dependency of library means simply adding a DEPENDS_ON call to the Makefile in the library 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/sublibrary)

include $(_ROOT)/top.mak

SRCS := lib1.c lib2.c

BINARY := lib

BINARY_EXT := $(_LIBEXT)

include $(_ROOT)/bottom.mak

In this example I didn't add a DEPS line so the library doesn't depend on sublibrary at the object level we're simply declaring sublibrary as a sub-module of library that needs to be built if library is.

Going back and repeating the examples above we see that the sublibrary has been successfully included in the library build (and automatically in the executable build).

Here's the full build from the top, followed by a clean:

$ cd /src

$ make -n

cc    -c -o '/tmp/out/library/sublibrary/slib1.o' '/home/jgc/doc/nonrecursive/library/sublibrary/slib1.c'

cc    -c -o '/tmp/out/library/sublibrary/slib2.o' '/home/jgc/doc/nonrecursive/library/sublibrary/slib2.c'

ar r '/tmp/out/library/sublibrary/slib.a' /tmp/out/library/sublibrary/slib1.o /tmp/out/library/sublibrary/slib2.o

ranlib '/tmp/out/library/sublibrary/slib.a'

cc    -c -o '/tmp/out/library/lib1.o' '/home/jgc/doc/nonrecursive/library/lib1.c'

cc    -c -o '/tmp/out/library/lib2.o' '/home/jgc/doc/nonrecursive/library/lib2.c'

ar r '/tmp/out/library/lib.a' /tmp/out/library/lib1.o /tmp/out/library/lib2.o

ranlib '/tmp/out/library/lib.a'

cc    -c -o '/tmp/out/executable/foo.o' '/home/jgc/doc/nonrecursive/executable/foo.c'

cc    -c -o '/tmp/out/executable/bar.o' '/home/jgc/doc/nonrecursive/executable/bar.c'

g++     /tmp/out/executable/foo.o /tmp/out/executable/bar.o /tmp/out/library/lib.a -o'/tmp/out/executable/exec'

$ make -n clean

rm -rf /tmp/out/library/sublibrary

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

Sep 24
Oct 12
Oct 15
Nov 09