| [ < ] | [ > ] | [ << ] | [ 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] | [ ? ] |
