File: autoconf.info, Node: Automatic Rule Rewriting, Next: Make Target Lookup, Prev: $< in Explicit Rules, Up: VPATH and Make 12.18.4 Automatic Rule Rewriting -------------------------------- Some ‘make’ implementations, such as Solaris, 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. In fact, ‘make’ is completely unaware of shell syntax used in the rules, so the VPATH rewrite can potentially apply to _any_ whitespace-separated word in a rule, including shell variables, functions, and keywords. $ mkdir build $ cd build $ cat > Makefile <<'END' VPATH = .. all: arg func for echo func () { for arg in "$$@"; do echo $$arg; done; }; \ func "hello world" END $ touch ../arg ../func ../for ../echo $ make ../func () { ../for ../arg in "$@"; do ../echo $arg; done; }; \ ../func "hello world" sh: syntax error at line 1: `do' unexpected *** Error code 2 To avoid this problem, portable makefiles should never mention a source file or dependency whose name is that of a shell keyword like ‘for’ or ‘until’, a shell command like ‘cat’ or ‘gcc’ or ‘test’, or a shell function or variable used in the corresponding ‘Makefile’ recipe. 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 ‘make’ executes 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.
