automake
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

LONG: Limitations of Make (Portable Shell Programming chapter of Autocon


From: Alexandre Duret-Lutz
Subject: LONG: Limitations of Make (Portable Shell Programming chapter of Autoconf)
Date: 24 Mar 2002 19:16:08 +0100
User-agent: Gnus/5.0808 (Gnus v5.8.8) Emacs/20.7

Hi,

This is an update of the "Limitations of Make" node of the
Autoconf manual with some issues raised on the Automake lists
recently.

I'm submiting the formated text here to get comments and
corrections I could integrate before submitting the patch to
Autoconf.

Thanks for any help.

 ----

Limitations of Make
===================

   Make itself suffers a great number of limitations, only a few of
which being listed here.  First of all, remember that since commands are
executed by the shell, all its weaknesses are inherited...

`$<'
     POSIX says that the `$<' construct in makefiles can be used only
     in inference rules and in the `.DEFAULT' rule; its meaning in
     ordinary rules is unspecified.  Solaris 8's `make' for instance
     will replace it with the argument.

Leading underscore in macro names
     Some Make don't support leading underscores in macro names, such
     as on NEWS-OS 4.2R.

          $ cat Makefile
          _am_include = #
          _am_quote =
          all:; @echo this is test
          $ make
          Make: Must be a separator on rules line 2.  Stop.
          $ cat Makefile2
          am_include = #
          am_quote =
          all:; @echo this is test
          $ make -f Makefile2
          this is test

`make variable=value' and sub-`make's.
     An command argument definition such as `foo=bar' overrides any foo
     definition in the Makefile.  Some Make implementations (such as GNU
     Make) will propagate this override to sub-invocations of `make',
     but POSIX conformant implementations won't.

          % cat Makefile
          foo = foo
          one:
                  @echo $(foo)
                  $(MAKE) two
          two:
                  @echo $(foo)
          % make foo=bar                # GNU make 3.79.1
          bar
          make two
          make[1]: Entering directory `/home/adl'
          bar
          make[1]: Leaving directory `/home/adl'
          % pmake foo=bar               # BSD make
          bar
          pmake two
          foo

     You have a few possibilities if you do want the `foo=bar' override
     to propagate to sub-`make's.  One is to use the `-e' option, which
     cause all environment variables to have precedence over the
     `Makefile' definitions, and declare foo as an environment variable:

          % env foo=bar make -e

     The `-e' option is propagated to sub-`make's automatically, and
     since the environment is inherited between `make' invocations the
     `foo' variable will be overriden in sub-`make's as expected.

     Using `-e' could have unexpected side-effects if your environment
     contains some other variables usually defined by the Makefile.
     (See also the note about `make -e' and `SHELL' below.)

     Another way to propagate overrides to sub-`make's is to do it
     manually, from your `Makefile':

          foo = foo
          one:
                  @echo $(foo)
                  $(MAKE) foo=$(foo) two
          two:
                  @echo $(foo)

     You need to foresee all variables that a user might want to
     override if you do that.

The `SHELL' variable
     POSIX Makes internally use the `$(SHELL)' variable to spawn shell
     processes and execute `Makefile' rules.  This is a built-in
     variable supplied by Make, but it can be modified from the
     Makefile or a command line argument.

     Not all Makes will define this `SHELL' variable.  OSF/Tru64 Make is
     an example, this implementation will always use `/bin/sh'.  So it's
     a good idea to always define `SHELL' in your `Makefile's.  If you
     use Autoconf, do


          SHELL = @SHELL@

     POSIX compliant makes should never acquire the value of $(SHELL)
     from the environment, even when `make -e' is used (otherwise, think
     about what would happen to your rules if `SHELL=/bin/tcsh').

     However not all Make implementations will make this exception.
     specially.  For instance it's not surprising that OSF/Tru64 Make
     doesn't protect `SHELL', since it doesn't use it.

          % cat Makefile
          SHELL = /bin/sh
          FOO = foo
          all:
                  @echo $(SHELL)
                  @echo $(FOO)
          % env SHELL=/bin/tcsh FOO=bar make -e   # OSF1 V4.0 Make
          /bin/tcsh
          bar
          % env SHELL=/bin/tcsh FOO=bar gmake -e  # GNU make
          /bin/sh
          bar

`VPATH'
     There is no `VPATH' support specified in POSIX.  Many Makes have a
     form of `VPATH' support, but its implementation is not coherent
     amongst Makes.

     Maybe the best suggestion to do to people who needs the `VPATH'
     feature is to chose a Make implementation an stick to it.  Since
     the resulting `Makefile's are not portable anyway, better chose a
     portable Make (hint, hint).

     Here are a couple of known issues with some `VPATH'
     implementations.

    `VPATH' and double-colon rules
          Any assignment to `VPATH' causes Sun Make to only execute the
          first set of double-colon rules.  (This comments is here
          since 1994 and the context has been lost.  It's probably
          about SunOS 4.  If you can reproduce this, please send us a
          testcase for illustration.)

    `$<' in inference rules:
          An implementation a make would not adjust prefix `$<' this
          prerequisite have been found in a `VPATH' dir.  This means
          that

               VPATH = ../src
               .c.o:
                       cc -c $< -o $

          would run `cc -c foo.c -o foo.o', even if `foo.c' was actually
          found in `../src/'.

          This can be fixed as follow.

               VPATH = ../src
               .c.o:
                       cc -c `test -f $< || echo ../src/`$< -o $

          This kludge was introduced in Automake in 2000, but the exact
          context have been lost.  If you know which make
          implementation is involved here, please drop us a note.

    `$<' not supported in explicit rules
          As said elsewhere, using `$<' in explicit rules is not
          portable.  You have to make a `VPATH' search manually.  For
          instance, using the same pattern as above:

               VPATH = ../src
               foo.o: foo.c
                       cc -c `test -f foo.c || echo ../src/`foo.c -o foo.o

    Automatic rule rewriting
          Some Make implementations, such as SunOS Make, will search
          prerequisites in `VPATH' and rewrite all their occurences in
          the rule appropriately.

          For instance

               VPATH = ../src
               foo.o: foo.c
                       cc -c foo.c -o foo.o

          would execute `cc -c ../src/foo.c -o foo.o' if `foo.c' was
          found in `../src'.  That sounds great.

          However, for the sake of other Make implementations, we can't
          rely on this, and we have to search `VPATH' manually:

               VPATH = ../src
               foo.o: foo.c
                       cc -c `test -f foo.c || echo ../src/`foo.c -o foo.o

          however the "prerequisite rewriting" still applies here.  So
          if `foo.c' is in `../src', SunOS Make will execute

               `cc -c `test -f ../src/foo.c || echo ../src/`foo.c -o foo.o'

          which reduces to

               cc -c foo.c -o foo.o

          and thus fails.  Oops.

          One workaround is to make sure that foo.c never appear as a
          plain word in the rule.  For instance these three rules would
          be safe.

               VPATH = ../src
               foo.o: foo.c
                       cc -c `test -f ./foo.c || echo ../src/`foo.c -o foo.o
               foo2.o: foo2.c
                       cc -c `test -f 'foo2.c' || echo ../src/`foo2.c -o foo2.o
               foo3.o: foo3.c
                       cc -c `test -f "foo3.c" || echo ../src/`foo3.c -o foo3.o

          Things get worse when your prerequisites are in a variable.

               VPATH = ../src
               HEADERS = foo.h foo2.h foo3.h
               install-HEADERS: $(HEADERS)
                       for i in $(HEADERS); do \
                         $(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
                           $(DESTDIR)$(includedir)/$$i; \
                       done

          The above `install-HEADERS' rule is not sun-proof because `for
          i in $(HEADERS);' will expanded as `for i in foo.h foo2.h
          foo3.h;' where `foo.h' and `foo2.h' are plain words and are
          hence subject to `VPATH' adjustements.
          If the three files are in `../src', the rule is run as

               for i in ../src/foo.h ../src/foo2.h foo3.h; do \
                 install -m 644 `test -f $i || echo ../src/`$i \
                    /usr/local/include/$i; \
               done

          where the two first `install' calls will fails.  Consider the
          `foo.h' installation, for instance:

               install -m 644 `test -f ../src/foo.h || echo ../src/`../src/foo.\
h \
                 /usr/local/inclue/../src/foo.h;

          reduces to:

               install -m 644 ../src/foo.h /usr/local/include/../src/foo.h;

          Note that the manual `VPATH' search did not cause any problem
          here, however this command fails to install `foo.h' in the
          correct directory.

          Deciding to quote `$(HEADERS)' in some way, like we did for
          `foo.c' a few `Makefile's ago, do not help:

               install-HEADERS: $(HEADERS)
                       headers='$(HEADERS)'; for i in $$headers; do \
                         $(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
                           $(DESTDIR)$(includedir)/$$i; \
                       done

          Indeed, `headers='$(HEADERS)'' expands to `headers='foo.h
          foo2.h foo3.h'' where `foo2.h' is still a plain word.
          (Aside: the `headers='$(HEADERS)'; for i in $$headers;' idiom
          is this a good idea if `$(HEADERS)' can be empty, because
          some shell produce a syntax error on `for i in;'.)

          One workaround is to strip this unwanted `../src/' prefix
          manually:
               VPATH = ../src
               HEADERS = foo.h foo2.h foo3.h
               install-HEADERS: $(HEADERS)
                       headers='$(HEADERS)'; for i in $$headers; do \
                         i=`expr "$$i" : '../src/\(.*\)'`;
                         $(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
                           $(DESTDIR)$(includedir)/$$i; \
                       done

    OSF/Tru64 make creates prerequisite directories magically
          When a prerequisite is a sub-directory of `VPATH', Tru64 Make
          will create it in the current directory.

               % cat Makefile
               VPATH = ..
               all : foo/bar
               % make -p foo/bar build
               % cd build
               % make
               mkdir foo
               mkdir foo/bar

          This can yield unexpected results if a rule uses a manual
          `VPATH' search as presented before.

               VPATH = ..
               all : foo/bar
                       command `test -d foo/bar || echo ../`foo/bar

          The above `command' will be run on the empty `foo/bar'
          directory created in the current directory.

-- 
Alexandre Duret-Lutz




reply via email to

[Prev in Thread] Current Thread [Next in Thread]