(autoconf.info)Automatic Rule Rewriting


Next: Tru64 Directory Magic Prev: $< in Explicit Rules Up: VPATH and Make

11.13.3 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


automatically generated by info2www