autoconf: Automatic Rule Rewriting

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