[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
- LONG: Limitations of Make (Portable Shell Programming chapter of Autoconf),
Alexandre Duret-Lutz <=