Ch 7 -- The make Utility
UNIX Unleashed, Internet Edition
- 7 -
The make Utility
by Sean Drew
This chapter is intended to be an overview of the UNIX make facility.
Most of the salient features of make are covered; however, you should consult
your man page for additional information because versions of make
tend to differ between different UNIX vendors. Following is a list of the major topics
covered in this chapter.
- Makefiles. This section gives an overview of the contents of a makefile.
- Target Lines. This section describes how to inform the make utility
what items can be built.
- Shell Command-Lines. This section explains how to encode Unix shell statements
in a makefile.
- Macros. This section tackles how and when to define macros in makefiles.
- make Directives. This section describes special make
commands can be included in a makefile.
- Command-Line Arguments. This section enumerates what command-line arguments
can be passed to the make utility in order to alter make's behavior.
- Different make Programs. This section reviews a few of the more
commonly available versions of make that offer features not found in the
"standard" make.
- make Utilities. This section reviews a few of the more commonly
available make utilities.
Introduction to make
The UNIX make utility was originally designed for maintaining C program
files in order to prevent unnecessary recompilation. However, make is eminently
useful for maintaining any set of files with interdependencies. For example, make
can be used to maintain C++ or HTML source code. In fact, NIS (Network Information
Service) uses make to maintain user information. The make utility
provides a way of codifying the relationships between files as well as which commands
need to be generated to bring files up to date when they are changed. The make
utility provides a powerful, nonprocedural, template-based way to maintain files.
The basic concept of make is akin to logic programming in languages such
as Prolog. You tell make what needs to be done and supply some rules, and
make figures out the rest.
Makefiles
A makefile is set of make commands. The makefile describes what
set of files can be built and how to build those files. Four types of lines are allowable
in a makefile: target lines, shell command lines, macro lines, and make
directive lines (such as include). Comments in a makefile are denoted by
the pound sign (#). When you invoke make, it looks for a file named
makefile in your current working directory. If makefile does not
exist, then make searches for a file named Makefile. Some UNIX
versions also search for additional files, in the following order: s.makefile,
SCCS/s.makefile, s.Makefile, and SCCS/s.Makefile. If you
don't want to use one of the default names, other files can be used with the -f
command-line option. (See the "Command-Line Options" section later in this
chapter for more information.) The convention used to identify makefiles not named
makefile or Makefile is to use the .mk suffix (for example,
foo.mk).
Target Lines
Target lines tell make what can be built. Target lines consist
of a list of targets, followed by a colon (:), followed by a list of dependencies.
Although the target list can contain multiple targets, typically only one target
is listed. The target list cannot be empty; however, the list of dependencies can
be empty. Following are some example target lines:
singleTarget: dependency1 dependency2 #target with 2 dependencies
target1 target2: dependency1 dependency2 #target list 2 dependencies
target: #no dependencies
The dependency list consists of targets that must be older than the current target
after make successfully executes. In other words, the target must be newer than any
of its dependent targets. If any of the dependent targets is newer than the current
target or if the dependent target does not exist, the dependent targets must be made
and then the current target must be made. If the list of dependencies is empty, the
target is always made.
Filename pattern matching can be used to automatically generate a dependency list
for a target. The shell metacharacters asterisk (*), question mark (?),
and braces ([]) can be used. For example, if parse.cc, main.cc,
and io.cc were all the files ending with .cc in a directory with
a makefile, then the following two target lines would be identical.
main: parse.cc main.cc io.cc # list all the dependent files manually
main: *.cc #use shell metacharacters to generate dependent list
Typically, the list contains dependent items that are used in the construction
of the target. For example, the dependent list may be comprised of object files that
make up an executable
program: part.o component.o module.o
or header and source files that an object file depends on
module.o: module.cc module.hh part.hh
However, dependent targets do not have be components of the target. For example,
the dependent targets might be actions you need to perform before a target is built,
such as making a backup of the current target before it is rebuilt.
Lines following a target line are usually the commands required to build the target.
See the "Shell Command-Lines" section for more detail.
Library Targets
Library targets are a special type of target. A special syntax is supported
for library targets (Check the man page on ar for details on using
UNIX libraries or to see if your UNIX system has ar.). Library targets allow
for the addressing of individual modules within the library. If a target or dependency
includes parentheses (()), then the target or dependency is considered to
be a library.
libfoo.a(foo.o): foo.cc foo.hh #the object file foo.o in library
libfoo.a depends on foo.hh and foo.cc
libfoo.a: libfoo(foo.o) libfoo(bar.o) #libfoo.a depends on the
two object files foo.o and bar.o
Some make variants allow multiple object files to be listed within the
parentheses (see the following line of code). Some make variants, however,
allow only one object file to be listed within the parentheses.
libfoo.a: libfoo.a(foo.o bar.o) #libfoo.a depends
on the two object files foo.o and bar.o
Do not use any spaces between the parentheses, unless you can specify multiple
object files within the library--in which case the spaces can only appear between
the object files.
Sometimes you wish to use different compile options among different dependencies
of a library target. However, make only allows a target to be defined once
with a single colon. Double colons enable you to define a target multiple times with
different lists of dependencies. Suppose there was a particular troublesome module,
buggy.o, that needed to be compiled with debugging information, while the
more stable object files did not need debugging information. The target lines
libfoo.a:: libfoo.a(foo.o bar.o) #target list one
libfoo.a:: libfoo.a(buggy.o) #target list two
would enable the building of foo.o and bar.o without debugging
information (typically -g to most UNIX C/C++ compilers) and buggy.o
with debugging information included.
Rule Targets
One of the more powerful features of the make utility is its capability
to specify generic targets, also known as suffix rules, inference rules,
or just simply rules. Rules are a convenient way to tell make only
one time how to build certain types of targets. Consider the makefile excerpt
foo.o: foo.cc
CC -c foo.cc -I. -I/usr/local/include -DDEBUG +g #shell command-line
bar.o: bar.cc
CC -c bar.cc -I. -I/usr/local/include -DDEBUG +g #shell command-line
main.o: main.cc
CC -c main.cc -I. -I/usr/local/include -DDEBUG +g #shell command-line
main: foo.o bar.o main.o
CC foo.o bar.o main.o
which has a great deal of redundancy. All of the shell command lines are identical,
except for the file being compiled. This is a prime candidate for a rule. The following
rule tells make how to transform a file ending in .cc to a file
ending in .o.
.cc.o:
CC -c $< -I. -I/usr/local/include -DDEBUG +g
The preceding rule makes use of a special macro, $<, which substitutes
the current source file in the body of a rule (see the "Special Built-In Macros"
section for more information). Applying the .cc.o rule to the previous makefile
simplifies the makefile to
.cc.o:
CC -c $< -I. -I/usr/local/include -DDEBUG +g
main: foo.o bar.o main.o
CC foo.o bar.o main.o
There are many default inference rules supplied by make. It is able to
determine which rule to apply by applying the following algorithm:
Search the list of rules--both default and user supplied--for a rule that will
build the desired file type. File type is determined by the file extension. The file
extension is the set of characters after a dot (.). If a file of the specified
type exists, the rule is applied. For example, if a makefile specified only two rules
in the following order, a .c.o rule and a .cc.o, and make
was asked to create foo.o, the following set of events would occur:
- 1. make would first try to apply the .c.o rule. The
root of the target foo.o (foo) (the root is the filename with the
first extension removed (.o)) is used to apply the .c.o rule.
2. The .c.o rule tells make that in order to make a file
named root.o, a file named root.c
can be employed.
3. If the file root.c exists in the current directory,
then apply the rule to root.c. In the example, the file
foo.c is checked for existence in the current directory.
4. If foo.c does not exist, then foo.cc is checked for existence.
5. If foo.cc does not exist, then an error is reported that the target
cannot be made.
Rules can be chained together by make in order to reach a target goal.
(Some versions of make do not perform rule chaining.) For example, say you
are working with a CORBA IDL (Common Object Request Broker Architecture Interface
Definition Language) compiler. The IDL compiler takes interface definitions as input
and creates C++ source as output. For example, if you ask make to create
foo.o, given a makefile that has an .idl.cc and a .cc.o
rule, make does the following:
- 1. If foo.cc does not exist, make then looks for foo.idl
to create the foo.cc file.
2. After the foo.cc file is created, the .cc.o rule is applied
to reach the final goal of foo.o.
This chaining of rules is a very powerful feature of make.
Double Suffix Rules
The rules defined so far are known as double suffix rules because they contain
double suffixes, such as .rtf.html. The .rtf.html rule can be used
to convert a Rich Text Format file to a HyperText Markup Language File. Double suffix
rules describe how to transform root.suffix1 to root.suffix2
(for example, index.rtf to index.html). Following is a list of
the more commonly available double suffix rules supplied as defaults by make:
.c.o .c~.o .c~.c .c.a .c~.a .C.o .C~.o .C~.C .C.a .C~.a
.cc.o .cc~.o .cc~.cc .cc.a .cc~.a .h~.h .H~.H
.s.o .s~.o .s~.a .p.o .p~.o .p~.p .p.a .p~.a
.f.o .f~.o .f~.f .f.a .f~.a .r.o .r~.o .r~.r .r.a .r~.a
.y.o .y~.o .y.c .y~.c .l.o .l~.o .l.c
You may redefine any of the default rules supplied by make. The default
rules take advantage of standard macros in order to make the default rules more generic.
For example, the way a C or C++ file is compiled does not change much, other than
some of the flags supplied to the compiler. The most commonly used of these macros
are LDFLAGS, CFLAGS, and CXXFLAGS, which are used to parameterize
the linker (ld), the C compiler (cc), and the C++ compiler (CC
on HP-UX), respectively. The LIBS macro is commonly used but is not incorporated
into the default rules. The LIBS macro is used to define which libraries
other than the system default libraries are to be used at link time and in what order
should the libraries be evaluated.
The tildes (~) in the double suffix rules refer to an SCCS (Source Code
Control System) file. SCCS files have a prefix prepended to a filename, which is
in direct conflict with make because make bases all of its algorithms
on suffixes. For example, the file foo.cc becomes s.foo.cc when
using SCCS. The tilde signals make to treat the file as an SCCS file, so
that .cc~.o can be used to transform s.foo.cc to foo.o
with the command make foo.o, which is preferable to the command make
s.foo.o && mv s.foo.o foo.o.
Single Suffix Rules
Single suffix rules describe how to transform root.suffix1 to
root (for example, cat.c to cat). The second suffix
is in effect null in a single suffix rule. Single suffix rules are useful for creating
programs that are composed of a single source file. In fact, if you have the CFLAGS
and LDFLAGS environment variables defined, you don't need a makefile to
effectively use the .c rule, as the .c rule is part of the default
set of rules. Assuming that a Bourne-compatible shell and the source files cat.c,
echo.c, cmp.c, and chown.c are in the current directory,
the following commands build the targets cat, echo, cmp,
and chown without a makefile:
% export CFLAGS="-I. -DDEBUG +g" LDFLAGS="-lfoo -lbar"
% make cat echo cmp chown
Following is a list of the more commonly available single suffix rules supplied
as defaults by make:
.c .c~ .C .C~ .cc .cc~ .sh .sh~ .p .p~ .f .f~ .r .r~
Built-In Targets
Make supplies several built-in targets for modifying the behavior of make.
Some of the built-in targets accept dependencies, but the dependencies are really
arguments to the built-in target. For example, the arguments to .PRECIOUS
are file suffixes. Table 7.1 lists the build-in targets.
Table 7.1. Built-in targets for make.
Target |
Target Description |
.IGNORE |
The .IGNORE default target causes make to ignore nonzero error
codes returned from the command-lines specified to build the target. The default
make behavior is to cease all processing and exit when a command-line returns
a nonzero status. The -i make command-line option can be used to
achieve the same behavior. |
.SILENT |
The .SILENT default target tells make to echo any of the command-lines
it is executing. By default, make echoes any command-line that is not preceded
by the at sign (@). The -s make command-line option can
be used to achieve the same behavior. |
.DEFAULT |
make executes any commands associated with the .DEFAULT target
if no other rule can be applied. The purpose is similar to the default:
case in C/C++ and C shell switch statements. |
.PRECIOUS |
make deletes all the files it has built when it receives a signal or encounters
a non-zero return code from a shell command. There are certain types of files that
you do not want make to delete even if there are errors. These precious
files are arguments to the .PRECIOUS target. The .PRECIOUS target
may appear multiple times in a makefile, with each occurrence appending to the list
of precious files. .PRECIOUS is useful for commands that generate source
(lex, yacc, IDL compilers) or fetch source (RCS, SCCS, and so on). |
.SUFFIXES |
The .SUFFIXES default target supplies make with the list of known
file types that make will process. A blank .SUFFIXES target will
reset the suffix list to an empty state. The .SUFFIXES target may appear
multiple times in a makefile, with each occurrence appending to the list of known
suffixes. The order of the suffixes is important, as that is the order rules are
tried. To rearrange the order, use a blank .SUFFIX target to clear the current
suffix list, then subsequent .SUFFIX targets can specify a new order. Common
default suffixes are: .o .c .c~ .C .C~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~
.h .h~ .H .H~ .p .p~ .f .f~ .r .r~ . |
Common Targets
By convention, there are some common targets in makefiles. These common targets
are usually not files, and are known as dummy targets. One of the most common
of the dummy targets is clean. The command make clean generally
removes all of the built files, which are typically programs and object files. Clobber
is a more severe target, which removes all files and associated directories. The
command make clobber is often used to uninstall software. For makefiles
that build and install software, install is often a target. The command
make install usually creates the programs, installs the man pages,
and copies the program to its intended location. Another common target is all.
When a makefile builds several targets, make all typically builds all the
targets.
Shell Command-Lines
Shell command-lines, also known more simply as commands, define the actions that
are used to build a target. Any text following a semi-colon (;) on a target
line is considered a command. All subsequent lines after a target that begin with
a tab are also commands for the target. The comment character of a pound sign (#)
is allowed within a target's command definition. For example, the following makefile
excerpt shows a comment embedded in a command definition:
foo.html: foo.rtf
rtftohtml -hx $<
#place current date in file, $$$$ expands to $$ in shell
sed s/__DATE__/"'date'"/ $@ > foo.$$$$ && mv foo.$$$$ $@
The first line that is not a comment or does not start with a tab ends the list
of commands associated with the target. Long command-lines can be continued on the
next line using the backslash () newline sequence.
foo.html: foo.rtf
rtftohtml -hx $<
sed -e "s/PAGE_DATE/'date'/"
-e "s/PAGE_TITLE/$(DOCTITLE)/"
-e "s/PAGE_NAME/$(HOMETITLE)/" $@
> foo.$$$$ && mv foo.$$$$ $@
Any macros embedded in the command are evaluated, and the proper value is substituted
by make; as a result, the shell sees only the values of the macros.
Normally, the commands are echoed to the standard output, unless the command is
preceded by the at sign (@). Use of the @ directive provides a
finer grain of output control than the .SILENT target or the -s
command-line option--both of which turn off all command output. The @ directive
is particularly useful when issuing an echo command. The command echo Build complete
at 'date' without the @ directive would produce the output
echo Build complete at 'date'
Build complete at Mon May 12 02:32:37 MST 1997
while using the @ results in the cleaner output of
Build complete at Mon May 12 02:32:37 MST 1997
Note that the @ directive, the -s option, and the .SILENT
target only suppress the echoing of the command; the output of the command is still
shown. The output of the following makefile snippet
target:
#echo this command to standard out
echo echoed to standard out
#do not echo this command to standard out
@echo not echoed to standard out
for target is
% make target
echo echoed to standard out
echoed to standard out
not echoed to standard out
The comment character can appear after shell commands; however, the comment is
not a make comment. The # sign and all the following text are passed
to the shell. The good news in this case is that the # sign is a comment
in just about any shell, and the net effect is the same.
Macros
Macros serve the following four purposes in a makefile:
- 1. Macros can save you a great deal of typing. By specifying lists of
information as macros, the list of information can be referenced simply by using
the macro. The following makefile snippet shows a list of object files used to build
program in bold:
program: oh.o dot.o polka.o disor.o o.o whoa.o doe.o
$(CPLUSPLUS) oh.o dot.o polka.o disor.o o.o whoa.o doe.o $(LIBS) $(LDFLAGS) -o $@
- The following makefile snippet introduces a macro, OBJECTS, for the
list of object files and has the macro reference bolded:
OBJECTS = oh.o dot.o polka.o disor.o o.o whoa.o doe.o
program: $(OBJECTS)
$(CPLUSPLUS) $(OBJECTS) $(LIBS) $(LDFLAGS) -o $@
- Notice how much more succinct the second example makefile is.
2. Macros increase maintainability by allowing information to reside in
only one place within the makefile. When information is in only one spot, that information
needs to be changed in only one spot. For example, the list of object files needed
to create a program needs to appear in the makefile in the dependency list and in
the link command, as in the makefile example in step one. If a new object file, yo.o,
is added to the executable, you must remember to update at least two places: the
dependency list and the link command. By placing the object files in a macro, only
the macro definition must be changed. The ease of updating macros is coupled with
the side benefit of error reduction; if more than one part of the makefile needs
to be updated, odds are that one of the parts will not be updated.
3. Macros provide a way to introduce variability into a makefile by parameterizing
what is likely to change. For example, whether or not a program should be built with
debugging information included can be determined through a macro. By changing the
value of the macro, which can be done via the make command-line or an environment
variable, the desired behavior can be achieved without modifying the makefile.
4. Macros improve the readability of makefiles. A long list of object files
distorts what is really being done in a makefile. The macro not only provides a convenient
shorthand, but documentation as well. Consider the example makefiles presented in
step one; the second version is easier to read.
Macro definitions, in order of preference, can come from four places: make
internal defaults, environment variables, the makefile(s), and the command-line.
The precedence order can be changed via the -e make command-line
option to have environment variables override makefile macro definitions. See the
"Command-Line Options" section for a discussion of make command-line
options.
Macro Syntax
See the "Command-Line Macro Definition" for information on how to define
macros on the command-line. The basic syntax for defining macros within a makefile
is
name = valueList
The name may consist of any combination of uppercase (A-Z) and
lowercase (a-z) letters, digits (0-9), and underlines (_). Macro names are all uppercase
by convention. Depending on your version of make, certain punctuation characters
are allowed in a macro name, such as the caret (^) or at sign (@).
Unless strange compulsions force you to name macros ^foo*@, such punctuation
usage is strongly discouraged; it seldom helps readability or portability.
The equal sign (=) can migrate rather freely about the macro assignment
expression because blanks and tabs surrounding the equal sign are removed. As a result
of the white space removal behavior, all of the following assignments produce the
same result, the string VALUE is assigned to the name NAME:
NAME=VALUE
NAME = VALUE
NAME= VALUE
NAME =VALUE
The valueList may contain zero, one, or more entries, as demonstrated
in the following:
BLANK =
ONE_VALUE = one
LIST_VALUE = one two three
The valueList can be quite long and the backslash () newline
escape may be used to continue a definition on another line. If the line is continued,
the newline is translated to a space by make and all subsequent white space
(blanks, tabs, and newlines) are removed. Thus, the makefile
BAR=one
space
X:
echo $(BAR)
would produce the following output if target X were made:
echo one space
one space
Other than the white space translations mentioned previously, white space in a
macro definition is preserved.
Macro definitions can use other macros. Nested definitions cannot be recursive,
or make will complain.
RECURSIVE = $(BAD) #don't do this
BAD = $(RECURSIVE) #don't do this
FIRST_HALF = first
SECOND_HALF = second
NESTED = $(FIRST_HALF) $(SECOND_HALF)
NESTED_AGAIN = zero.$(FIRST_HALF).$(SECOND_HALF)
Ordering is not important when defining macros. In the preceding example, the
macro NESTED could have been defined before FIRST_HALF and SECOND_HALF.
A macro does need to be defined before it is used in any target line as a dependency.
If a macro is defined multiple times, the last value is used. This means that a macro
cannot have one value for part of the makefile and a different value for another
part of the makefile. If the value of a macro needs to be changed, a recursive call
to make is needed with the new value passed in the command-line, as in the
following example:
MACRO_NAME=oldValue
target:
$(MAKE) MACRO_NAME=newValue target
A macro is dereferenced by applying the dollar ($) operator and either
parenthesis (()) or curly braces ({}). For example, the macro MAY
could be dereferenced as $(MAY) or ${MAY}. However, in the case
of single character macros, just the $ suffices, so the macro Z
could be dereferenced as $Z, $(Z), or ${Z}. However, in
the case of single character macros, the use of () or {} is encouraged.
Single character names are not good to use in general; a more descriptive name will
be appreciated by the next person to read the makefile.
If a macro is undefined or is assigned a blank value, the null string is substituted
for its value. The makefile
BLANK=
X:; echo foo$(BLANK)$(UNDEFINED)bar
produces the following output for target X:
echo foobar
foobar
If you need to use a dollar sign ($) in make, it needs to be
escaped with another dollar sign. Multiple consecutive occurrences of the dollar
sign are allowed:
#echo environment variable $LOGNAME and process id $$
foo:
echo $$LOGNAME $$$$
Macro Substitution
The make utility supports a simple text substitution function for macros.
The syntax is :oldString=newString, which is appended immediately following
the macro name in macro reference. For example, if the macro OBJS were defined
as
OBJS = fuggles.o perle.o cascade.o saaz.o
the .o extension could be replaced by the .cc extension by using
the macro reference, $(OBJS:.o=.cc), which would evaluate to fuggles.cc
perle.cc cascade.cc saaz.cc. The macro string substitution is somewhat limited
in capability, but works well for maintaining files that differ only in suffix or
trailing characters.
Special Built-In Macros
The make utility provides several special built-in macros in order to
allow rules to be generic. If the rules were not generic, then the filenames would
be hard coded into the rule, and thus, not really a rule, because one rule would
be required for every filename. The built-in macros can be referenced without parenthesis.
For example, the @ macro can be referred to as $@ instead of $(@).
The built-in macros may not have a value, depending at what state make is
in when the macro is evaluated. Some macros are only valid during suffix rule evaluation,
while other macros are only valid during regular rule evaluation. Table 7.2 lists
the built-in macros.
Table 7.2. Built-in macros for make.
Macro |
Macro Description |
$@ |
The value of the entire current target name is substituted for $@. However,
in the case of a library target, the value is the name of the library, not the name
of the archive member to be placed in the library. $@ can be used in target
and suffix rules. |
$% |
The value of the current archive member is substituted for $%, so this macro
is only valid when the current target is a library. Remember that a library target
has the form of lib(object.o) or lib((kernel_entry)). $%
is needed because $@ evaluates to the library name for library targets.
$% can be used in target and suffix rules. |
$? |
The list of dependents that are out of date for the current target is substituted
for $?. $? can be used in target and suffix rules. However, $?
evaluates to possibly many names in a target rule, but only evaluates to one name
in a suffix rule. |
$< |
The current source file is substituted for $<. The current source file
is the file that is out of date with respect to the current target, based on the
implicit rule that is being invoked. While that sounds very complicated, it really
boils down to which file is currently being manipulated to build the target. For
example, in a .cc.o rule, $< would be whichever .cc
file is being compiled. $< is only valid in a suffix rule or in the .DEFAULT
rule. |
$* |
The root of the current target name is substituted for $*. For example,
if the target were foo.o, the root would have the suffix .o deleted
for an end result of foo. $* is valid only during evaluation of
inference rules. |
The preceding built-in macros can have special modifiers appended to the end of
the macro to return the filename or directory name portion. Use F to retrieve
the filename, and D to retrieve the directory name. Note that only uppercase
F and D will work. The shortcut method without parenthesis may
not be used when a modifier is applied. For example, if $< evaluated
to /users/dylan/foo.cc, $(<F) would return foo.cc and
$(<D) would return /users/dylan. Some versions of make
return a trailing slash appended to directory names, so using the previous example,
$(<D) would return /users/dylan/. If the macro evaluates
to multiple values, as does the $? macro, the F or D modifier
is applied to each of the multiple values in turn. (Some versions of make
do not support the F and D modifiers.)
In addition to the five macros previously discussed, there is the dynamic dependency
macro $$@. The macro is called dynamic because it is evaluated at the time
the dependency is processed. $$@ can only be used on dependency lines. The
$$@ macro evaluates to the current target just as $@ does, but
$$@ is allowed on the dependency line, whereas $@ is not. The $$@
macro is useful for building executables made up of only one source file, as the
following makefile snippet demonstrates:
COMMANDS = cat dog say sed test true false more ar less
$(COMMANDS) : $$@.c
$(CC) $? -o $@
The macros previously discussed have values that are supplied by make
and are not modifiable by you. There are other macros that make uses but
in which you can modify the default value supplied by make. The macros VPATH
and SHELL fall into this category. Some versions of make have additional
macros of this type; however, VPATH and SHELL are the most widely
available.
VPATH is a path where make can search for dependent files. The
current directory is searched first, then each of the VPATH elements is
searched. VPATH uses colons (:) to delimit the list elements. For
example, if
VPATH = source:../moreSource:/the/rest/of/the/source
appeared in a makefile, make would first search for dependents in the
current directory, then in a subdirectory named source, followed by a sibling
directory named moreSource, and then the absolute directory of /the/rest/of/the/source.
The SHELL macro tells make which shell to use when processing
the command-line portions of a target. Most versions of make default to
the Bourne or POSIX shell (/bin/sh). To maximize portability, it is best
to write shell commands in the POSIX shell syntax and to set the SHELL variable
to /bin/sh, for example, SHELL = /bin/sh. Some versions of make
will only allow the use of Bourne shell syntax.
make Directives
A makefile is mostly composed of macro, command, and target lines. A fourth type
of line found in makefiles is lines that are directives to make. make
directives are one of the more nonstandardized areas of make. You are likely
to find many incompatibilities in this area. If portability is of concern to you,
you may want to avoid using make directive features.
The most common of the make directives is the include directive.
The include directive enables common definitions to be written once and
included. The include directive must be the first item on a line followed
by a filename, as in the following example.
include /project/global.mk
include /users/sdrew/myGlobal.mk
#rest of makefile
If your version of make does not have include directives, you
can fake the same behavior using multiple -f options (see the "Command-Line
Options" section for a description of the -f option). The following
command effectively emulates the previous example makefile:
% make -f /project/global.mk -f /users/sdrew/myGlobal.mk
If you grow tired of typing the -f command-line option, some shell trickery
should relieve the drudgery. You can write an alias or shell script to automatically
supply the include file options to make.
WARNING: For you C/C++ programmers, when using
the include directive, do not place a pound sign (#) in front of
the include directive (for example, #include foo.mk). The include
line will then be interpreted as a comment by make and the file will not
be included.
The comment (#) is a directive to make to ignore the line if
it is the first nonwhitespace character on a line. Comments can appear after other
make lines was well, as shown in the following:
foo=bar #assignment
target:;echo $(FOO) #target
#this whole line is a comment
Note that the comment directive is supported by all versions of make.
Command-Line Arguments
While make has methods of configuration from within a makefile, the make
command-line options provide a convenient way to configure make on-the-fly.
The typical sequence of make command-line arguments is shown here, although
arguments may appear in any order:
make [-f makefile] [options] [macro definitions] [targets]
Note that optional items are enclosed in braces ([]).
Command-Line Options
Command-line options are indicated with a dash (-) and then the option,
for example, make -e. If multiple options are needed, the options may be
preceded by only one dash, make -kr, or by using a dash per option, make
-k -r. Mixing option specification methods is allowed, make -e -kr.
Table 7.3 lists the command-line options for make.
Table 7.3. Command-line options for make.
Option |
Option Description |
-b |
Turns on compatibility mode for makefiles written before the current versions of
make. The -b option is usually on by default. |
-d |
Turns on debug mode. Debug mode is exceedingly verbose and is generally only used
as a last resort when debugging a makefile. Information about file dates, internal
flags, and variables are printed to the standard out. |
-e |
Environment variables override assignments made in a makefile. |
-f filename |
Denotes the name of a file to be used as a makefile. Multiple -f options
may be used; the files are processed in the order they appear on the command line.
A hyphen (-) may be used to indicate that make should read commands
from the standard input. Multiple -f - options are not allowed.
The -f option is the only option that requires an argument. A space must
appear between the -f argument and the filename that appears afterward.
The -f option can be used as a "poor man's" include directive
if your version of make does not support the include directive. |
-p |
Prints all the macro definitions, suffix rules, suffixes, and explicit description
file entries to the standard output. The -p option is useful for interrogating
make's set of default rules. |
-i |
Places make into ignore mode. When make is in ignore mode, non-zero
error codes returned by commands will no longer cause make to terminate
the building of all targets. The ignore mode can be entered by placing the .IGNORE
target in a makefile. |
-k |
Instructs make kill work on the current target only if a non-zero error
code is returned by a command. Work on the other targets may continue. This is the
opposite of the -S mode. If both -k and -S are supplied,
the last one specified is used. This overriding behavior provides a way to override
the presence of a -S in the MAKEFLAGS environment variable. |
-n |
Places make into no execute mode. When in no execute mode, make
will just print the commands rather than execute the commands. Lines beginning with
the at sign (@), which are not normally printed, will be printed. Lines
that have the string $(MAKE) or ${MAKE} are executed so that all
the commands can be seen. |
-q |
Places make into question mode. make will return a zero status
code if all the targets are up to date, and a non-zero status code if any one of
the targets is out of date. |
-r |
Removes built-in suffix list and built-in rules. This will put make in a
pristine state, such that only user-specified rules and suffixes are used. |
-s |
Places make in silent mode. Normally commands are executed to the standard
output, unless the commands are preceded with the at (@) symbol. The -s
option has the same effect as including the .SILENT target in the makefile. |
-S |
Places make in standard error handling mode. The -S option
will have make terminate building all targets if any command returns a non-zero
status. If both -k and -S are supplied, the last one specified
is used. This overriding behavior provides a way to override the presence of a -k
in the MAKEFLAGS environment variable. -S is the default mode. |
-t |
Places make in touch mode. When in touch mode, make will not issue
the commands associated with a rule, but will simply touch the files. (Consult
your man page for description of the UNIX command touch.) |
Command-Line Macro Definition
Macros can be defined on the command line using the name=value syntax.
Zero or more macro definitions may be supplied on the command line. Command-line
macros have the highest precedence and override macros defined internally by make,
macros from the current environment, and macros specified in the makefile. Command-line
macros provide a convenient way of temporarily overriding current settings without
changing them in the environment or in the makefile. For scripting, command-line
macros help ensure consistent execution from run to run.
Command-Line Target Specification
Zero or more targets can be specified on the make command line. If no
target is provided on the command line, make searches for the first nonrule
target in the first makefile, and then each subsequent makefile, and tries to update
the target if one is found. Targets specified on the command line should be listed
in one of the makefiles currently being used by make. Each of the targets
specified is updated by make in the order the arguments appeared on the
command line.
Different Make Programs
While make is a very powerful tool, the "standard" versions
of make have some rather gaping feature holes (for example, no conditional
statements such as if). As a result, there are other versions of make
available that try to fill some of the feature gaps. In addition to extra features,
other make offerings offer a portability solution. You can either try to
write a makefile that matches the lowest common denominator feature set while exercising
the fewest bugs for various UNIX platforms, or use the same make offering
on all platforms. If you are not distributing your makefile for public consumption,
the latter choice is much more palatable. Some of the more commonly available make
offerings are covered in this following sections.
GNU make
If you intend to use make extensively for software development, you need GNU's
version of make. This is especially true if you develop software on multiple
UNIX platforms. GNU processes "standard" makefiles better than most UNIX
platforms "standard" offering. If after reading this section you feel you
absolutely must have GNU make, then look up http://www.gnu.org in your WWW browser.
You can also use your favorite Internet search engine and search for gmake
or GNU make.
Conditionals
GNU make provides a full complement of if statements for conditional
processing within a makefile.
Calling Shell Using $(shell)
GNU make has the handy ability to substitute the output of any shell
command as though it were a macro. When used in conjunction with if statements,
$(shell) is very handy for parameterizing a makefile automatically. For
example,
HOSTTYPE = $(shell uname)
ifeq "$(HOSTTYPE" "HP-UX"
# config host environment
endif
Pattern Rules
Pattern rules are a powerful extension to suffix rules. The pattern rule has the
form of targetPattern: dependencyPattern, which is the opposite order of
a suffix rule. For example, .cc.o expressed as a pattern rule would be %.o:
%.cc. The % in the pattern rule operates as the asterisk (*)
wildcard operates in the shell, which allows more than just a suffix to specify a
rule. For example, if you have a directory of .gif files that you wish to
enlarge with a command-line utility for users with larger screens, the following
makefile with a pattern rule would do the job nicely:
%_big.gif: %.gif
giftran -x 125 $< > $*_big.gif
GIFS = $(shell ls *.gif)
BIG_GIFS: $(GIFS:.gif=_big.gif)
Other Nifty Features
GNU make is loaded with far too many nifty features to list here, but
some of the more commonly used are: := operator, simplified library syntax
- libfoo.a(one.o two.o), and extra text processing functions.
imake
Include Make, or imake, is a pre-processor for the make utility.
The C pre-processor provides functionality that make does not offer: include
directives, if directives, macro functions. imake is used in the
X distribution. imake works by providing a template file that is then processed
to create a file for use by make. imake can be a bit of a pain
to use because the documentation is somewhat poor and the extra level of indirection
can be cumbersome. Because of the template ability, large project trees can be generated
automatically, which can offset some of the pain of use.
If you want to get a copy of imake, try ftp://ftp.primate.wisc.edu/pub/imake-book/itools.tar.Z
or ftp://ftp.primate.wisc.edu/pub/imake-book/itools.tar.gz. The tar file contains,
among other files, the following: imake, makedepend, xmkmf, mkdirhier, imboot, msub,
imdent. Another good place to look for imake is ftp://ftp.x.org which contains the
X11 R5 distribution directory structure, so you can retrieve imake without having
to pull the entire X11 distribution. You can also consult your favorite Internet
search engine using the keyword imake.
nmake
Developed by AT&T, nmake has been tailored to help meet the demands
of large scale C development. nmake plugs many of the standard holes of
make, and has a few new twists of its own. nmake is the only make
that stores compile options for use in subsequent runs of make. That enables
the ability to ask make to build any file that was not built with a certain
macro definition or compiler switch. Another good feature of nmake is its
capability to include multiple shell lines in a single shell without your bending
over backward using semicolons and backslash newline.
make Utilities
Several utilities are available to enhance the usability of make. A few
of the more common utilities are briefly covered in the following sections.
makedepend
A makefile is intended to document the relationships between files as well as
the rules for building those files. makedepend provides a way of automating
the documentation of the file relationships by generating a dependency list for your
source files. makedepend takes into account #if directives for
proper dependency generation.
mkmf
mkmf is short for make makefile. mkmf examines all the
source in the current directory, searches the source for dependencies, and then generate
a makefile based on templates.
Summary
By now you should be familiar with the contents of a makefile and know how to
use make to manage your source files. We covered how to specify targets
for make and how to encode the instructions for building files into your
makefile. You may want to consult the chapters on UNIX shells to help you with shell
syntax. We also looked at efficiency of expression using make macros and
shell metacharacters. You may wish to read Chapter 6, "The C and C++ Programming
Languages" and see how make can help you manage your C and C++ source
files.
make is a powerful tool for maintaining sets of files with interdependencies.
Whether you are writing software or maintaining Web pages, make can make
your job easier (pun intended). Investing a little time learning make can
save you a lot of work.
©Copyright,
Macmillan Computer Publishing. All rights reserved.
|