20.6. Writing Command Lines
Writing good, portable Makefile files
is a bit of an art.
Skill comes with practice and experience.
Here are some tips to get you started:
Naming your file Makefile instead of
makefile usually causes it to be listed first
This makes it easier to find in a directory with many files.
Remember that command lines must start
with a leading tab character.
You cannot just indent the line with spaces,
even eight spaces.
If you use spaces, make exits with an
unhelpful message about “missing separator characters.”
Remember that $ is special to make.
To get a literal $ into your command lines,
This is particularly important if you want to access an environment
variable that isn't a make macro.
Also, if you wish to use the shell's $$
for the current process ID, you have to type it as $$$$.
Write multiline shell statements, such as shell conditionals
and loops, with trailing semicolons and a trailing backslash:
if [ -f specfile ] ; then \
... ; \
... ; \
Note that the shell keywords then and else
don't need the semicolon.
(What happens is that make passes the backslashes
and the newlines to the shell.
The escaped newlines are not syntactically important, so the
semicolons are needed to separate the different parts of the command.
This can be confusing.
If you use a semicolon where you would normally put a newline
in a shell script, things should work correctly.)
Remember that each line is run in a separate shell.
This means that commands that change the shell's environment
(such as cd) are ineffective across multiple
The correct way to write such commands is to separate commands on the
same line with a semicolon:
cd subdir; $(MAKE)
For guaranteed portability, always set SHELL
Some versions of make use whatever value
is in the environment for SHELL, unless
it is explicitly set in the Makefile.
Use macros for standard commands.
make already helps out with this,
providing macros such as $(CC),
$(YACC), and so on.
When removing files, start your command line with
-$(RM) instead of $($RM).
(The – causes make to
ignore the exit status of the command.)
This way, if the file you were trying to remove doesn't
exist, and rm exits with an error,
make can keep going.
When running subsidiary invocations of make,
typically in subdirectories of your main program tree, always
use $(MAKE), and not make.
Lines that contain $(MAKE) are always
executed, even if -n has been provided,
allowing you to test out a whole hierarchy of Makefile
This does not happen for lines that invoke make directly.
Often, it is convenient to organize a large software project into
subprojects, with each one having a subdirectory.
The top-level Makefile then just
invokes make in each subdirectory.
Here's the way to do it:
SUBDIRS = proj1 proj2 proj3
for i in $(SUBDIRS); \
echo ====== Making in $$i ; \
( cd $$i ; $(MAKE) $(MAKEFLAGS) $@ ) ; \
Copyright © 2003 O'Reilly & Associates. All rights reserved.