Chapter 2. Rules
In the last chapter, we wrote some rules to compile and link our word-counting program. Each of those rules defines a target, that is, a file to be updated. Each target file depends on a set of prerequisites, which are also files. When asked to update a target, make
will execute the command script of the rule if any of the prerequisite files has been modified more recently than the target. Since the target of one rule can be referenced as a prerequisite in another rule, the set of targets and prerequisites form a chain or graph of dependencies (short for “dependency graph”). Building and processing this dependency graph to update the requested target is what make
is all about.
Since rules are so important in make
, there are a number of different kinds of rules. Explicit rules, like the ones in the previous chapter, indicate a specific target to be updated if it is out of date with respect to any of its prerequisites. This is the most common type of rule you will be writing. Pattern rules use wildcards instead of explicit filenames. This allows make
to apply the rule any time a target file matching the pattern needs to updated. Implicit rules are either pattern rules or suffix rules found in the rules database built-in to make
. Having a built-in database of rules makes writing makefiles easier since for many common tasks make
already knows the file types, suffixes, and programs for updating targets. Static pattern rules are like regular pattern rules except they apply only to a specific list of target files.
GNU make
can be used as a “drop in” replacement for many other versions of make
and includes several features specifically for compatibility. Suffix rules were make
’s original means for writing general rules. GNU make
includes support for suffix rules, but they are considered obsolete having been replaced by pattern rules that are clearer and more general.
Explicit Rules
Most rules you will write are explicit rules that specify particular files as targets and prerequisites. A rule can have more than one target. This means that each target has the same set of prerequisites as the others. If the targets are out of date, the same set of actions will be performed to update each one. For instance:
vpath.o variable.o: make.h config.h getopt.h gettext.h dep.h
This indicates that both vpath.o and variable.o depend on the same set of C header files. This line has the same effect as:
vpath.o: make.h config.h getopt.h gettext.h dep.h variable.o: make.h config.h getopt.h gettext.h dep.h
The two targets are handled independently. If either object file is out of date with respect to any of its prerequisites (that is, any header file has a newer modification time than the object file), make
will update the object file by executing the commands associated with the rule.
A rule does not have to be defined “all at once.” Each time make
sees a target file it adds the target and prerequisites to the dependency graph. If a target has already been seen and exists in the graph, any additional prerequisites are appended to the target file entry in make
’s dependency graph. In the simple case, this is useful for breaking long lines naturally to improve the readability of the makefile:
vpath.o: vpath.c make.h config.h getopt.h gettext.h dep.h vpath.o: filedef.h hash.h job.h commands.h variable.h vpath.h
In more complex cases, the prerequisite list can be composed of files that are managed very differently:
# Make sure lexer.c is created before vpath.c is compiled. vpath.o: lexer.c ... # Compile vpath.c with special flags. vpath.o: vpath.c $(COMPILE.c) $(RULE_FLAGS) $(OUTPUT_OPTION) $< ... # Include dependencies generated by a program. include auto-generated-dependencies.d
The first rule says that the vpath.o target must be updated whenever lexer.c is updated (perhaps because generating lexer.c has other side effects). The rule also works to ensure that a prerequisite is always updated before the target is updated. (Notice the bidirectional nature of rules. In the “forward” direction the rule says that if the lexer.c has been updated, perform the action to update vpath.o. In the “backward” direction, the rule says that if we need to make or use vpath.o ensure that lexer.c is up to date first.) This rule might be placed near the rules for managing lexer.c so developers are reminded of this subtle relationship. Later, the compilation rule for vpath.o is placed among other compilation rules. The command for this rule uses three make
variables. You’ll be seeing a lot of these, but for now you just need to know that a variable is either a dollar sign followed by a single character or a dollar sign followed by a word in parentheses. (I will explain more later in this chapter and a lot more in Chapter 3.) Finally, the .o/.h dependencies are included in the makefile from a separate file managed by an external program.
Wildcards
A makefile often contains long lists of files. To simplify this process make
supports wildcards (also known as globbing). make
’s wildcards are identical to the Bourne shell’s: ~
, *
, ?
, [...]
, and [^...]
. For instance, *.*
expands to all the files containing a period. A question mark represents any single character, and [...]
represents a character class. To select the “opposite” (negated) character class use [^...]
.
In addition, the tilde (~
) character can be used to represent the current user’s home directory. A tilde followed by a user name represents that user’s home directory.
Wildcards are automatically expanded by make
whenever a wildcard appears in a target, prerequisite, or command script context. In other contexts, wildcards can be expanded explicitly by calling a function. Wildcards can be very useful for creating more adaptable makefiles. For instance, instead of listing all the files in a program explicitly, you can use wildcards:[1]
prog: *.c $(CC) -o $@ $^
It is important to be careful with wildcards, however. It is easy to misuse them as the following example shows:
*.o: constants.h
The intent is clear: all object files depend on the header file constants.h, but consider how this expands on a clean directory without any object files:
: constants.h
This is a legal make
expression and will not produce an error by itself, but it will also not provide the dependency the user wants. The proper way to implement this rule is to perform a wildcard on the source files (since they are always present) and transform that into a list of object files. We will cover this technique when we discuss make
functions in Chapter 4.
When mp_make expands a wildcard (or indeed when mp_make looks for any file), it reads and caches the directory contents. This caching improves mp_make’s performance considerably. However, once mp_make has read and cached the directory contents, mp_make will not “see” any changes made to the directory. This can be a mysterious source of errors in a mp_makefile. The issue can sometimes be resolved by using a sub-shell and globbing (e.g., shell wildcards) rather than mp_make’s own wildcards, but occasionally, this is not possible and we must resort to bizarre hacks.
Finally, it is worth noting that wildcard expansion is performed by make
when the pattern appears as a target or prerequisite. However, when the pattern appears in a command, the expansion is performed by the subshell. This can occasionally be important because make
will expand the wildcards immediately upon reading the makefile, but the shell will expand the wildcards in commands much later when the command is executed. When a lot of complex file manipulation is being done, the two wildcard expansions can be quite different.
Phony Targets
Until now all targets and prerequisites have been files to be created or updated. This is typically the case, but it is often useful for a target to be just a label representing a command script. For instance, earlier we noted that a standard first target in many makefiles is called all
. Targets that do not represent files are known as phony targets. Another standard phony target is clean
:
clean: rm -f *.o lexer.c
Normally, phony targets will always be executed because the commands associated with the rule do not create the target name.
It is important to note that make
cannot distinguish between a file target and phony target. If by chance the name of a phony target exists as a file, make
will associate the file with the phony target name in its dependency graph. If, for example, the file clean happened to be created running make clean
would yield the confusing message:
$ make clean make: `clean' is up to date.
Since most phony targets do not have prerequisites, the clean
target would always be considered up to date and would never execute.
To avoid this problem, GNU make
includes a special target, .PHONY
, to tell make
that a target is not a real file. Any target can be declared phony by including it as a prerequisite of .PHONY
:
.PHONY: clean clean: rm -f *.o lexer.c
Now make
will always execute the commands associated with clean
even if a file named clean exists. In addition to marking a target as always out of date, specifying that a target is phony tells make
that this file does not follow the normal rules for making a target file from a source file. Therefore, make
can optimize its normal rule search to improve performance.
It rarely makes sense to use a phony target as a prerequisite of a real file since the phony is always out of date and will always cause the target file to be remade. However, it is often useful to give phony targets prerequisites. For instance, the all
target is usually given the list of programs to be built:
.PHONY: all all: bash bashbug
Here the all
target creates the bash
shell program and the bashbug
error reporting tool.
Phony targets can also be thought of as shell scripts embedded in a makefile. Making a phony target a prerequisite of another target will invoke the phony target script before making the actual target. Suppose we are tight on disk space and before executing a disk-intensive task we want to display available disk space. We could write:
.PHONY: make-documentation make-documentation: df -k . | awk 'NR = = 2 { printf( "%d available\n", $$4 ) }' javadoc ...
The problem here is that we may end up specifying the df
and awk
commands many times under different targets, which is a maintenance problem since we’ll have to change every instance if we encounter a df
on another system with a different format. Instead, we can place the df
line in its own phony target:
.PHONY: make-documentation make-documentation: df javadoc ... .PHONY: df df: df -k . | awk 'NR = = 2 { printf( "%d available\n", $$4 ) }'
We can cause make
to invoke our df
target before generating the documentation by making df
a prerequisite of make-documentation
. This works well because make-documentation
is also a phony target. Now I can easily reuse df
in other targets.
There are a number of other good uses for phony targets. The output of make
can be confusing to read and debug. There are several reasons for this: makefiles are written top-down but the commands are executed by make
bottom-up; also, there is no indication which rule is currently being evaluated. The output of make
can be made much easier to read if major targets are commented in the make
output. Phony targets are a useful way to accomplish this. Here is an example taken from the bash
makefile:
$(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP) $(RM) $@ $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS) ls -l $(Program) size $(Program) .PHONY: build_msg build_msg: @printf "#\n# Building $(Program)\n#\n"
Because the printf
is in a phony target, the message is printed immediately before any prerequisites are updated. If the build message were instead placed as the first command of the $(Program)
command script, then it would be executed after all compilation and dependency generation. It is important to note that because phony targets are always out of date, the phony build_msg
target causes $(Program)
to be regenerated even when it is not out of date. In this case, it seems a reasonable choice since most of the computation is performed while compiling the object files so only the final link will always be performed.
Phony targets can also be used to improve the “user interface” of a makefile. Often targets are complex strings containing directory path elements, additional filename components (such as version numbers) and standard suffixes. This can make specifying a target filename on the command line a challenge. The problem can be avoided by adding a simple phony target whose prerequisite is the actual target file.
By convention there are a set of more or less standard phony targets that many makefiles include. Table 2-1 lists these standard targets.
Target | Function |
| Perform all tasks to build the application |
| Create an installation of the application from the compiled binaries |
| Delete the binary files generated from sources |
| Delete all the generated files that were not in the original source distribution |
| Create a tags table for use by editors |
| Create GNU info files from their Texinfo sources |
| Run any tests associated with this application |
The target TAGS
is not really a phony since the output of the ctags
and etags
programs is a file named TAGS. It is included here because it is the only standard non-phony target we know of.
Empty Targets
Empty targets are similar to phony targets in that the target itself is used as a device to leverage the capabilities of make
. Phony targets are always out of date, so they always execute and they always cause their dependent (the target associated with the prerequisite) to be remade. But suppose we have some command, with no output file, that needs to be performed only occasionally and we don’t want our dependents updated? For this, we can make a rule whose target is an empty file (sometimes referred to as a cookie):
prog: size prog.o $(CC) $(LDFLAGS) -o $@ $^ size: prog.o size $^ touch size
Notice that the size
rule uses touch
to create an empty file named size after it completes. This empty file is used for its timestamp so that make
will execute the size
rule only when prog.o has been updated. Furthermore, the size
prerequisite to prog will not force an update of prog unless its object file is also newer.
Empty files are particularly useful when combined with the automatic variable $?
. We discuss automatic variables in the section Automatic Variables, but a preview of this variable won’t hurt. Within the command script part of a rule, make
defines the variable $?
to be the set of prerequisites that are newer than the target. Here is a rule to print all the files that have changed since the last time make print
was executed:
print: *.[hc] lpr $? touch $@
Generally, empty files can be used to mark the last time a particular event has taken place.
Variables
Let’s look at some of the variables we have been using in our examples. The simplest ones have the syntax:
$(variable-name)
This indicates that we want to expand the variable whose name is variable-name
. Variables can contain almost any text, and variable names can contain most characters including punctuation. The variable containing the C compile command is COMPILE.c
, for example. In general, a variable name must be surrounded by $( )
or ${ } t
o be recognized by make
. As a special case, a single character variable name does not require the parentheses.
A makefile will typically define many variables, but there are also many special variables defined automatically by make
. Some can be set by the user to control make
’s behavior while others are set by make
to communicate with the user’s makefile.
Automatic Variables
Automatic variables are set by make
after a rule is matched. They provide access to elements from the target and prerequisite lists so you don’t have to explicitly specify any filenames. They are very useful for avoiding code duplication, but are critical when defining more general pattern rules (discussed later).
There are seven “core” automatic variables:
$@
The filename representing the target.
$%
The filename element of an archive member specification.
$<
The filename of the first prerequisite.
$?
The names of all prerequisites that are newer than the target, separated by spaces.
$^
The filenames of all the prerequisites, separated by spaces. This list has duplicate filenames removed since for most uses, such as compiling, copying, etc., duplicates are not wanted.
$+
Similar to
$^
, this is the names of all the prerequisites separated by spaces, except that$+
includes duplicates. This variable was created for specific situations such as arguments to linkers where duplicate values have meaning.$*
The stem of the target filename. A stem is typically a filename without its suffix. (We’ll discuss how stems are computed later in the section Pattern Rules.) Its use outside of pattern rules is discouraged.
In addition, each of the above variables has two variants for compatibility with other make
s. One variant returns only the directory portion of the value. This is indicated by appending a “D” to the symbol, $(@D)
, $(<D)
, etc. The other variant returns only the file portion of the value. This is indicated by appending an F to the symbol, $(@F)
, $(<F)
, etc. Note that these variant names are more than one character long and so must be enclosed in parentheses. GNU make
provides a more readable alternative with the dir
and notdir
functions. We will discuss functions in Chapter 4.
Automatic variables are set by make
after a rule has been matched with its target and prerequisites so the variables are only available in the command script of a rule.
Here is our makefile with explicit filenames replaced by the appropriate automatic variable.
count_words: count_words.o counter.o lexer.o -lfl gcc $^ -o $@ count_words.o: count_words.c gcc -c $< counter.o: counter.c gcc -c $< lexer.o: lexer.c gcc -c $< lexer.c: lexer.l flex -t $< > $@
Finding Files with VPATH and vpath
Our examples so far have been simple enough that the makefile and sources all lived in a single directory. Real world programs are more complex (when’s the last time you worked on a single directory project?). Let’s refactor our example and create a more realistic file layout. We can modify our word counting program by refactoring main
into a function called counter
.
#include <lexer.h> #include <counter.h> void counter( int counts[4] ) { while ( yylex( ) ) ; counts[0] = fee_count; counts[1] = fie_count; counts[2] = foe_count; counts[3] = fum_count; }
A reusable library function should have a declaration in a header file, so let’s create counter.h containing our declaration:
#ifndef COUNTER_H_ #define COUNTER_H_ extern void counter( int counts[4] ); #endif
We can also place the declarations for our lexer.l symbols in lexer.h:
#ifndef LEXER_H_ #define LEXER_H_ extern int fee_count, fie_count, foe_count, fum_count; extern int yylex( void ); #endif
In a traditional source tree layout the header files are placed in an include directory and the source is placed in a src directory. We’ll do this and put our makefile in the parent directory. Our example program now has the layout shown in Figure 2-1.
Since our source files now include header files, these new dependencies should be recorded in our makefile so that when a header file is modified, the corresponding object file will be updated.
count_words: count_words.o counter.o lexer.o -lfl gcc $^ -o $@ count_words.o: count_words.c include/counter.h gcc -c $< counter.o: counter.c include/counter.h include/lexer.h gcc -c $< lexer.o: lexer.c include/lexer.h gcc -c $< lexer.c: lexer.l flex -t $< > $@
Now when we run our makefile, we get:
$ make
make: *** No rule to make target `count_words.c', needed by `count_words.o'. Stop.
Oops, what happened? The makefile is trying to update count_words.c, but that’s a source file! Let’s “play make
.” Our first prerequisite is count_words.o. We see the file is missing and look for a rule to create it. The explicit rule for creating count_words.o references count_words.c. But why can’t make
find the source file? Because the source file is in the src directory not the current directory. Unless you direct it otherwise, make
will look only in the current directory for its targets and prerequisites. How do we get make
to look in the src directory for source files? Or, more generally, how do we tell make
where our source code is?
You can tell make
to look in different directories for its source files using the VPATH
and vpath
features. To fix our immediate problem, we can add a VPATH
assignment to the makefile:
VPATH = src
This indicates that make
should look in the directory src if the files it wants are not in the current directory. Now when we run our makefile, we get:
$ make
gcc -c src/count_words.c -o count_words.o
src/count_words.c:2:21: counter.h: No such file or directory
make: *** [count_words.o] Error 1
Notice that make
now successfully tries to compile the first file, filling in correctly the relative path to the source. This is another reason to use automatic variables: make
cannot use the appropriate path to the source if you hardcode the filename. Unfortunately, the compilation dies because gcc
can’t find the include file. We can fix this latest problem by “customizing” the implicit compilation rule with the appropriate -I
option:
CPPFLAGS = -I include
and changing occurrences of gcc
to gcc$(CPPFLAGS)
. Now the build succeeds:
$ make
gcc -I include -c src/count_words.c -o count_words.o
gcc -I include -c src/counter.c -o counter.o
flex -t src/lexer.l > lexer.c
gcc -I include -c lexer.c -o lexer.o
gcc count_words.o counter.o lexer.o /lib/libfl.a -o count_words
The VPATH
variable consists of a list of directories to search when make
needs a file. The list will be searched for targets as well as prerequisites, but not for files mentioned in command scripts. The list of directories can be separated by spaces or colons on Unix and separated by spaces or semicolons on Windows. I prefer to use spaces since that works on all systems and we can avoid the whole colon/semicolon imbroglio. Also, the directories are easier to read when separated by spaces.
The VPATH
variable is good because it solved our searching problem above, but it is a rather large hammer. make
will search each directory for any file it needs. If a file of the same name exists in multiple places in the VPATH
list, make
grabs the first one. Sometimes this can be a problem.
The vpath
directive is a more precise way to achieve our goals. The syntax of this directive is:
vpath pattern directory-list
So our previous VPATH
use can be rewritten as:
vpath %.c src vpath %.l src vpath %.h include
Now we’ve told make
that it should search for .c and .l files in the src directory and we’ve also added a line to search for .h files in the include directory (so we can remove the include/ from our header file prerequisites). In more complex applications, this control can save a lot of headache and debugging time.
Here we used vpath
to handle the problem of finding source that is distributed among several directories. There is a related but different problem of how to build an application so that the object files are written into a “binary tree” while the source files live in a separate “source tree.” Proper use of vpath
can also help to solve this new problem, but the task quickly becomes complex and vpath
alone is not sufficient. We’ll discuss this problem in detail in later sections.
Pattern Rules
The makefile examples we’ve been looking at are a bit verbose. For a small program of a dozen files or less we may not care, but for programs with hundreds or thousands of files, specifying each target, prerequisite, and command script becomes unworkable. Furthermore, the commands themselves represent duplicate code in our makefile. If the commands contain a bug or ever change, we would have to update all these rules. This can be a major maintenance problem and source of bugs.
Many programs that read one file type and output another conform to standard conventions. For instance, all C compilers assume that files that have a .c suffix contain C source code and that the object filename can be derived by replacing the .c suffix with .o (or .obj for some Windows compilers). In the previous chapter, we noticed that flex
input files use the .l suffix and that flex
generates .c files.
These conventions allow make
to simplify rule creation by recognizing common filename patterns and providing built-in rules for processing them. For example, by using these built-in rules our 17-line makefile can be reduced to:
VPATH = src include CPPFLAGS = -I include count_words: counter.o lexer.o -lfl count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h
The built-in rules are all instances of pattern rules. A pattern rule looks like the normal rules you have already seen except the stem of the file (the portion before the suffix) is represented by a %
character. This makefile works because of three built-in rules. The first specifies how to compile a .o file from a .c file:
%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $<
The second specifies how to make a .c file from a .l file:
%.c: %.l @$(RM) $@ $(LEX.l) $< > $@
Finally, there is a special rule to generate a file with no suffix (always an executable) from a .c file:
%: %.o $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
We’ll go into the details of this syntax in a bit, but first let’s go over make
’s output carefully and see how make
applies these built-in rules.
When we run make
on our seven-line makefile, the output is:
$ make
gcc -I include -c -o count_words.o src/count_words.c
gcc -I include -c -o counter.o src/counter.c
flex -t src/lexer.l > lexer.c
gcc -I include -c -o lexer.o lexer.c
gcc count_words.o counter.o lexer.o /lib/libfl.a -o count_words
rm lexer.c
First, make
reads the makefile and sets the default goal to count_words since there are no command-line targets specified. Looking at the default goal, make
identifies four prerequisites: count_words.o (this prerequisite is missing from the makefile, but is provided by the implicit rule), counter.o, lexer.o, and -lfl
. It then tries to update each prerequisite in turn.
When make
examines the first prerequisite, count_words.o, make
finds no explicit rule for it but discovers the implicit rule. Looking in the local directory, make
cannot find the source, so it begins searching the VPATH
and finds a matching source file in src. Since src/count_words.c has no prerequisites, make
is free to update count_words.o so it runs the commands for the implicit rule. counter.o is similar. When make
considers lexer.o, it cannot find a corresponding source file (even in src) so it assumes this (nonexistent source) is an intermediate file and looks for a way to make lexer.c from some other source file. It discovers a rule to create a .c file from a .l file and notices that lexer.l exists. There is no action required to update lexer.l, so it moves on to the command for updating lexer.c, which yields the flex
command line. Next make
updates the object file from the C source. Using sequences of rules like this to update a target is called rule chaining.
Next, make
examines the library specification -lfl
. It searches the standard library directories for the system and discovers /lib/libfl.a.
Now make
has all the prerequisites for updating count_words, so it executes the final gcc
command. Lastly, make
realizes it created an intermediate file that is not necessary to keep so it cleans it up.
As you can see, using rules in makefiles allows you to omit a lot of detail. Rules can have complex interactions that yield very powerful behaviors. In particular, having a built-in database of common rules makes many types of makefile specifications very simple.
The built-in rules can be customized by changing the values of the variables in the command scripts. A typical rule has a host of variables, beginning with the program to execute, and including variables to set major groupings of command-line options, such as the output file, optimization, debugging, etc. You can look at make
’s default set of rules (and variables) by running make --print-data-base
.
The Patterns
The percent character in a pattern rule is roughly equivalent to *
in a Unix shell. It represents any number of any characters. The percent character can be placed anywhere within the pattern but can occur only once. Here are some valid uses of percent:
%,v s%.o wrapper_%
Characters other than percent match literally within a filename. A pattern can contain a prefix or a suffix or both. When make
searches for a pattern rule to apply, it first looks for a matching pattern rule target. The pattern rule target must start with the prefix and end with the suffix (if they exist). If a match is found, the characters between the prefix and suffix are taken as the stem of the name. Next make
looks at the prerequisites of the pattern rule by substituting the stem into the prerequisite pattern. If the resulting filename exists or can be made by applying another rule, a match is made and the rule is applied. The stem word must contain at least one character.
It is also possible to have a pattern containing only a percent character. The most common use of this pattern is to build a Unix executable program. For instance, here are several pattern rules GNU make
includes for building programs:
%: %.mod $(COMPILE.mod) -o $@ -e $@ $^ %: %.cpp $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@ %: %.sh cat $< >$@ chmod a+x $@
These patterns will be used to generate an executable from a Modula source file, a preprocessed C source file, and a Bourne shell script, respectively. We will see many more implicit rules in the section “The Implicit Rules Database.”
Static Pattern Rules
A static pattern rule is one that applies only to a specific list of targets.
$(OBJECTS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
The only difference between this rule and an ordinary pattern rule is the initial $(OBJECTS):
specification. This limits the rule to the files listed in the $(OBJECTS)
variable.
This is very similar to a pattern rule. Each object file in $(OBJECTS)
is matched against the pattern %.o
and its stem is extracted. The stem is then substituted into the pattern %.c
to yield the target’s prerequisite. If the target pattern does not exist, make
issues a warning.
Use static pattern rules whenever it is easier to list the target files explicitly than to identify them by a suffix or other pattern.
Suffix Rules
Suffix rules are the original (and obsolete) way of defining implicit rules. Because other versions of make
may not support GNU make
’s pattern rule syntax, you will still see suffix rules in makefiles intended for a wide distribution so it is important to be able to read and understand the syntax. So, although compiling GNU make
for the target system is the preferred method for makefile portability, you may still need to write suffix rules in rare circumstances.
Suffix rules consist of one or two suffixes concatenated and used as a target:
.c.o: $(COMPILE.c) $(OUTPUT_OPTION) $<
This is a little confusing because the prerequisite suffix comes first and the target suffix second. This rule matches the same set of targets and prerequisites as:
%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $<
The suffix rule forms the stem of the file by removing the target suffix. It forms the prerequisite by replacing the target suffix with the prerequisite suffix. The suffix rule is recognized by make
only if the two suffixes are in a list of known suffixes.
The above suffix rule is known as a double-suffix rule since it contains two suffixes. There are also single-suffix rules. As you might imagine a single-suffix rule contains only one suffix, the suffix of the source file. These rules are used to create executables since Unix executables do not have a suffix:
.p: $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
This rule produces an executable image from a Pascal source file. This is completely analogous to the pattern rule:
%: %.p $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
The known suffix list is the strangest part of the syntax. A special target, .SUFFIXES
, is used to set the list of known suffixes. Here is the first part of the default .SUFFIXES
definition:
.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l
You can add your own suffixes by simply adding a .SUFFIXES
rule to your makefile:
.SUFFIXES: .pdf .fo .html .xml
If you want to delete all the known suffixes (because they are interfering with your special suffixes) simply specify no prerequisites:
.SUFFIXES:
You can also use the command-line option --no-builtin-rules
(or -r
).
We will not use this old syntax in the rest of this book because GNU make
’s pattern rules are clearer and more general.
The Implicit Rules Database
GNU make
3.80 has about 90 built-in implicit rules. An implicit rule is either a pattern rule or a suffix rule. There are built-in pattern rules for C, C++, Pascal, FORTRAN, ratfor, Modula, Texinfo, TEX (including Tangle and Weave), Emacs Lisp, RCS, and SCCS. In addition, there are rules for supporting programs for these languages, such as cpp
, as
, yacc
, lex
, tangle
, weave
and dvi tools.
If you are using any of these tools, you’ll probably find most of what you need in the built-in rules. If you’re using some unsupported languages such as Java or XML, you will have to write rules of your own. But don’t worry, you typically need only a few rules to support a language and they are quite easy to write.
To examine the rules database built into make
, use the --print-data-base
command-line option (-p
for short). This will generate about a thousand lines of output. After version and copyright information, make
prints its variable definitions each one preceded by a comment indicating the “origin” of the definition. For instance, variables can be environment variables, default values, automatic variables, etc. After the variables, come the rules. The actual format used by GNU make
is:
%: %.C # commands to execute (built-in): $(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@
For rules defined by the makefile, the comment will include the file and line where the rule was defined:
%.html: %.xml # commands to execute (from `Makefile', line 168): $(XMLTO) $(XMLTO_FLAGS) html-nochunks $<
Working with Implicit Rules
The built-in implicit rules are applied whenever a target is being considered and there is no explicit rule to update it. So using an implicit rule is easy: simply do not specify a command script when adding your target to the makefile. This causes make
to search its built-in database to satisfy the target. Usually this does just what you want, but in rare cases your development environment can cause problems. For instance, suppose you have a mixed language environment consisting of Lisp and C source code. If the file editor.l and editor.c both exist in the same directory (say one is a low-level implementation accessed by the other) make
will believe that the Lisp file is really a flex
file (recall flex
files use the .l suffix) and that the C source is the output of the flex
command. If editor.o is a target and editor.l is newer than editor.c, make
will attempt to “update” the C file with the output of flex
overwriting your source code. Gack.
To work around this particular problem you can delete the two rules concerning flex
from the built-in rule base like this:
%.o: %.l %.c: %.l
A pattern with no command script will remove the rule from make
’s database. In practice, situations such as this are very rare. However, it is important to remember the built-in rules database contains rules that will interact with your own makefiles in ways you may not have anticipated.
We have seen several examples of how make
will “chain” rules together while trying to update a target. This can lead to some complexity, which we’ll examine here. When make
considers how to update a target, it searches the implicit rules for a target pattern that matches the target in hand. For each target pattern that matches the target file, make
will look for an existing matching prerequisite. That is, after matching the target pattern, make
immediately looks for the prerequisite “source” file. If the prerequisite is found, the rule is used. For some target patterns, there are many possible source files. For instance, a .o file can be made from .c, .cc, .cpp, .p, .f, .r, .s, and .mod files. But what if the source is not found after searching all possible rules? In this case, make
will search the rules again, this time assuming that the matching source file should be considered as a new target for updating. By performing this search recursively, make
can find a “chain” of rules that allows updating a target. We saw this in our lexer.o example. make
was able to update the lexer.o target from lexer.l even though the intermediate .c file was missing by invoking the .l to .c rule, then the .c to .o rule.
One of the more impressive sequences that make
can produce automatically from its database is shown here. First, we setup our experiment by creating an empty yacc
source file and registering with RCS using ci
(that is, we want a version-controlled yacc
source file):
$ touch foo.y $ ci foo.y foo.y,v <-- foo.y . initial revision: 1.1 done
Now, we ask make
how it would create the executable foo. The --just-print
(or -n
) option tells make
to report what actions it would perform without actually running them. Notice that we have no makefile and no “source” code, only an RCS file:
$ make -n foo
co foo.y,v foo.y
foo.y,v --> foo.y
revision 1.1
done
bison -y foo.y
mv -f y.tab.c foo.c
gcc -c -o foo.o foo.c
gcc foo.o -o foo
rm foo.c foo.o foo.y
Following the chain of implicit rules and prerequisites, make
determined it could create the executable, foo, if it had the object file foo.o. It could create foo.o if it had the C source file foo.c. It could create foo.c if it had the yacc source file foo.y. Finally, it realized it could create foo.y by checking out the file from the RCS file foo.y,v, which it actually has. Once make
has formulated this plan, it executes it by checking out foo. y with co
, transforming it into foo.c with bison
, compiling it into foo.o with gcc
, and linking it to form foo again with gcc
. All this from the implicit rules database. Pretty cool.
The files generated by chaining rules are called intermediate files and are treated specially by make
. First, since intermediate files do not occur in targets (otherwise they would not be intermediate), make
will never simply update an intermediate file. Second, because make
creates intermediate files itself as a side effect of updating a target, make
will delete the intermediates before exiting. You can see this in the last line of the example.
The implicit rules database contains a lot of rules and even very large projects rarely use most of it. Because it contains such a wide variety, rules you didn’t expect to fire may be used by make in unexpected ways. As a preventative measure, some large projects choose to discard the implicit rules entirely in favor of their own handcrafted rules. You can do this easily with the --no-builtin-rules
(or -r
) option. (I have never had to use this option even on the largest projects.) If you use this option, you may also want to consider using --no-builtin-variables
(or -R
).
Rule Structure
The built-in rules have a standard structure intended to make them easily customizable. Let’s go over the structure briefly then talk about customization. Here is the (by now familiar) rule for updating an object file from its C source:
%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $<
The customization of this rule is controlled entirely by the set of variables it uses. We see two variables here, but COMPILE.c
in particular is defined in terms of several other variables:
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c CC = gcc OUTPUT_OPTION = -o $@
The C compiler itself can be changed by altering the value of the CC
variable. The other variables are used for setting compilation options (CFLAGS
), preprocessor options (CPPFLAGS
), and architecture-specific options (TARGET_ARCH
).
The variables in a built-in rule are intended to make customizing the rule as easy as possible. For that reason, it is important to be very careful when setting these variables in your makefile. If you set these variables in a naive way, you destroy the end user’s ability to customize them. For instance, given this assignment in a makefile:
CPPFLAGS = -I project/include
If the user wanted to add a CPP define to the command line, they would normally invoke make
like:
$ make CPPFLAGS=-DDEBUG
But in so doing they would accidentally remove the -I
option that is (presumably) required for compiling. Variables set on the command line override all other assignments to the variable. (See the section Where Variables Come From in Chapter 3 for more details on command-line assignments). So, setting CPPFLAGS
inappropriately in the makefile “broke” a customization feature that most users would expect to work. Instead of using simple assignment, consider redefining the compilation variable to include your own variables:
COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) -c INCLUDES = -I project/include
Or you can use append-style assignment, which is discussed in the section Other Types of Assignment in Chapter 3.
Implicit Rules for Source Control
make
knows about two source code control systems, RCS and SCCS, and supports their use with built-in implicit rules. Unfortunately, it seems the state of the art in source code control and modern software engineering have left make
behind. I’ve never found a use for the source control support in make
, nor have I seen it used in other production software. I do not recommend the use of this feature. There are a number of reasons for this.
First, the source control tools supported by make
, RCS and SCCS, although valuable and venerable tools in their day, have largely been supplanted by CVS, the Concurrent Version System, or proprietary tools. In fact, CVS uses RCS to manage individual files internally. However, using RCS directly proved to be a considerable problem when a project spanned more than one directory or more than one developer. CVS, in particular, was implemented to fill the gaps in RCS’s functionality in precisely these areas. Support for CVS has never been added to make
, which is probably a good thing.[2]
It is now well recognized that the life cycle of software becomes complex. Applications rarely move smoothly from one release to the next. More typically, one or more distinct releases of an application are being used in the field (and require bug fix support), while one or more versions are in active development. CVS provides powerful features to help manage these parallel versions of the software. But it also means that a developer must be very aware of the specific version of the code she is working on. Having the makefile automatically check out source during a compilation begs the question of what source is being checked out and whether the newly checked out source is compatible with the source already existing in the developer’s working directories. In many production environments, developers are working on three or more distinct versions of the same application in a single day. Keeping this complexity in check is hard enough without having software quietly updating your source tree for you.
Also, one of the more powerful features of CVS is that it allows access to remote repositories. In most production environments, the CVS repository (the database of controlled files) is not located on the developer’s own machine, but on a server. Although network access is now quite fast (particularly on a local area network) it is not a good idea to have make
probing the network server in search of source files. The performance impact would be disastrous.
So, although it is possible to use the built-in implicit rules to interface more or less cleanly with RCS and SCCS, there are no rules to access CVS for gathering source files or makefile. Nor do I think it makes much sense to do so. On the other hand, it is quite reasonable to use CVS in makefiles. For instance, to ensure that the current source is properly checked in, that the release number information is managed properly, or that test results are correct. These are uses of CVS by makefile authors rather than issues of CVS integration with make
.
A Simple Help Command
Large makefiles can have many targets that are difficult for users to remember. One way to reduce this problem is to make the default target a brief help command. However, maintaining the help text by hand is always a problem. To avoid this, you can gather the available commands directly from make
’s rules database. The following target will present a sorted four column listing of the available make
targets:
# help - The default goal .PHONY: help help: @$(MAKE) --print-data-base --question no-such-target | \ $(GREP) -v -e '^no-such-target' -e '^makefile' | \ $(AWK) '/^[^.%][-A-Za-z0-9_]*:/ \ { print substr($$1, 1, length($$1)-1) }' | \ $(SORT) | \ $(PR) --omit-pagination --width=80 --columns=4 \
The command script consists of a single pipeline. The make
rule database is dumped using the --print-data-base
command. Specifying a target of no-such-target (instead of the default target) ensures that running the makefile recursively does not trigger infinite recursion. The grep
command filters out the bogus target and the makefile itself (which appears as a rule in the database dump). Using the --question
option prevents make
from running any actual commands. The database is then passed through a simple awk
filter that grabs every line representing a target that does not begin with percent or period (pattern rules and suffix rules, respectively) and discards extra information on the line. Finally, the target list is sorted and printed in a simple four-column listing.
Another approach to the same command (my first attempt), used the awk
command on the makefile itself. This required special handling for included makefiles (covered in the section The include Directive in Chapter 3) and could not handle generated rules at all. The version presented here handles all that automatically by allowing make
to process these elements and report the resulting rule set.
Special Targets
A special target is a built-in phony target used to change make
’s default behavior. For instance, .PHONY
, a special target, which we’ve already seen, declares that its prerequisite does not refer to an actual file and should always be considered out of date. The .PHONY
target is the most common special target you will see, but there are others as well.
These special targets follow the syntax of normal targets, that is target
: prerequisite
, but the target
is not a file or even a normal phony. They are really more like directives for modifying make
’s internal algorithms.
There are twelve special targets. They fall into three categories: as we’ve just said many are used to alter the behavior of make
when updating a target, another set act simply as global flags to make
and ignore their targets, finally the .SUFFIXES
special target is used when specifying old-fashioned suffix rules (discussed in the section Suffix Rules earlier in this chapter).
The most useful target modifiers (aside from .PHONY
) are:
.INTERMEDIATE
Prerequisites of this special target are treated as intermediate files. If
make
creates the file while updating another target, the file will be deleted automatically whenmake
exits. If the file already exists whenmake
considers updating the file, the file will not be deleted.This can be very useful when building custom rule chains. For instance, most Java tools accept Windows-like file lists. Creating rules to build the file lists and marking their output files as intermediate allows
make
to clean up many temporary files..SECONDARY
Prerequisites of this special target are treated as intermediate files but are never automatically deleted. The most common use of
.SECONDARY
is to mark object files stored in libraries. Normally these object files will be deleted as soon as they are added to an archive. Sometimes it is more convenient during development to keep these object files, but still use themake
support for updating archives..PRECIOUS
When
make
is interrupted during execution, it may delete the target file it is updating if the file was modified sincemake
started. This is somake
doesn’t leave a partially constructed (possibly corrupt) file laying around in the build tree. There are times when you don’t want this behavior, particularly if the file is large and computationally expensive to create. If you mark the file as precious,make
will never delete the file if interrupted.Use of
.PRECIOUS
is relatively rare, but when it is needed it is often a life saver. Note thatmake
will not perform an automatic delete if the commands of a rule generate an error. It does so only when interrupted by a signal..DELETE_ON_ERROR
This is sort of the opposite of
.PRECIOUS
. Marking a target as.DELETE_ON_ERROR
says thatmake
should delete the target if any of the commands associated with the rule generates an error.make
normally only deletes the target if it is interrupted by a signal.
The other special targets will be covered later when their use is more relevant. We’ll discuss .EXPORT_ALL_VARIABLES
in Chapter 3 and the targets relating to parallel execution in Chapter 10.
Automatic Dependency Generation
When we refactored our word counting program to use header files, a thorny little problem snuck up on us. We added the dependency between the object files and the C header files to the makefile by hand. It was easy to do in this case, but in normal programs (not toy examples) this can be tedious and error-prone. In fact, in most programs it is virtually impossible because most header files include other header files forming a complex tree. For instance, on my system, the single header file stdio.h (the most commonly referenced header file in C) expands to include 15 other header files. Resolving these relationships by hand is a hopeless task. But failing to recompile files can lead to hours of debugging headaches or worse, bugs in the resulting program. So what do we do?
Well, computers are pretty good at searching and pattern matching. Let’s use a program to identify the relationships between files and maybe even have this program write out these dependencies in makefile syntax. As you have probably guessed, such a program already exists—at least for C/C++. There is a option to gcc
and many other C/C++ compilers that will read the source and write makefile dependencies. For instance, here is how I found the dependencies for stdio.h:
$ echo "#include <stdio.h>" > stdio.c $ gcc -M stdio.c stdio.o: stdio.c /usr/include/stdio.h /usr/include/_ansi.h \ /usr/include/newlib.h /usr/include/sys/config.h \ /usr/include/machine/ieeefp.h /usr/include/cygwin/config.h \ /usr/lib/gcc-lib/i686-pc-cygwin/3.2/include/stddef.h \ /usr/lib/gcc-lib/i686-pc-cygwin/3.2/include/stdarg.h \ /usr/include/sys/reent.h /usr/include/sys/_types.h \ /usr/include/sys/types.h /usr/include/machine/types.h \ /usr/include/sys/features.h /usr/include/cygwin/types.h \ /usr/include/sys/sysmacros.h /usr/include/stdint.h \ /usr/include/sys/stdio.h
“Fine.” I hear you cry, “Now I need to run gcc
and use an editor to paste the results of -M
into my makefiles. What a pain.” And you’d be right if this was the whole answer. There are two traditional methods for including automatically generated dependencies into makefiles. The first and oldest is to add a line such as:
# Automatically generated dependencies follow - Do Not Edit
to the end of the makefile and then write a shell script to update these generated lines. This is certainly better than updating them by hand, but it’s also very ugly. The second method is to add an include
directive to the make
program. By now most versions of make
have the include
directive and GNU make
most certainly does.
So, the trick is to write a makefile target whose action runs gcc
over all your source with the -M
option, saves the results in a dependency file, and then re-runs make
including the generated dependency file in the makefile so it can trigger the updates we need. Before GNU make
, this is exactly what was done and the rule looked like:
depend: count_words.c lexer.c counter.c $(CC) -M $(CPPFLAGS) $^ > $@ include depend
Before running make
to build the program, you would first execute make depend
to generate the dependencies. This was good as far as it went, but often people would add or remove dependencies from their source without regenerating the depend file. Then source wouldn’t get recompiled and the whole mess started again.
GNU make
solved this last niggling problem with a cool feature and a simple algorithm. First, the algorithm. If we generated each source file’s dependencies into its own dependency file with, say, a .d suffix and added the .d file itself as a target to this dependency rule, then make
could know that the .d needed to be updated (along with the object file) when the source file changed:
counter.o counter.d: src/counter.c include/counter.h include/lexer.h
Generating this rule can be accomplished with a pattern rule and a (fairly ugly) command script (this is taken directly from the GNU make
manual):[3]
%.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
Now, for the cool feature. make
will treat any file named in an include
directive as a target to be updated. So, when we mention the .d files we want to include, make
will automatically try to create these files as it reads the makefile. Here is our makefile with the addition of automatic dependency generation:
VPATH = src include CPPFLAGS = -I include SOURCES = count_words.c \ lexer.c \ counter.c count_words: counter.o lexer.o -lfl count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h include $(subst .c,.d,$(SOURCES)) %.d: %.c $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
The include
directive should always be placed after the hand-written dependencies so that the default goal is not hijacked by some dependency file. The include
directive takes a list of files (whose names can include wildcards). Here we use a make
function, subst
, to transform the list of source files into a list of dependency filenames. (We’ll discuss subst
in detail in the section String Functions in Chapter 4.) For now, just note that this use replaces the string .c with .d in each of the words in $(SOURCES)
.
When we run this makefile with the --just-print
option, we get:
$ make --just-print
Makefile:13: count_words.d: No such file or directory
Makefile:13: lexer.d: No such file or directory
Makefile:13: counter.d: No such file or directory
gcc -M -I include src/counter.c > counter.d.$$; \
sed 's,\(counter\)\.o[ :]*,\1.o counter.d : ,g' < counter.d.$$ >
counter.d; \
rm -f counter.d.$$
flex -t src/lexer.l > lexer.c
gcc -M -I include lexer.c > lexer.d.$$; \
sed 's,\(lexer\)\.o[ :]*,\1.o lexer.d : ,g' < lexer.d.$$ > lexer.d;
\
rm -f lexer.d.$$
gcc -M -I include src/count_words.c > count_words.d.$$;
\
sed 's,\(count_words\)\.o[ :]*,\1.o count_words.d : ,g' < count_words.d.
$$
count_words.d; \
rm -f count_words.d.$$
rm lexer.c
gcc -I include -c -o count_words.o src/count_words.c
gcc -I include -c -o counter.o src/counter.c
gcc -I include -c -o lexer.o lexer.c
gcc count_words.o counter.o lexer.o /lib/libfl.a -o count_words
At first the response by make
is a little alarming—it looks like a make
error message. But not to worry, this is just a warning. make
looks for the include files and doesn’t find them, so it issues the No such file or directory
warning before searching for a rule to create these files. This warning can be suppressed by preceding the include
directive with a hyphen (-
). The lines following the warnings show make
invoking gcc
with the -M
option, then running the sed
command. Notice that make
must invoke flex
to create lexer.c, then it deletes the temporary lexer.c before beginning to satisfy the default goal.
This gives you a taste of automatic dependency generation. There’s lots more to say, such as how do you generate dependencies for other languages or build tree layouts. We’ll return to this topic in more depth in Part II of this book.
Managing Libraries
An archive library, usually called simply a library or archive, is a special type of file containing other files called members. Archives are used to group related object files into more manageable units. For example, the C standard library libc.a contains low-level C functions. Libraries are very common so make
has special support for creating, maintaining, and referencing them. Archives are created and modified with the ar
program.
Let’s look at an example. We can modify our word counting program by refactoring the reusable parts into a reusable library. Our library will consist of the two files counter.o and lexer.o. The ar
command to create this library is:
$ ar rv libcounter.a counter.o lexer.o
a - counter.o
a - lexer.o
The options rv
indicate that we want to replace members of the archive with the object files listed and that ar
should verbosely echo its actions. We can use the replace option even though the archive doesn’t exist. The first argument after the options is the archive name followed by a list of object files. (Some versions of ar
also require the “c” option, for create, if the archive does not exist but GNU ar
does not.) The two lines following the ar
command are its verbose output indicating the object files were added.
Using the replace option to ar
allows us to create or update an archive incrementally:
$ ar rv libcounter.a counter.o r - counter.o $ ar rv libcounter.a lexer.o r - lexer.o
Here ar
echoed each action with “r” to indicate the file was replaced in the archive.
A library can be linked into an executable in several ways. The most straightforward way is to simply list the library file on the command line. The compiler or linker will use the file suffix to determine the type of a particular file on the command line and do the Right Thing™:
cc count_words.o libcounter.a /lib/libfl.a -o count_words
Here cc
will recognize the two files libcounter.a and /lib/libfl.a as libraries and search them for undefined symbols. The other way to reference libraries on the command line is with the -l
option:
cc count_words.o -lcounter -lfl -o count_words
As you can see, with this option you omit the prefix and suffix of the library filename. The -l
option makes the command line more compact and easier to read, but it has a far more useful function. When cc
sees the -l
option it searches for the library in the system’s standard library directories. This relieves the programmer from having to know the precise location of a library and makes the command line more portable. Also, on systems that support shared libraries (libraries with the extension .so on Unix systems), the linker will search for a shared library first, before searching for an archive library. This allows programs to benefit from shared libraries without specifically requiring them. This is the default behavior of GNU’s linker/compiler. Older linker/compilers may not perform this optimization.
The search path used by the compiler can be changed by adding -L
options indicating the directories to search and in what order. These directories are added before the system libraries and are used for all -l
options on the command line. In fact, the last example fails to link because the current directory is not in cc
’s library search path. We can fix this error by adding the current directory like this:
cc count_words.o -L. -lcounter -lfl -o count_words
Libraries add a bit of complication to the process of building a program. How can make
help to simplify the situation? GNU make
includes special features to support both the creation of libraries and their use in linking programs. Let’s see how they work.
Creating and Updating Libraries
Within a makefile, a library file is specified with its name just like any other file. A simple rule to create our library is:
libcounter.a: counter.o lexer.o $(AR) $(ARFLAGS) $@ $^
This uses the built-in definition for the ar
program in AR
and the standard options rv
in ARFLAGS
. The archive output file is automatically set in $@
and the prerequisites are set in $^
.
Now, if you make libcounter.a a prerequisite of count_words make
will update our library before linking the executable. Notice one small irritation, however. All members of the archive are replaced even if they have not been modified. This is a waste of time and we can do better:
libcounter.a: counter.o lexer.o $(AR) $(ARFLAGS) $@ $?
If you use $?
instead of $^
, make
will pass only those objects files that are newer than the target to ar
.
Can we do better still? Maybe, but maybe not. make
has support for updating individual files within an archive, executing one ar
command for each object file member, but before we go delving into those details there are several points worth noting about this style of building libraries. One of the primary goals of make
is to use the processor efficiently by updating only those files that are out of date. Unfortunately, the style of invoking ar
once for each out-of-date member quickly bogs down. If the archive contains more than a few dozen files, the expense of invoking ar
for each update begins to outweigh the “elegance” factor of using the syntax we are about to introduce. By using the simple method above and invoking ar
in an explicit rule, we can execute ar
once for all files and save ourselves many fork/exec calls. In addition, on many systems using the r
to ar
is very inefficient. On my 1.9 GHz Pentium 4, building a large archive from scratch (with 14,216 members totaling 55 MB) takes 4 minutes 24 seconds. However, updating a single object file with ar r
on the resulting archive takes 28 seconds. So building the archive from scratch is faster if I need to replace more than 10 files (out of 14,216!). In such situations it is probably more prudent to perform a single update of the archive with all modified object files using the $?
automatic variable. For smaller libraries and faster processors there is no performance reason to prefer the simple approach above to the more elegant one below. In those situations, using the special library support that follows is a fine approach.
In GNU make
, a member of an archive can be referenced using the notation:
libgraphics.a(bitblt.o): bitblt.o $(AR) $(ARFLAGS) $@ $<
Here the library name is libgraphics.a and the member name is bitblt.o (for bit block transfer). The syntax libname
.a(
module
.o)
refers to the module contained within the library. The prerequisite for this target is simply the object file itself and the command adds the object file to the archive. The automatic variable $<
is used in the command to get only the first prerequisite. In fact, there is a built-in pattern rule that does exactly this.
When we put this all together, our makefile looks like this:
VPATH = src include CPPFLAGS = -I include count_words: libcounter.a /lib/libfl.a libcounter.a: libcounter.a(lexer.o) libcounter.a(counter.o) libcounter.a(lexer.o): lexer.o $(AR) $(ARFLAGS) $@ $< libcounter.a(counter.o): counter.o $(AR) $(ARFLAGS) $@ $< count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h
When executed, make
produces this output:
$ make gcc -I include -c -o count_words.o src/count_words.c flex -t src/lexer.l> lexer.c gcc -I include -c -o lexer.o lexer.c ar rv libcounter.a lexer.o ar: creating libcounter.a a - lexer.o gcc -I include -c -o counter.o src/counter.c ar rv libcounter.a counter.o a - counter.o gcc count_words.o libcounter.a /lib/libfl.a -o count_words rm lexer.c
Notice the archive updating rule. The automatic variable $@
is expanded to the library name even though the target in the makefile is libcounter.a(lexer.o).
Finally, it should be mentioned that an archive library contains an index of the symbols it contains. Newer archive programs such as GNU ar
manage this index automatically when a new module is added to the archive. However, many older versions of ar
do not. To create or update the index of an archive another program ranlib
is used. On these systems, the built-in implicit rule for updating archives is insufficient. For these systems, a rule such as:
libcounter.a: libcounter.a(lexer.o) libcounter.a(counter.o) $(RANLIB) $@
must be used. Or if you choose to use the alternate approach for large archives:
libcounter.a: counter.o lexer.o $(RM) $@ $(AR) $(ARFLGS) $@ $^ $(RANLIB) $@
Of course, this syntax for managing the members of an archive can be used with the built-in implicit rules as well. GNU make
comes with a built-in rule for updating an archive. When we use this rule, our makefile becomes:
VPATH = src include CPPFLAGS = -I include count_words: libcounter.a -lfl libcounter.a: libcounter.a(lexer.o) libcounter.a(counter.o) count_words.o: counter.h counter.o: counter.h lexer.h lexer.o: lexer.h
Using Libraries as Prerequisites
When libraries appear as prerequisites, they can be referenced using either a standard filename or with the -l
syntax. When filename syntax is used:
xpong: $(OBJECTS) /lib/X11/libX11.a /lib/X11/libXaw.a $(LINK) $^ -o $@
the linker will simply read the library files listed on the command line and process them normally. When the -l
syntax is used, the prerequisites aren’t proper files at all:
xpong: $(OBJECTS) -lX11 -lXaw $(LINK) $^ -o $@
When the -l
form is used in a prerequisite, make
will search for the library (preferring a shared library) and substitute its value, as an absolute path, into the $^
and $?
variables. One great advantage of the second form is that it allows you to use the search and shared library preference feature even when the system’s linker cannot perform these duties. Another advantage is that you can customize make
’s search path so it can find your application’s libraries as well as system libraries. In this case, the first form would ignore the shared library and use the archive library since that is what was specified on the link line. In the second form, make
knows that shared libraries are preferred and will search first for a shared version of X11 before settling for the archive version. The pattern for recognizing libraries from the -l
format is stored in .LIBPATTERNS
and can be customized for other library filename formats. Unfortunately, there is a small wrinkle. If a makefile specifies a library file target, it cannot use the -l
option for that file in a prerequisite. For instance, the following makefile:
count_words: count_words.o -lcounter -lfl $(CC) $^ -o $@ libcounter.a: libcounter.a(lexer.o) libcounter.a(counter.o)
fails with the error:
No rule to make target `-lcounter', needed by `count_words'
It appears that this error occurs because make
does not expand -lcounter
to libcounter.a and search for a target, but instead does a straight library search. So for libraries built within the makefile, the filename form must be used.
Getting complex programs to link without error can be somewhat of a black art. The linker will search libraries in the order in which they are listed on the command line. So if library A
includes an undefined symbol, say open
, that is defined in library B
, the link command line must list A
before B
(that is, A
requires B
). Otherwise, when the linker reads A
and sees the undefined symbol open
, it’s too late to go back to B
. The linker doesn’t ever go back. As you can see, the order of libraries on the command line is of fundamental importance.
When the prerequisites of a target are saved in the $^
and $?
variables, their order is preserved. So using $^
as in the previous example expands to the same files in the same order as the prerequisites list. This is true even when the prerequisites are split across multiple rules. In that case, the prerequisites of each rule are appended to the target prerequisite list in the order they are seen.
A closely related problem is mutual reference between libraries, often referred to as circular references or circularities. Suppose a change is made and library B
now references a symbol defined in library A
. We know A
must come before B
, but now B
must come before A
. Hmm, a problem. The solution is to reference A
both before and after B
: -l
A
-l
B
-l
A
. In large, complex programs, libraries often need to be repeated in this way, sometimes more than twice.
This situation poses a minor problem for make
because the automatic variables normally discard duplicates. For example, suppose we need to repeat a library prerequisite to satisfy a library circularity:
xpong: xpong.o libui.a libdynamics.a libui.a -lX11 $(CC) $^ -o $@
This prerequisite list will be processed into the following link command:
gcc xpong.o libui.a libdynamics.a /usr/lib/X11R6/libX11.a -o xpong
Oops. To overcome this behavior of $^
an additional variable is available in make
, $+
. This variable is identical to $^
with the exception that duplicate prerequisites are preserved. Using $+
:
xpong: xpong.o libui.a libdynamics.a libui.a -lX11 $(CC) $+ -o $@
This prerequisite list will be processed into the following link command:
gcc xpong.o libui.a libdynamics.a libui.a /usr/lib/X11R6/libX11.a -o xpong
Double-Colon Rules
Double-colon rules are an obscure feature that allows the same target to be updated with different commands depending on which set of prerequisites are newer than the target. Normally, when a target appears more than once all the prerequisites are appended in a long list with only one command script to perform the update. With double-colon rules, however, each occurrence of the target is considered a completely separate entity and is handled individually. This means that for a particular target, all the rules must be of the same type, either they are all double-colon rules or all single-colon rules.
Realistic, useful examples of this feature are difficult to come by (which is why it is an obscure feature), but here is an artificial example:
file-list:: generate-list-script chmod +x $< generate-list-script $(files) > file-list file-list:: $(files) generate-list-script $(files) > file-list
We can regenerate the file-list target two ways. If the generating script has been updated, we make the script executable, then run it. If the source files have changed, we simply run the script. Although a bit far-fetched, this gives you a feel for how the feature might be used.
We’ve covered most of the features of make
rules and, along with variables and commands, this is the essence of make
. We’ve focused largely on the specific syntax and behavior of the features without going much into how to apply them in more complex situations. That is the subject of Part II. For now, we will continue our discussion with variables and then commands.
[1] In more controlled environments using wildcards to select the files in a program is considered bad practice because a rogue source file might be accidentally linked into a program.
[2] CVS is, in turn, becoming supplanted by newer tools. While it is currently the most ubiquitous source control system, subversion (http://subversion.tigris.org) looks to be the new wave.
[3] This is an impressive little command script, but I think it requires some explanation. First, we use the C compiler with the -M
option to create a temporary file containing the dependencies for this target. The temporary filename is created from the target, $@
, with a unique numeric suffix added, $$$$
. In the shell, the variable $$
returns the process number of the currently running shell. Since process numbers are unique, this produces a unique filename. We then use sed
to add the .d file as a target to the rule. The sed
expression consists of a search part, \($*\)\.o[ :]*
, and a replacement part, \1.o $@ :
, separated by commas. The search expression begins with the target stem, $*
, enclosed in a regular expression (RE) group, \(\)
, followed by the file suffix, \.o
. After the target filename, there come zero or more spaces or colons, [ :]*
. The replacement portion restores the original target by referencing the first RE group and appending the suffix, \1.o
, then adding the dependency file target, $@
.
Get Managing Projects with GNU Make, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.