Previous: Limitations of Usual Tools, Up: Portable Shell
make itself suffers a great number of limitations, only a few of which are listed here. First of all, remember that since commands are executed by the shell, all its weaknesses are inherited....
$< $ 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
FOO = one \
BAR = two
test:
: FOO is "$(FOO)"
: BAR is "$(BAR)"
shows FOO equal to one BAR = two. Other makes
sensibly let a backslash continue only to the immediately following
line.
#
and continue until an unescaped newline is reached.
% cat Makefile
# A = foo \
bar \
baz
all:
@echo ok
% make # GNU make
ok
However in Real World this is not always the case. Some implementations
discards anything from # up to the end of line, ignoring any
trailing backslash.
% pmake # BSD make
"Makefile", line 3: Need an operator
Fatal errors encountered -- cannot continue
Therefore, if you want to comment out a multi-line definition, prefix each
line with #, not only the first.
# A = foo \
# bar \
# baz
make macro=value and sub-makes.foo=bar overrides any
definition of foo in the Makefile. Some make
implementations (such as GNU make) will propagate this
override to sub-invocations of make. Some other implementation
will not pass the substitution along to sub-makes.
% 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-makes. One is to use the -e
option, which causes all environment variables to have precedence over
the Makefile macro definitions, and declare foo as an environment
variable:
% env foo=bar make -e
The -e option is propagated to sub-makes automatically,
and since the environment is inherited between make
invocations, the foo macro will be overridden in
sub-makes as expected.
This syntax (foo=bar make -e) is portable only when used
outside a Makefile, for instance from a script or from the
command line. When run inside a make rule, GNU
make 3.80 and prior versions forget to propagate the
-e option to sub-makes.
Moreover, using -e could have unexpected side-effects if your
environment contains some other macros usually defined by the
Makefile. (See also the note about make -e and SHELL
below.)
Another way to propagate overrides to sub-makes 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 macros that a user might want to override if
you do that.
SHELL macro$(SHELL)
macro to spawn shell processes and execute Makefile rules. This
is a builtin macro supplied by make, but it can be modified
from the Makefile or a command-line argument.
Not all makes will define this SHELL macro. 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 Makefiles. 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.
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
Some make treat anything starting with a tab as a command for
the current rule, even if the tab is immediately followed by a #.
The make from Tru64 Unix V5.1 is one of them. The following
Makefile will run # foo through the shell.
all:
# foo
If an obj/ directory exists, BSD make will enter it before reading Makefile. Hence the Makefile in the current directory will not be read.
% cat Makefile
all:
echo Hello
% cat obj/Makefile
all:
echo World
% make # GNU make
echo Hello
Hello
% pmake # BSD make
echo World
World
make -kmake -k. Some implementations
reflect whether they encountered an error in their exit status; other
implementations always succeed.
% cat Makefile
all:
false
% make -k; echo exit status: $? # GNU make
false
make: *** [all] Error 1
exit status: 2
% pmake -k; echo exit status: $? # BSD make
false
*** Error code 1 (continuing)
exit status: 0
VPATHVPATH support specified in POSIX. Many
makes have a form of VPATH support, but its
implementation is not consistent amongst makes.
Maybe the best suggestion to give to people who need the VPATH
feature is to choose a make implementation and stick to it.
Since the resulting Makefiles are not portable anyway, better
choose a portable make (hint, hint).
Here are a couple of known issues with some VPATH
implementations.
VPATH and double-colon rulesVPATH causes Sun make to only execute
the first set of double-colon rules. (This comment has been here since
1994 and the context has been lost. It's probably about SunOS 4. If
you can reproduce this, please send us a test case for illustration.)
$< not supported in explicit rules$< in explicit rules is not portable.
The prerequisite file must be named explicitly in the rule. If you want
to find the prerequisite via a VPATH search, you have to code the
whole thing manually. For instance, using the following pattern:
VPATH = ../pkg/src
foo.c: ifoo.c
cp `test -f ifoo.c || echo ../pkg/src/`ifoo.c foo.c
VPATH and
rewrite all their occurrences in the rule appropriately.
For instance
VPATH = ../pkg/src
foo.c: ifoo.c
cp ifoo.c foo.c
would execute cp ../pkg/src/ifoo.c foo.c if ifoo.c was
found in ../pkg/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 = ../pkg/src
foo.c: ifoo.c
cp `test -f ifoo.c || echo ../pkg/src/`ifoo.c foo.c
However the "prerequisite rewriting" still applies here. So if ifoo.c is in ../pkg/src, SunOS make and OSF1/Tru64 make will execute
cp `test -f ../pkg/src/ifoo.c || echo ../pkg/src/`ifoo.c foo.c
which reduces to
cp ifoo.c foo.c
and thus fails. Oops.
One workaround is to make sure that ifoo.c never appears as a plain word in the rule. For instance these three rules would be safe.
VPATH = ../pkg/src
foo.c: ifoo.c
cp `test -f ./ifoo.c || echo ../pkg/src/`ifoo.c foo.c
foo2.c: ifoo2.c
cp `test -f 'ifoo2.c' || echo ../pkg/src/`ifoo2.c foo2.c
foo3.c: ifoo3.c
cp `test -f "ifoo3.c" || echo ../pkg/src/`ifoo3.c foo3.c
Things get worse when your prerequisites are in a macro.
VPATH = ../pkg/src
HEADERS = foo.h foo2.h foo3.h
install-HEADERS: $(HEADERS)
for i in $(HEADERS); do \
$(INSTALL) -m 644 `test -f $$i || echo ../pkg/src/`$$i \
$(DESTDIR)$(includedir)/$$i; \
done
The above install-HEADERS rule is not SunOS-proof because for
i in $(HEADERS); will be 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 adjustments.
If the three files are in ../pkg/src, the rule is run as:
for i in ../pkg/src/foo.h ../pkg/src/foo2.h foo3.h; do \
install -m 644 `test -f $i || echo ../pkg/src/`$i \
/usr/local/include/$i; \
done
where the two first install calls will fail. For instance,
consider the foo.h installation:
install -m 644 `test -f ../pkg/src/foo.h || echo ../pkg/src/`../pkg/src/foo.h \
/usr/local/include/../pkg/src/foo.h;
It reduces to:
install -m 644 ../pkg/src/foo.h /usr/local/include/../pkg/src/foo.h;
Note that the manual VPATH search did not cause any problems here;
however this command installs foo.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 ../pkg/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 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 = foo.h foo2.h foo3.h
install-HEADERS: $(HEADERS)
headers='$(HEADERS)'; for i in $$headers; do \
i=`expr "$$i" : '../pkg/src/\(.*\)'`;
$(INSTALL) -m 644 `test -f $$i || echo ../pkg/src/`$$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 = ../foo.h, the above fragment would fail in a VPATH
build with OSF1/Tru64 make. The reason is that not only does
OSF1/Tru64 make rewrite dependencies, but it also simplifies
them. Hence ../foo.h will become ../pkg/foo.h instead of
../pkg/src/../foo.h. This obviously defeats any attempt to strip
a leading ../pkg/src/ component.
The following example makes the behavior of OSF1/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 OSF1/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 records here is how SunOS make behaves on this very same example.
% make
make: Fatal error: Don't know how to make target `../foo'
% mkdir sub
% make
echo sub/../foo
sub/../foo
VPATH, Tru64
make will create it in the current directory.
% mkdir -p foo/bar build
% cd build
% cat >Makefile <<END
VPATH = ..
all: foo/bar
END
% 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 that was created in the current directory.
VPATH search. See How Directory Searches are Performed.
If a target needs to be rebuilt, GNU make discards the
filename found during the VPATH search for this target, and
builds the file locally using the filename given in the Makefile.
If a target does not need to be rebuilt, GNU make uses the
filename found during the VPATH search.
Other make implementations, like NetBSD make, are
easier to describe: the filename found during the VPATH search
will be used whether the target needs to be rebuilt or not. Therefore
new files are created locally, but existing files are updated at their
VPATH location.
OpenBSD and FreeBSD makes, however, will never perform a
VPATH search for a dependency which has an explicit rule.
This is extremely annoying.
When attempting a VPATH build for an autoconfiscated package
(e.g,, mkdir build && cd build && ../configure), this means the
GNU
make will build everything locally in the build
directory, while BSD make will build new files locally and
update existing files in the source directory.
% cat Makefile
VPATH = ..
all: foo.x bar.x
foo.x bar.x: newer.x
@echo Building $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
Building foo.x
Building bar.x
% pmake # NetBSD make
Building foo.x
Building ../bar.x
% fmake # FreeBSD make, OpenBSD make
Building foo.x
Building bar.x
% tmake # Tru64 make
Building foo.x
Building bar.x
% touch ../bar.x
% make # GNU make
Building foo.x
% pmake # NetBSD make
Building foo.x
% fmake # FreeBSD make, OpenBSD make
Building foo.x
Building bar.x
% tmake # Tru64 make
Building foo.x
Building bar.x
Note how NetBSD make updates ../bar.x in its VPATH location, and how FreeBSD, OpenBSD, and Tru64 make always update bar.x, even when ../bar.x is up to date.
Another point worth mentioning is that once GNU make has
decided to ignore a VPATH filename (e.g., it ignored
../bar.x in the above example) it will continue to ignore it when
the target occurs as a prerequisite of another rule.
The following example shows that GNU make does not look up
bar.x in VPATH before performing the .x.y rule,
because it ignored the VPATH result of bar.x while running
the bar.x: newer.x rule.
% cat Makefile
VPATH = ..
all: bar.y
bar.x: newer.x
@echo Building $@
.SUFFIXES: .x .y
.x.y:
cp $< $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
Building bar.x
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
make: *** [bar.y] Error 1
% pmake # NetBSD make
Building ../bar.x
cp ../bar.x bar.y
% rm bar.y
% fmake # FreeBSD make, OpenBSD make
echo Building bar.x
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
*** Error code 1
% tmake # Tru64 make
Building bar.x
cp: bar.x: No such file or directory
*** Exit 1
Note that if you drop away the command from the bar.x: newer.x
rule, GNU make will magically start to work: it
knows that bar.x hasn't been updated, therefore it doesn't
discard the result from VPATH (../bar.x) in succeeding
uses. Tru64 will also work, but FreeBSD and OpenBSD still don't.
% cat Makefile
VPATH = ..
all: bar.y
bar.x: newer.x
.SUFFIXES: .x .y
.x.y:
cp $< $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
cp ../bar.x bar.y
% rm bar.y
% pmake # NetBSD make
cp ../bar.x bar.y
% rm bar.y
% fmake # FreeBSD make, OpenBSD make
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
*** Error code 1
% tmake # True64 make
cp ../bar.x bar.y
It seems the sole solution that would please every make
implementation is to never rely on VPATH searches for targets.
In other words, VPATH should be reserved to unbuilt sources.
Separated dependencies simply refers to listing the prerequisite of a target, without defining a rule. Usually one can list on the one hand side, the rules, and on the other hand side, the dependencies.
Solaris make does not support separated dependencies for targets defined by single suffix rules:
$ cat Makefile
.SUFFIXES: .in
foo: foo.in
.in:
cp $< $ $ touch foo.in
$ make
$ ls
Makefile foo.in
while GNU Make does:
$ gmake
cp foo.in foo
$ ls
Makefile foo foo.in
Note it works without the `foo: foo.in' dependency.
$ cat Makefile
.SUFFIXES: .in
.in:
cp $< $ $ make foo
cp foo.in foo
and it works with double suffix inference rules:
$ cat Makefile
foo.out: foo.in
.SUFFIXES: .in .out
.in.out:
cp $< $ $ make
cp foo.in foo.out
As a result, in such a case, you have to write target rules.
Commands like `cp -p' and `touch -r' typically do not copy file timestamps to their full resolutions (see Limitations of Usual Tools). Hence you should be wary of rules like this:
dest: src
cp -p src dest
as dest will often appear to be older than src after the timestamp is truncated, and this can cause make to do needless rework the next time it is invoked. To work around this problem, you can use a timestamp file, e.g.:
dest-stamp: src
cp -p src dest
date >dest-stamp