BUY THIS BOOK
Add to Cart

Print Book $29.95


Add to Cart

PDF $23.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £20.95

What is this?

Looking to Reprint or License this content?


Managing Projects with GNU Make
Managing Projects with GNU Make, Third Edition

By Robert Mecklenburg
Book Price: $29.95 USD
£20.95 GBP
PDF Price: $23.99

Cover | Table of Contents | Online Book | Colophon


Table of Contents

Chapter 1: How to Write a Simple Makefile
The mechanics of programming usually follow a fairly simple routine of editing source files, compiling the source into an executable form, and debugging the result. Although transforming the source into an executable is considered routine, if done incorrectly a programmer can waste immense amounts of time tracking down the problem. Most developers have experienced the frustration of modifying a function and running the new code only to find that their change did not fix the bug. Later they discover that they were never executing their modified function because of some procedural error such as failing to recompile the source, relink the executable, or rebuild a jar. Moreover, as the program's complexity grows these mundane tasks can become increasingly error-prone as different versions of the program are developed, perhaps for other platforms or other versions of support libraries, etc.
The make program is intended to automate the mundane aspects of transforming source code into an executable. The advantages of make over scripts is that you can specify the relationships between the elements of your program to make, and it knows through these relationships and timestamps exactly what steps need to be redone to produce the desired program each time. Using this information, make can also optimize the build process avoiding unnecessary steps.
GNU make (and other variants of make) do precisely this. make defines a language for describing the relationships between source code, intermediate files, and executables. It also provides features to manage alternate configurations, implement reusable libraries of specifications, and parameterize processes with user-defined macros. In short, make can be considered the center of the development process by providing a roadmap of an application's components and how they fit together.
The specification that make uses is generally saved in a file named makefile. Here is a makefile to build the traditional "Hello, World" program:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Targets and Prerequisites
Essentially a makefile contains a set of rules used to build an application. The first rule seen by make is used as the default rule. A rule consists of three parts: the target, its prerequisites, and the command(s) to perform:
               target: prereq
               1 
               prereq
               2
               commands
            
The target is the file or thing that must be made. The prerequisites or dependents are those files that must exist before the target can be successfully created. And the commands are those shell commands that will create the target from the prerequisites.
Here is a rule for compiling a C file, foo.c, into an object file, foo.o:
foo.o: foo.c foo.h
        gcc -c foo.c
The target file foo.o appears before the colon. The prerequisites foo.c and foo.h appear after the colon. The command script usually appears on the following lines and is preceded by a tab character.
When make is asked to evaluate a rule, it begins by finding the files indicated by the prerequisites and target. If any of the prerequisites has an associated rule, make attempts to update those first. Next, the target file is considered. If any prerequisite is newer than the target, the target is remade by executing the commands. Each command line is passed to the shell and is executed in its own subshell. If any of the commands generates an error, the building of the target is terminated and make exits. One file is considered newer than another if it has been modified more recently.
Here is a program to count the number of occurrences of the words "fee," "fie," "foe," and "fum" in its input. It uses a flex scanner driven by a simple main:
#include <stdio.h>

extern int fee_count, fie_count, foe_count, fum_count;
extern int yylex( void );

int main( int argc, char ** argv )
{
    yylex( );
    printf( "%d %d %d %d\n", fee_count, fie_count, foe_count, fum_count );
    exit( 0 );
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Dependency Checking
How did make decide what to do? Let's go over the previous execution in more detail to find out.
First make notices that the command line contains no targets so it decides to make the default goal, count_words. It checks for prerequisites and sees three: count_words.o, lexer.o, and -lfl. make now considers how to build count_words.o and sees a rule for it. Again, it checks the prerequisites, notices that count_words.c has no rules but that the file exists, so make executes the commands to transform count_words.c into count_words.o by executing the command:
gcc -c count_words.c
This "chaining" of targets to prerequisites to targets to prerequisites is typical of how make analyzes a makefile to decide the commands to be performed.
The next prerequisite make considers is lexer.o. Again the chain of rules leads to lexer.c but this time the file does not exist. make finds the rule for generating lexer.c from lexer.l so it runs the flex program. Now that lexer.c exists it can run the gcc command.
Finally, make examines -lfl. The -l option to gcc indicates a system library that must be linked into the application. The actual library name indicated by "fl" is libfl.a. GNU make includes special support for this syntax. When a prerequisite of the form-l<NAME> is seen, make searches for a file of the form libNAME.so; if no match is found, it then searches for libNAME.a. Here make finds /usr/lib/libfl.a and proceeds with the final action, linking.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Minimizing Rebuilds
When we run our program, we discover that aside from printing fees, fies, foes, and fums, it also prints text from the input file. This is not what we want. The problem is that we have forgotten some rules in our lexical analyzer and flex is passing this unrecognized text to its output. To solve this problem we simply add an "any character" rule and a newline rule:
        int fee_count = 0;
        int fie_count = 0;
        int foe_count = 0;
        int fum_count = 0;
%%
fee     fee_count++;
fie     fie_count++;
foe     foe_count++;
fum     fum_count++;
.
\n
After editing this file we need to rebuild the application to test our fix:
$ make
flex -t lexer.l > lexer.c
gcc -c lexer.c
gcc count_words.o lexer.o -lfl -ocount_words
Notice this time the file count_words.c was not recompiled. When make analyzed the rule, it discovered that count_words.o existed and was newer than its prerequisite count_words.c so no action was necessary to bring the file up to date. While analyzing lexer.c, however, make saw that the prerequisite lexer.l was newer than its target lexer.c so make must update lexer.c. This, in turn, caused the update of lexer.o and then count_words. Now our word counting program is fixed:
$ count_words < lexer.l
3 3 3 3
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Invoking make
The previous examples assume that:
  • All the project source code and the make description file are stored in a single directory.
  • The make description file is called makefile, Makefile, or GNUMakefile.
  • The makefile resides in the user's current directory when executing the make command.
When make is invoked under these conditions, it automatically creates the first target it sees. To update a different target (or to update more than one target) include the target name on the command line:
$ make lexer.c
            
When make is executed, it will read the description file and identify the target to be updated. If the target or any of its prerequisite files are out of date (or missing) the shell commands in the rule's command script will be executed one at a time. After the commands are run make assumes the target is up to date and moves on to the next target or exits.
If the target you specify is already up to date, make will say so and immediately exit, doing nothing else:
$ make lexer.c
make: `lexer.c' is up to date.
If you specify a target that is not in the makefile and for which there is no implicit rule (discussed in Chapter 2), make will respond with:
$ make non-existent-target
make: *** No rule to make target `non-existent-target'.  Stop.
make has many command-line options. One of the most useful is —just-print (or -n) which tells make to display the commands it would execute for a particular target without actually executing them. This is particularly valuable while writing makefiles. It is also possible to set almost any makefile variable on the command line to override the default value or the value set in the makefile.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Basic Makefile Syntax
Now that you have a basic understanding of make you can almost write your own makefiles. Here we'll cover enough of the syntax and structure of a makefile for you to start using make.
Makefiles are usually structured top-down so that the most general target, often called all, is updated by default. More and more detailed targets follow with targets for program maintenance, such as a clean target to delete unwanted temporary files, coming last. As you can guess from these target names, targets do not have to be actual files, any name will do.
In the example above we saw a simplified form of a rule. The more complete (but still not quite complete) form of a rule is:
               target
               1 
               target
               2 
               target
               3 : prerequisite
               1 
               prerequisite
               2
               command
               1
               command
               2
               command
               3
            
One or more targets appear to the left of the colon and zero or more prerequisites can appear to the right of the colon. If no prerequisites are listed to the right, then only the target(s) that do not exist are updated. The set of commands executed to update a target are sometimes called the command script, but most often just the commands.
Each command must begin with a tab character. This (obscure) syntax tells make that the characters that follow the tab are to be passed to a subshell for execution. If you accidentally insert a tab as the first character of a noncommand line, make will interpret the following text as a command under most circumstances. If you're lucky and your errant tab character is recognized as a syntax error you will receive the message:
$ make
Makefile:6: *** commands commence before first target.  Stop.
We'll discuss the complexities of the tab character in Chapter 2.
The comment character for
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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 $( ) to 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 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 six "core" automatic variables:
$@
The filename representing the target.
$%
The filename element of an archive member specification.
$<
The filename of the first prerequisite.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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:
#ifdef 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.
Figure 2-1: Example source tree layout
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 $< > $@
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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:
%: %.c
        $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
We'll go into the details of this syntax in a bit, but first let's go over
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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 (which we will discuss briefly later). 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 $<
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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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 2.4.3 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 when make exits. If the file already exists when make 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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
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
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Variables and Macros
We've been looking at makefile variables for a while now and we've seen many examples of how they're used in both the built-in and user-defined rules. But the examples we've seen have only scratched the surface. Variables and macros get much more complicated and give GNU make much of its incredible power.
Before we go any further, it is important to understand that make is sort of two languages in one. The first language describes dependency graphs consisting of targets and prerequisites. (This language was covered in Chapter 2.) The second language is a macro language for performing textual substitution. Other macro languages you may be familiar with are the C preprocessor, m4, TEX, and macro assemblers. Like these other macro languages, make allows you to define a shorthand term for a longer sequence of characters and use the shorthand in your program. The macro processor will recognize your shorthand terms and replace them with their expanded form. Although it is easy to think of makefile variables as traditional programming language variables, there is a distinction between a macro "variable" and a "traditional" variable. A macro variable is expanded "in place" to yield a text string that may then be expanded further. This distinction will become more clear as we proceed.
A variable name can contain almost any characters including most punctuation. Even spaces are allowed, but if you value your sanity you should avoid them. The only characters actually disallowed in a variable name are :, #, and =.
Variables are case-sensitive, so cc and CC refer to different variables. To get the value of a variable, enclose the variable name in $( ). As a special case, single-letter variable names can omit the parentheses and simply use $ letter. This is why the automatic variables can be written without the parentheses. As a general rule you should use the parenthetical form and avoid single letter variable names.
Variables can also be expanded using curly braces as in
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What Variables Are Used For
In general it is a good idea to use variables to represent external programs. This allows users of the makefile to more easily adapt the makefile to their specific environment. For instance, there are often several versions of awk on a system: awk, nawk, gawk. By creating a variable, AWK, to hold the name of the awk program you make it easier for other users of your makefile. Also, if security is an issue in your environment, a good practice is to access external programs with absolute paths to avoid problems with user's paths. Absolute paths also reduce the likelihood of issues if trojan horse versions of system programs have been installed somewhere in a user's path. Of course, absolute paths also make makefiles less portable to other systems. Your own requirements should guide your choice.
Though your first use of variables should be to hold simple constants, they can also store user-defined command sequences such as:
DF  = df
AWK = awk
free-space := $(DF) . | $(AWK) 'NR =  = 2 { print $$4 }'
for reporting on free disk space. Variables are used for both these purposes and more, as we will see.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Variable Types
There are two types of variables in make: simply expanded variables and recursively expanded variables. A simply expanded variable (or a simple variable) is defined using the := assignment operator:
MAKE_DEPEND := $(CC) -M
It is called "simply expanded" because its righthand side is expanded immediately upon reading the line from the makefile. Any make variable references in the righthand side are expanded and the resulting text saved as the value of the variable. This behavior is identical to most programming and scripting languages. For instance, the normal expansion of this variable would yield:
gcc -M
However, if CC above had not yet been set, then the value of the above assignment would be:
<space>-M
$(CC) is expanded to its value (which contains no characters), and collapses to nothing. It is not an error for a variable to have no definition. In fact, this is extremely useful. Most of the implicit commands include undefined variables that serve as place holders for user customizations. If the user does not customize a variable it collapses to nothing. Now notice the leading space. The righthand side is first parsed by make to yield the string $(CC) -M. When the variable reference is collapsed to nothing, make does not rescan the value and trim blanks. The blanks are left intact.
The second type of variable is called a recursively expanded variable. A recursively expanded variable (or a recursive variable) is defined using the = assignment operator:
MAKE_DEPEND = $(CC) -M
It is called "recursively expanded" because its righthand side is simply slurped up by make and stored as the value of the variable without evaluating or expanding it in any way. Instead, the expansion is performed when the variable is used. A better term for this variable might be lazily expanded variable, since the evaluation is deferred until it is actually used. One surprising effect of this style of expansion is that assignments can be performed "out of order":
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Macros
Variables are fine for storing values as a single line of text, but what if we have a multiline value such as a command script we would like to execute in several places? For instance, the following sequence of commands might be used to create a Java archive (or jar) from Java class files:
echo Creating $@...
$(RM) $(TMP_JAR_DIR)
$(MKDIR) $(TMP_JAR_DIR)
$(CP) -r $^ $(TMP_JAR_DIR)
cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ .
$(JAR) -ufm $@ $(MANIFEST)
$(RM) $(TMP_JAR_DIR)
At the beginning of long sequences such as this, I like to print a brief message. It can make reading make's output much easier. After the message, we collect our class files into a clean temporary directory. So we delete the temporary jar directory in case an old one is left lying about, then we create a fresh temporary directory. Next we copy our prerequisite files (and all their subdirectories) into the temporary directory. Then we switch to our temporary directory and create the jar with the target filename. We add the manifest file to the jar and finally clean up. Clearly, we do not want to duplicate this sequence of commands in our makefile since that would be a maintenance problem in the future. We might consider packing all these commands into a recursive variable, but that is ugly to maintain and difficult to read when make echoes the command line (the whole sequence is echoed as one enormous line of text).
Instead, we can use a GNU make "canned sequence" as created by the define directive. The term "canned sequence" is a bit awkward, so we'll call this a macro. A macro is just another way of defining a variable in make, and one that can contain embedded newlines! The GNU make manual seems to use the words variable and macro interchangeably. In this book, we'll use the word macro specifically to mean variables defined using the define directive and variable only when assignment is used.
define create-jar
 @echo Creating $@...
 $(RM) $(TMP_JAR_DIR)
 $(MKDIR) $(TMP_JAR_DIR)
 $(CP) -r $^ $(TMP_JAR_DIR)
 cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ .
 $(JAR) -ufm $@ $(MANIFEST)
 $(RM) $(TMP_JAR_DIR)
endef
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
When Variables Are Expanded
In the previous sections, we began to get a taste of some of the subtleties of variable expansion. Results depend a lot on what was previously defined, and where. You could easily get results you don't want, even if make fails to find any error. So what are the rules for expanding variables? How does this really work?
When make runs, it performs its job in two phases. In the first phase, make reads the makefile and any included makefiles. At this time, variables and rules are loaded into make's internal database and the dependency graph is created. In the second phase, make analyzes the dependency graph and determines the targets that need to be updated, then executes command scripts to perform the required updates.
When a recursive variable or define directive is processed by make, the lines in the variable or body of the macro are stored, including the newlines without being expanded. The very last newline of a macro definition is not stored as part of the macro. Otherwise, when the macro was expanded an extra newline would be read by make.
When a macro is expanded, the expanded text is then immediately scanned for further macro or variable references and those are expanded and so on, recursively. If the macro is expanded in the context of an action, each line of the macro is inserted with a leading tab character.
To summarize, here are the rules for when elements of a makefile are expanded:
  • For variable assignments, the lefthand side of the assignment is always expanded immediately when make reads the line during its first phase.
  • The righthand side of = and ?= are deferred until they are used in the second phase.
  • The righthand side of := is expanded immediately.
  • The righthand side of += is expanded immediately if the lefthand side was originally defined as a simple variable. Otherwise, its evaluation is deferred.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Target- and Pattern-Specific Variables
Variables usually have only one value during the execution of a makefile. This is ensured by the two-phase nature of makefile processing. In phase one, the makefile is read, variables are assigned and expanded, and the dependency graph is built. In phase two, the dependency graph is analyzed and traversed. So when command scripts are being executed, all variable processing has already completed. But suppose we wanted to redefine a variable for just a single rule or pattern.
In this example, the particular file we are compiling needs an extra command-line option, -DUSE_NEW_MALLOC=1, that should not be provided to other compiles:
gui.o: gui.h
        $(COMPILE.c) -DUSE_NEW_MALLOC=1 $(OUTPUT_OPTION) $<
Here, we've solved the problem by duplicating the compilation command script and adding the new required option. This approach is unsatisfactory in several respects. First, we are duplicating code. If the rule ever changes or if we choose to replace the built-in rule with a custom pattern rule, this code would need to be updated and we might forget. Second, if many files require special treatment, the task of pasting in this code will quickly become very tedious and error-prone (imagine a hundred files like this).
To address this issue and others, make provides target-specific variables. These are variable definitions attached to a target that are valid only during the processing of that target and any of its prerequisites. We can rewrite our previous example using this feature like this:
gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1
gui.o: gui.h
        $(COMPILE.c) $(OUTPUT_OPTION) $<
The variable CPPFLAGS is built in to the default C compilation rule and is meant to contain options for the C preprocessor. By using the += form of assignment, we append our new option to any existing value already present. Now the compile command script can be removed entirely:
gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1
gui.o: gui.h
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Where Variables Come From
So far, most variables have been defined explicitly in our own makefiles, but variables can have a more complex ancestry. For instance, we have seen that variables can be defined on the make command line. In fact, make variables can come from these sources:
File
Of course, variables can be defined in the makefile or a file included by the makefile (we'll cover the include directive shortly).
Command line
Variables can be defined or redefined directly from the make command line:
$ make CFLAGS=-g CPPFLAGS='-DBSD -DDEBUG'
A command-line argument containing an = is a variable assignment. Each variable assignment on the command line must be a single-shell argument. If the value of the variable (or heaven forbid, the variable itself) contains spaces, the argument must be surrounded by quotes or the spaces must be escaped.
An assignment of a variable on the command line overrides any value from the environment and any assignment in the makefile. Command-line assignments can set either simple or recursive variables by using := or =, respectively. It is possible using the override directive to allow a makefile assignment to be used instead of a command-line assignment.
# Use big-endian objects or the program crashes!
override LDFLAGS = -EB
Of course, you should ignore a user's explicit assignment request only under the most urgent circumstances (unless you just want to irritate your users).
Environment
All the variables from your environment are automatically defined as make variables when make starts. These variables have very low precedence, so assignments within the makefile or command-line arguments will override the value of an environment variable. You can cause environment variables to override
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Conditional and include Processing
Parts of a makefile can be omitted or selected while the makefile is being read using conditional processing directives. The condition that controls the selection can have several forms such as "is defined" or "is equal to." For example:
# COMSPEC is defined only on Windows.
ifdef COMSPEC
  PATH_SEP := ;
  EXE_EXT  := .exe
else
  PATH_SEP := :
  EXE_EXT  :=
endif
This selects the first branch of the conditional if the variable COMSPEC is defined.
The basic syntax of the conditional directive is:
               if-condition
               text if the condition is true
endif
or:
               if-condition
               text if the condition is true
else
  text if the condition is false
endif
The if-condition can be one of:
ifdef  variable-name
ifndef variable-name
ifeq  test
ifneq tes