manpagez: man pages & more
info autoconf
Home | html | info | man

File: autoconf.info,  Node: Automatic Rule Rewriting,  Next: Tru64 Directory Magic,  Prev: $< in Explicit Rules,  Up: VPATH and Make

12.18.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.  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 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.)

© manpagez.com 2000-2025
Individual documents may contain additional copyright information.