12.18.6 Make Target Lookup

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.