GNU make
uses a complex algorithm to decide when it
should use files found via a VPATH
search. See How Directory Searches are Performed in The GNU Make
Manual.
If a target needs to be rebuilt, GNU make
discards the
file name found during the VPATH
search for this target, and
builds the file locally using the file name given in the makefile.
If a target does not need to be rebuilt, GNU make
uses the
file name found during the VPATH
search.
Other make
implementations, like NetBSD make
, are
easier to describe: the file name found during the VPATH
search
is used whether the target needs to be rebuilt or not. Therefore
new files are created locally, but existing files are updated at their
VPATH
location.
OpenBSD and FreeBSD make
, however,
never perform a
VPATH
search for a dependency that has an explicit rule.
This is extremely annoying.
When attempting a VPATH
build for an autoconfiscated package
(e.g., mkdir build && cd build && ../configure
), this means
GNU
make
builds everything locally in the build
directory, while BSD make
builds new files locally and
updates existing files in the source directory.
$ cat Makefile VPATH = .. all: foo.x bar.x foo.x bar.x: newer.x @echo Building $@ $ touch ../bar.x $ touch ../newer.x $ make # GNU make Building foo.x Building bar.x $ pmake # NetBSD make Building foo.x Building ../bar.x $ fmake # FreeBSD make, OpenBSD make Building foo.x Building bar.x $ tmake # Tru64 make Building foo.x Building bar.x $ touch ../bar.x $ make # GNU make Building foo.x $ pmake # NetBSD make Building foo.x $ fmake # FreeBSD make, OpenBSD make Building foo.x Building bar.x $ tmake # Tru64 make Building foo.x Building bar.x
Note how NetBSD make
updates ../bar.x in its
VPATH location, and how FreeBSD, OpenBSD, and Tru64
make
always
update bar.x, even when ../bar.x is up to date.
Another point worth mentioning is that once GNU make
has
decided to ignore a VPATH
file name (e.g., it ignored
../bar.x in the above example) it continues to ignore it when
the target occurs as a prerequisite of another rule.
The following example shows that GNU make
does not look up
bar.x in VPATH
before performing the .x.y
rule,
because it ignored the VPATH
result of bar.x while running
the bar.x: newer.x
rule.
$ cat Makefile VPATH = .. all: bar.y bar.x: newer.x @echo Building $@ .SUFFIXES: .x .y .x.y: cp $< $@ $ touch ../bar.x $ touch ../newer.x $ make # GNU make Building bar.x cp bar.x bar.y cp: cannot stat 'bar.x': No such file or directory make: *** [bar.y] Error 1 $ pmake # NetBSD make Building ../bar.x cp ../bar.x bar.y $ rm bar.y $ fmake # FreeBSD make, OpenBSD make echo Building bar.x cp bar.x bar.y cp: cannot stat 'bar.x': No such file or directory *** Error code 1 $ tmake # Tru64 make Building bar.x cp: bar.x: No such file or directory *** Exit 1
Note that if you drop away the command from the bar.x: newer.x
rule, GNU make
magically starts to work: it
knows that bar.x
hasn’t been updated, therefore it doesn’t
discard the result from VPATH
(../bar.x) in succeeding
uses. Tru64 also works, but FreeBSD and OpenBSD
still don’t.
$ cat Makefile VPATH = .. all: bar.y bar.x: newer.x .SUFFIXES: .x .y .x.y: cp $< $@ $ touch ../bar.x $ touch ../newer.x $ make # GNU make cp ../bar.x bar.y $ rm bar.y $ pmake # NetBSD make cp ../bar.x bar.y $ rm bar.y $ fmake # FreeBSD make, OpenBSD make cp bar.x bar.y cp: cannot stat 'bar.x': No such file or directory *** Error code 1 $ tmake # Tru64 make cp ../bar.x bar.y
It seems the sole solution that would please every make
implementation is to never rely on VPATH
searches for targets.
In other words, VPATH
should be reserved to sources that are not built.