[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
12.14.4 Automatic Rule Rewriting
Some make
implementations, such as Solaris and Tru64,
search for prerequisites in VPATH
and
then rewrite each occurrence as a plain word in the rule.
For instance:
# This isn't portable to GNU make. VPATH = ../pkg/src f.c: if.c cp if.c f.c |
executes cp ../pkg/src/if.c f.c
if ‘if.c’ is
found in ‘../pkg/src’.
However, this rule leads to real problems in practice. For example, if
the source directory contains an ordinary file named ‘test’ that is
used in a dependency, Solaris make
rewrites commands like
‘if test -r foo; …’ to ‘if ../pkg/src/test -r foo;
…’, which is typically undesirable. To avoid this problem,
portable makefiles should never mention a source file whose name is that
of a shell keyword like ‘until’ or a shell command like
cat
or gcc
or test
.
Because of these problems GNU make
and many other
make
implementations do not rewrite commands, so portable
makefiles should
search VPATH
manually. It is tempting to write this:
# This isn't portable to Solaris make. VPATH = ../pkg/src f.c: if.c cp `test -f if.c || echo $(VPATH)/`if.c f.c |
However, the “prerequisite rewriting” still applies here. So if
‘if.c’ is in ‘../pkg/src’, Solaris and Tru64 make
execute
cp `test -f ../pkg/src/if.c || echo ../pkg/src/`if.c f.c |
which reduces to
cp if.c f.c |
and thus fails. Oops.
A simple workaround, and good practice anyway, is to use ‘$?’ and ‘$@’ when possible:
VPATH = ../pkg/src f.c: if.c cp $? $@ |
but this does not generalize well to commands with multiple prerequisites. A more general workaround is to rewrite the rule so that the prerequisite ‘if.c’ never appears as a plain word. For example, these three rules would be safe, assuming ‘if.c’ is in ‘../pkg/src’ and the other files are in the working directory:
VPATH = ../pkg/src f.c: if.c f1.c cat `test -f ./if.c || echo $(VPATH)/`if.c f1.c >$@ g.c: if.c g1.c cat `test -f 'if.c' || echo $(VPATH)/`if.c g1.c >$@ h.c: if.c h1.c cat `test -f "if.c" || echo $(VPATH)/`if.c h1.c >$@ |
Things get worse when your prerequisites are in a macro.
VPATH = ../pkg/src HEADERS = f.h g.h h.h install-HEADERS: $(HEADERS) for i in $(HEADERS); do \ $(INSTALL) -m 644 \ `test -f $$i || echo $(VPATH)/`$$i \ $(DESTDIR)$(includedir)/$$i; \ done |
The above install-HEADERS
rule is not Solaris-proof because for
i in $(HEADERS);
is expanded to for i in f.h g.h h.h;
where f.h
and g.h
are plain words and are hence
subject to VPATH
adjustments.
If the three files are in ‘../pkg/src’, the rule is run as:
for i in ../pkg/src/f.h ../pkg/src/g.h h.h; do \ install -m 644 \ `test -f $i || echo ../pkg/src/`$i \ /usr/local/include/$i; \ done |
where the two first install
calls fail. For instance,
consider the f.h
installation:
install -m 644 \ `test -f ../pkg/src/f.h || \ echo ../pkg/src/ \ `../pkg/src/f.h \ /usr/local/include/../pkg/src/f.h; |
It reduces to:
install -m 644 \ ../pkg/src/f.h \ /usr/local/include/../pkg/src/f.h; |
Note that the manual VPATH
search did not cause any problems here;
however this command installs ‘f.h’ in an incorrect directory.
Trying to quote $(HEADERS)
in some way, as we did for
foo.c
a few makefiles ago, does not help:
install-HEADERS: $(HEADERS) headers='$(HEADERS)'; \ for i in $$headers; do \ $(INSTALL) -m 644 \ `test -f $$i || echo $(VPATH)/`$$i \ $(DESTDIR)$(includedir)/$$i; \ done |
Now, headers='$(HEADERS)'
macro-expands to:
headers='f.h g.h h.h' |
but g.h
is still a plain word. (As an aside, the idiom
headers='$(HEADERS)'; for i in $$headers;
is a good
idea if $(HEADERS)
can be empty, because some shells diagnose a
syntax error on for i in;
.)
One workaround is to strip this unwanted ‘../pkg/src/’ prefix manually:
VPATH = ../pkg/src HEADERS = f.h g.h h.h install-HEADERS: $(HEADERS) headers='$(HEADERS)'; \ for i in $$headers; do \ i=`expr "$$i" : '$(VPATH)/\(.*\)'`; $(INSTALL) -m 644 \ `test -f $$i || echo $(VPATH)/`$$i \ $(DESTDIR)$(includedir)/$$i; \ done |
Automake does something similar. However the above hack works only if
the files listed in HEADERS
are in the current directory or a
subdirectory; they should not be in an enclosing directory. If we had
HEADERS = ../f.h
, the above fragment would fail in a VPATH
build with Tru64 make
. The reason is that not only does
Tru64 make
rewrite dependencies, but it also simplifies
them. Hence ../f.h
becomes ../pkg/f.h
instead of
../pkg/src/../f.h
. This obviously defeats any attempt to strip
a leading ‘../pkg/src/’ component.
The following example makes the behavior of Tru64 make
more apparent.
$ cat Makefile VPATH = sub all: ../foo echo ../foo $ ls Makefile foo $ make echo foo foo |
Dependency ‘../foo’ was found in ‘sub/../foo’, but Tru64
make
simplified it as ‘foo’. (Note that the ‘sub/’
directory does not even exist, this just means that the simplification
occurred before the file was checked for.)
For the record here is how SunOS 4 make
behaves on this
example.
$ make make: Fatal error: Don't know how to make target `../foo' $ mkdir sub $ make echo sub/../foo sub/../foo |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |