The program bsdmake has a very good support for storing the built targets in a directory distinct form the one holding the sources. A Makefile
using this functionality might be a bit harder to write, so I will review the most important techniques in my answer.
Choosing a directory to store built targets
The built-in variable .OBJDIR
contains the name of a directory where targets are built. It is determined like this (excerpt from the man
page):
.OBJDIR A path to the directory where the targets are built. Its
value is determined by trying to chdir(2) to the follow‐
ing directories in order and using the first match:
1. ${MAKEOBJDIRPREFIX}${.CURDIR}
(Only if ‘MAKEOBJDIRPREFIX’ is set in the environ‐
ment or on the command line.)
2. ${MAKEOBJDIR}
…
6. ${.CURDIR}
The program make
will chdir(2)
to .OBJDIR
and set PWD
to that directory before
executing any targets. In the absence of any adequate preparation .OBJDIR
is .CURDIR
, therefore we need to create the desired directory — or tree structure — before running make
to build our targets.
Assume, you choose to rely on the first mechanism to set .OBJDIR
, you can then organise your build system so that, before running make
to build your targets,
- The environment variable
MAKEOBJDIRPREFIX
is set to suitable value.
- A special script (it might be a
make
target) prepares the necessary directory or directories under ${MAKEOBJDIRPREFIX}
.
Makefiles arrangements
We consider a simple Makefile
and describe arrangements necessary for the previous setup to work.
program : a.o b.o c.o
cc a.o b.o c.o -o program
a.o b.o c.o : defs.h
a.o : a.c
cc -c a.c
b.o : b.c
cc -c b.c
c.o : c.c
cc -c c.c
manual.html: manual.css
manual.html: a.c b.c c.c defs.h
manualtool -c manual.css -o manual.html a.c b.c c.c defs.h
Before building a.o
the program make
will chdir(2)
to ${.OBJDIR}
and run the compiler cc
from that directory. Since the source a.c
is located in another directory, the compiler cc
will not find it and the compilation will fail. This issue is solved by using the local variables defined by make
, the previous Makefile
therefore translates to:
program : a.o b.o c.o
cc ${.ALLSRC} -o program
a.o b.o c.o : defs.h
a.o : a.c
cc -c ${.ALLSRC:M*.c}
b.o : b.c
cc -c ${.ALLSRC:M*.c}
c.o : c.c
cc -c ${.ALLSRC:M*.c}
manual.html: manual.css
manual.html: a.c b.c c.c defs.h
manualtool -c ${.ALLSRC:M*.css} -o manual.html ${.ALLSRC:N*.css}
Note how globbing patterns are used in the last recipe to appropriately select pieces of the sources at the various locations of the command line.
Examples
The build system for freebsd — and possibly netbsd and openbsd — use this functionality to be able to build simultaneously the operating system for various architectures. (It also make cleaning easier!) You could study these Makefiles to gain a better understanding of the techniques involved.
My portable macros bsdowl for bdsmake also support this functionality of bsdmake
, you could look at the file bps.objdir.mk
and the ocaml examples.