GNU Make Manual

From University
Jump to: navigation, search
Most material is from
Managing Projects with GNU Make 3rd 2004
(MPGM3).

File:OReilly - Managing Projects with GNU Make(2004) 3Ed.tar.gz
File:OReilly - Managing Projects with GNU Make(2004) 3Ed make rules.txt
Erratta[1].

  • GNU make Manual[2]
  • Install flex and bison to run the examples.

The All-Powerful Tab Key

  • Chapter 1

Remember the all-powerful Tab key. So set the text editor preferences to keep tabs and to NEVER substitute spaces for a tab.

Example 1 from MPGM3, page 10.
Rule for creating foo.o and linking it with the foo.h header file.

foo.o: foo.c foo.h
<tab>   gcc -c foo.c

The example project will be Super Star Trek[3] with the original File:Sstsrc makefile.txt. And creating a new one for emscripten SDK[4].

Example 2 from MPGM3, page 11, ch01-cw1.
The rule for producing lexer.c uses ">" to invoke the flex program.

lexer.c: lexer.l
      flex -t lexer.l > lexer.c

The targets and prerequisites form an event chain or dependency graph. The graph for any one run changes with file changes different parameters.

Rules - Part I - Chapter 2

A Makefile has targets, prerequisites, and the rules to make both. Since the output for one can be the input for another make forms them into a dependency chain or graph. Building and processing this dependency graph to update the requested target is what make is all about.

Explicit Rules

Explicit rules specify each target and prerequisites in full. No variables are used. For instance:

vpath.o variable.o: make.h config.h getopt.h gettext.h dep.h

Indicates vpath.o and variable.o on the same 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

Both targets are handled independently.

A rule might not be defined in one place. Each time make sees a target file it adds the target and prerequisites to the dependency graph. If a target exists in the graph, any additional prerequisites are appended to the target file entry in the dependency graph. In the simple case it can break long lines to improve readability:

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 hold 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 about 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.

Wildcards

Wildcards, a.k.a. globbing, used are identical to Bourne shell wildcards and grammar. The tilde character (~) represents the user's home directory. A tilde followed by a user name represents that user’s home directory.

Using wildcards to select files for linking is a bad practice because unknown files might accidentally be linked into a program with no way to trace where the file came from.

Mistakes with wildcards are easy to make.

*.o: constants.h

All object files depend on the header file constants.h. But if no object files exist make will see

   : constants.h

Better to use something like

*.c: constants.h

Phony Targets

A phony target is a label representing a command script. They are always out-of-date and always processed first. Common labels are all and clean as in:

clean:
      rm -f *.o lexer.c

There is no distinction between a real file and a phony target. If a file in the directory matches the phony target a confusing message like,

$ make clean
make: 'clean' is up to date.

will appear.
To avoid confusion a special target, .PHONY, is used to indicate a target is not a real file. This means the command will execute at intended.

.PHONY: clean
clean:
      rm -f *.o lexer.c

Phony targets can have real targets as prerequisites. For instance,

.PHONY: all
all: bash bashbug

builds bash and bashbug.

Table 1 Common phony targets
Target Function
all Perform all tasks to build the application
install Create an installation for the application
clean Delete the binary files generated from sources
distclean Delete all generated files that were not in the original source distribution
TAGS Create a tags table for use by editors
info Create GNU info files from their Texinfo sources
check Run any tests associated with this application

Empty Targets

Empty targets use an empty file, cookie, as the target file. This means the command is executed occasionally, without the dependencies being updated with every run. The empty file is useful as a timestamp recording when the command was last run. As in the example below:

prog: size prog.o
      $(CC) $(LDFLAGS) -o $@ $^

size: prog.o
      size $^
      touch size

Variables

Main Article: GNU Make Manual Implicit Variables
A variable is a label, usually in all capitals, for a command or file list that is expanded using $(<variable-name). For example,

OFILES = file1.o file2.o file3.o
clean:
	rm $(OFILES)

deletes all the object files.

Automatic Variables

Automatic variables are set by make after a rule is matched.

Table 2 Seven "core" automatic variables
Variable Function
$@ The file 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.

Variables have two variants. The first appends a "D" to the variable and returns only the directory portion, as in $(@D), $(<D), etc. The second appends an "F" to the variable and returns only the file portion, as in $(@F), $(<F), etc.

Here is the makefile with explicit filenames replaced with automatic variables.

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 -r $< > $@

Finding Files with VPATH and vpath

Now to expand the example by placing source files in different directories. Start by refactoring main into a function called counter.

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


Below the paths are added to the makefile.

# ch02-cw5a source code
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 $< > $@

But make spits up an error that it can't update count_words.c. That's because it can't find it. Add the path to the makefile,

VPATH = src

source code in ch02-cw5b
This produces a new error message:

$ 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

The first line shows make found the correct source but gcc failed because it could not find the include file. The fix is adding the appropriate -I option:

CPPFLAGS = -I include

Now the build succeeds, source code ch02-cw6. But it still spits up an error for src/count_words.c at line 8 where it says counter( counts); is an implicit declaration, which it isn't. Found it, the count_words.h had a bug, what should have been

#ifndef COUNTER_H_
#define COUNTER_H_

was really

#ifdef COUNTER_H_
#define COUNTER_H_

so the header file was never defined, and so no header definition to load.

The general form for VPATH is

vpath pattern directory-list

so VPATH in code ch02-cw6 could be expanded to

VPATH= ./src:./include

which allows us to remove the include header file prerequisites.

Pattern Rules

Wildcards are used instead of explicit filenames. This allows make to apply the rule any time a target file matching the pattern is updated. Defining files for grepping once, so there is only one place to make a change. A pattern rule works with file stems, the portion before the suffix, and is represented by the % character.

The ch02-cw6 makefile works because three built-in rules are used. The first specifies how to compile a .o file from a .c file:

%.o: %.c
      $(COMPILE.c) $(OUTPUT_OPTION) $<
<nowiki>
The second specifies how to make a ''.c'' file from a ''.l'' file:
 <nowiki>
%.c: %.l
      @$(RM) $@
      $(LEX.l) $< > $@

The third rule generates a file with no suffix, always an executable, from a .c file:

%: %.c
      $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

The Patterns

The % (percent) character in a pattern rule is roughly equivalent to a * (asterisk) in a UNIX shell in that it represents a character string. The percent character can occur anywhere, but only once, in a pattern. Here are some valid uses:

%,V
s%.o
wrapper_%

Other characters match literally within a filename. A pattern can contain a prefix, suffix, or both, or none. A stem word must contain at least one character. Patterns are evaluated as follows:

  1. A matching pattern rule target. The pattern rule must start with a prefix and end with a suffix, if they exist.
  2. If found, the characters between the prefix and suffix are taken as the filename stem.
  3. The stem is substituted into the prerequisite pattern.
  4. If the resulting filename exists, or can be made by applying another rule, a match is made and the rule is applied.

Patterns with only a percent character have many uses. A common one is building UNIX executable programs. The very reason for make's existence. For eample:

%: %.cpp
      $(LINK.cpp) $^ $(LOADLIBS) $(LDLIBS) -o $@

Builds an executable from a C preprocessed source file.

Static Pattern Rules

Are similar to pattern rules except they apply only to specific target files. A static pattern rule applies to just a specific target list.

$(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.

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, a warning is displayed. 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) method for defining implicit rules. The Pattern Rules described above are preferred. GNU make retains suffix rules for compatibility with legacy makefiles and make variants.

Suffix rules have one or two suffices 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 targets and prerequisites as:

%.o: %.c
     $(COMPILE.c) $(OUTPUT_OPTION) $<

The suffix rule forms the file stem 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. A single-suffix rule contains only one suffix, the suffix for the source file. These rules are used to create executables since Unix executables do not have a suffix.

This rule produces an executable image from a Pascal source file.

.p:
      $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@

The pattern rule version looks like this:

%: %.p
      $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@

A special target, .SUFFIXES, holds known suffixes. Here is the list for make 4.2.1:

.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .el

Add suffixes using a rule:

.SUFFIXES: .pdf .fo .html .xml

Delete all the suffixes by setting it to null:

.SUFFIXES:

The Implicit Rules Database

An implicit rule is either a pattern rule or a suffix rule supporting some software language. The Implicit Rules Database has rules supporting C, C++, Pascal, FORTRAN, ratfor, Modula, and Texinfo, among others. Use the command

$make --print-data-base

or just -p, to list make's definitions, rules, and version. The format looks like this:

%.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. Use an implicit rule by not defining a command script for it. This causes make to search its built-in database to satisfy the target. Usually this works as intended, but in rare cases unintended side-effects may occur.

For example, in a mixed language environment with Lisp and C source code the files editor.l and editor.c may both exist in the same directory and make will believe that the Lisp file is really a flex, since both use the .l suffix, and that the C source is the flex command's output. 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 from flex, overwriting the original source code.

The work-around is to mask the two flex rules in the built-in rule database by redefining them like this:

%.o: %.l
%.c: %.l

A pattern with no command script will redefine the rule in make’s database.

When make considers how to update a target, it searches the implicit rules for a target pattern that matches the target at hand. For each matching pattern make will look for an existing matching prerequisite. If a prerequisite is found, the associated rule is used. For some target patterns, there are many possible source files. For example, 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 but now assumes the matching source file should be considered as a new target for updating. By performing this search recursively, make can find a “rule chain” to update a target. In the 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.

Here is a chain rule in more detail. Create an empty yacc source file and register it with RCS using ci for version control. Note there is no Makefile and no "source" code, only an RCS file:

$touch foo.y
$ci foo.y
foo.y,v <-- foo.y
.
initial revision 1.1
done

Next run make with the --just-print or -n option so make runs through the build steps without actually building foo.

$ 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 just the 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.

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 intermediate files are a side effect for updating a target, make will delete them before exiting. As shown in the last line.

Rule Structure

The built-in rules have a standard structure intended to make them easily customizable. To review an existing rule, consider the rule for updating an object file from its C source:

%.o: %.c
        $(COMPILE.c) $(OUTPUT_OPTION) $<

Customizing this rule is controlled entirely by the variables it uses. There are two variables but COMPILE.c in particular is defined with four other variables:

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
CC = gcc
OUTPUT_OPTION = -o $@

The C compiler's behavior can be changed by altering the value for the CC variable. The other variables are used for setting compilation options (CFLAGS), preprocessor options (CPPFLAGS), and architecture-specific options (TARGET_ARCH).

The power to customize a built-in rule is also the power to introduce a customized bug. For example, give this assignment in a Makefile:

CPPFLAGS = -I project/include

A user would add a CPP define to the command line like this:

$make CPPFLAGS=-DEBUG 

But this would accidentally remove the -I option required for compilation. Thus breaking the author's customization feature. Variables set on the command line override all other assignments to the variable. As a work-around consider redefining the compilation variable to include customized variables:

COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) -c
INCLUDES = -I project/include

The alternative append-style assignment is described in the section "Other Types of Assignment" in Chapter 3.

Implicit Rules for Source Control

Using make's Implicit Rules for Source Control is a really, really bad idea. Modern version control systems used in complex software development simply can't be integrated with make in any effective manner. Best to leave version control to specialized software.

Special Targets

A special target is a built-in phony target used to change make’s default behavior. The special target .PHONY declares that its prerequisite does not refer to an actual file and should always be considered out of date.

Special targets follow the same syntax normal targets do but the target is not a file or even a normal phony. They are really directives for modifying make’s internal algorithms.

There are twelve special targets in three categories, alter make's behavior when updating a target, global compile flags, and the .SUFFIXES special target is used when specifying old-fashioned suffix rules.

  • .INTERMEDIATE

Prerequisites 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 are treated as intermediate files but are never automatically deleted. The most common use 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 the make 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 since make started. This is so make 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 that make 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 that make should delete the target if any of the commands associated with the rule generates an error. Only an interruption from a signal will cause makenormally to deletes the target.

Automatic Dependency Generation

When header files were added to the word counting program, ch02-cw3, a thorny problem surfaced. The header file dependency between the object files and C header files was added to the makefile by hand. This was easy in a toy example, but that solution does not scale up to real programs. Most header files include references to other header file, which include references to other header files, forming a complex dependency tree. For example, the C header file stdio.h depends on 16 other header files in Linux Mint:

$ echo "#include <stdio.h>" > stdio.c
$ gcc -M stdio.c
stdio.o: stdio.c
 /usr/include/stdc-predef.h \
 /usr/include/stdio.h \
 /usr/include/features.h \
 /usr/include/x86_64-linux-gnu/sys/cdefs.h \
 /usr/include/x86_64-linux-gnu/bits/wordsize.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
 /usr/lib/gcc/x86_64-linux-gnu/5/include/stddef.h \
 /usr/include/x86_64-linux-gnu/bits/types.h \
 /usr/include/x86_64-linux-gnu/bits/typesizes.h \
 /usr/include/libio.h \
 /usr/include/_G_config.h \
 /usr/include/wchar.h \
 /usr/lib/gcc/x86_64-linux-gnu/5/include/stdarg.h \
 /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
 /usr/include/x86_64-linux-gnu/bits/sys_errlist.h

The solution used in make works like this. Generate 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

The pattern rule for this script, from ch02-cw6, is a fairly ugly command:

 %.d: %.c
	 $(CC) -M $(CPPFLAGS) $< > $@.$$$$;			\
	 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;	\
	 rm -f $@.$$$$

All files in an include directive are treated by make as a target for updating. When make sees the .d files it will automatically try to create them. Here is the makefile for ch02-cw6 with 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 file list (whose names can include wildcards). The make function, subst transforms the file list a dependency list. This replaces the string .c with .d in each word in $(SOURCES).

Running make with the --just-print option produces:

$ make --just-print
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

The first three lines 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.

Managing Libraries

An archive library, usually called simply a library or archive, is a special file type 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.

For example, take the word counting program and store the reusable parts into a library. This library would hold two files, counter.o and lexer.o. The ar command to create the library is :

 $ar rv libcounter.a counter.o lexer.o
  ar: creating libcounter.a
  a - counter.o
  a - lexer.o

The r option replaces files in the archive. The v displays messages from ar in verbose format. The first argument after the options is the archive name followed by the file list. The next line is the verbose comment. The next two lines describe the files added.

A library can link with an executable in several ways. The most straightforward way is to simply list the library file on the command line and the compiler or linker will use the file suffix to determine the file type and subsequent updates:

cc count_words.o libcounter.a /lib/libfl.a -o count_words

Here cc will recognize the files libcounter.a and /lib/libfl.a as libraries and search them for undefined symbols. The next way to reference libraries is with the -l option:

cc count_words.o -lcounter -lfl -o count_words

When cc sees the -l option it searches for the library in the system’s standard library directories. Shared libraries are searched before archive libraries.

The search path 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. The last example fails to link because the current directory is not in cc’s library search path. The fix is adding the current directory like this:

cc count_words.o -L. -lcounter -lfl -o count_words

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 $^.

If libcountera is a prerequisite for count_words make will update the library before linking the executable. One problem. All items in the archive are replaced even if they have not been modified. This is wasteful and there is a better solution.

libcounter.a: counter.o lexer.o
     $(AR) $(ARFLAGS) $@ $?

Replacing $^ with $? will cause make to pass only those object files newer than the target to ar.

How about updating each file in the archive? Could be done. Problem is how ar is invoked. Every time its run a new process spawns for each file in the archive. Once an archive grows to a few dozen files, the time needed to search the archive, check each file, update if needed, then rebuild the archive is prohibitively expensive.

An archive item 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

The output looks like this:

$ 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).

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 borders on 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 only moves forward and never ever go back. A linker moving forward and backward over objects would quickly create an insolvable many-to-many relationship problem. The library order on the command line is critically import.

When the target prerequisites 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 for each rule are appended to the target prerequisite list in the order they are seen.

A closely related problem is mutual references 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. The solution is to reference A both before and after B: -lA -lB -lA. 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 in $^ an additional variable is available in make, $+. This variable is identical to $^ with the exception that duplicate prerequisites are preserved. Using $+ produces:

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 prerequisite set is 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 target occurrence is considered a completely separate entity and is handled individually. This means that for a particular target, all the rules must be 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

The file-list target can be regenerated in two ways. If the generating script has been updated, make the script executable and run it. If the source files have changed, just run the script. Although a bit far-fetched, this gives you a feel for how the feature might be used.

Variables and Macros - Chapter 3

The make preprocessor interprets two different languages. The first language describes dependency graphs containing targets and prerequisites. As discussed in Chapter 2. The second language is a macro language for performing textual substitution. The macro languages works similarly to the C preprocessor, m4, \(T_{E}X\), and macro assemblers. It will recognize user defined shorthand terms and replace them with their expanded form. The distinction between a macro “variable” and a “traditional” variable important. A macro variable is expanded “in place” to yield a text string that may then be expanded further. A "traditional" programming variable is substituted, but not expanded.

A variable name can contain almost any case-sensitive characters, including most punctuation marks. This means cc and CC each refer to a different variable. 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 =.

The value for a variable is extracted by enclosing 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 use the parenthetical form and avoid single letter variable names. Using meaningful variable names is highly recommended.

Variables can also be expanded using curly braces as in ${CC}, a common practice in older makefiles. There is seldom an advantage to using one over the other, so just pick one and stick with it. Some people use curly braces for variable reference and parentheses for function call, similar to the way the shell uses them. Most modern makefiles use parentheses and that’s what we’ll use throughout this book.

By convention, variables representing constants a user might want to customize on the command line or in the environment are written in all UPPERCASE. Words are separated by underscores. Variables that appear only in the makefile are all lower-case with words separated by underscores. User-defined functions in variables and macros use lowercase words separated by dashes.

This example illustrates the naming conventions:

# Some simple constants.
CC := gcc
MKDIR := mkdir -p
# Internal variables.
sources = *.c
objects = $(subst .c,.o,$(sources))
# A function or two.
maybe-make-dir = $(if $(wildcard $1),,$(MKDIR) $1)
assert-not-null = $(if $1,,$(error Illegal null value.))

The variable value is all the words to the right of the assignment symbol with leading space trimmed. Trailing spaces are not trimmed. This can occasion- ally cause trouble, for instance, if the trailing whitespace is included in the variable and subsequently used in a command script:

LIBRARY = libio.a # LIBRARY has a trailing space.
missing_file:
     touch $(LIBRARY)
     ls -l | grep '$(LIBRARY)'

The variable assignment contains a trailing space that is made more apparent by the comment (but a trailing space can also be present without a trailing comment). When this makefile is run it produces:

$ make
touch libio.a
ls -l | grep 'libio.a ' # <- errant space in the name
make: *** [missing_file] Error 1

Oops, the grep search string also included the trailing space and failed to find the file in ls’s output. Whitespace issues will be discussed in more detail later.

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, awk comes in several variants on a system: awk, nawk, and gawk. Creating the variable, AWK, to hold the awk program makes it easier for other users to read and modify your makefile. Using absolute paths for external programs guarantees the system version, whatever the revision, will be used. Versions installed in the user's path, by the user or by malware, will be avoided.

Variables can hold simple constants and store user-defined command sequences such as:

DF = df
AWK = awk
free-space := $(DF) . | $(AWK) 'NR = = 2 { print $$4 }'

The free-space variable holds the currant volume's free disk space.

Variable Types

Variables come in two types, 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

The command:

$(MAKE_DEPEND)

expands it to

gcc -M

If CC is undefined the output is:

<space>-M

A variable with no definition is not an error. In fact, this is extremely useful. Most 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 right-hand 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.

A recursively expanded variable or a recursive variable) is defined using the = assignment operator:

MAKE_DEPEND = $(CC) -M

The righthand side is simply assigned to the variable name without evaluating or expanding it in any way. The expansion is performed when the variable is used, using the current definitions for any enclosed variables. The MAKE_DEPEND variable recursively expand like this:

CC = g++ -E
MAKE_DEPEND = $(CC) -M
...
# Lower in the makefile
CC = gcc
# Now MAKE_DEPEND evaluates differently.

While adding great flexibility, recursive variables should be used with the utmost care.

Other Assignment Types

The conditional variable assignment operator, ?= performs the assignment only if the variable is undefined.

# Put all generated files in the directory $(PROJECT_DIR)/out.
OUTPUT_DIR ?= $(PROJECT_DIR)/out

The output directory variable, OUTPUT_DIR is set only if it is empty. The trick to using this is to check if the assignment was made later in the makefile.

The append, += appends text to an existing variable without changing the original values in the variable. This is trivial for a simple variable but is critical for a recursive variable.

The += operator might be implemented like this:

simple := $(simple) new text

Since the value in the simple variable has already undergone expansion, make can expand $(simple), append the text, and finish the assignment. But applying append operators to recursive variables can create an infinite loop:

recursive = $(recursive) new text

Will evaluate itself infinitely, producing an error something like:

$ make
makefile:2: *** Recursive variable `recursive' references itself (eventually). Stop.

The append operator was invented to solve that problem. Another great use for the append operator is to create a list using a variable and the append operator to sequentially add items to the variable.

Macros

The GNU make manual[5] describes these as Multi-Line Variables. In this discussion they are called Macros. A more descriptive name. Suppose you are building JAVA JAR files and want to group all the commands in one spot. The define directive would be used to group each command on a line as:

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

The define directive is followed by the variable name and a newline. The variable body includes all the text up to the endef keyword, which must appear on a line by itself. A variable created with define is expanded normally, except that when it is used in a command script, each line in the macro has a tab prepended to it. An example use is:

$(UI_JAR): $(UI_CLASSES)
        $(create-jar)

Notice the @ before the echo command. Command lines prefixed with an @ character are not echoed by make when the command is executed. When make runs, it doesn't print the echo command, just the output from that command. Just like the old MS-DOS batch scripts. This only applies to the line it is on and from that point to the newline character. If the @ prefix character is used on the macro reference, the entire macro body is hidden:

$(UI_JAR): $(UI_CLASSES)
        @$(create-jar)

And displays:

$ make
Creating ui.jar...

When Variables are Expanded

What are the rules for expanding variables?

GNU make runs in two passes. The first pass reads the makefile imports any included makefiles. Variables and rules are loaded into make's database and the dependency graph is created. The second pass analyzes the dependency graph and determines the targets that need updating. Then the command scripts for the updates are executed.

When a recursive variable or define directive is processed by make, the lines in the variable or macro are stored, including the newlines without being expanded. The macro's very last newline is not stored because it ends the endef command.

When a macro is expanded the text is scanned for further macro or variable references and those are expanded and so on, recursively. If a macro is expanded as an action, each line in the macro is inserted with a leading tab character.

The rules for elements expansion are summarized in Table 3.

Table 3 Element Expansion Rules
Rule Left Expansion Right Expansion Function
1 Immediate Deferred The left-hand side for = is always expanded immediately during the first pass. The right-hand side expansion is deferred until they are used in the second pass.
2 Immediate Deferred The left-hand side for ?= is expanded immediately. The right-hand side expansion is deferred until they are used in the second pass.
3 Immediate Deferred The left-hand and right-hand sides for := are expanded immediately.
4 Immediate Deferred or Immediate The right-hand side for += is expanded immediately if the left-hand side was originally defined as a simple variable. Otherwise, its evaluation is deferred.
5 Immediate Deferred For macro definitions (those using define), the macro variable name is immediately expanded and the body is deferred until used.
6 Immediate Deferred For rules, the targets and prerequisites are always immediately expanded while the commands are always deferred.

To illustrate the execution order let's expand the free-space macro used earlier into a makefile.

OUTPUT_DIR := /tmp
$(OUTPUT_DIR)/very_big_file:
        $(free-space)
define free-space
  $(PRINTF) "Free disk space "
  $(DF) . | $(AWK) 'NR = 2 { print $$4 }'
endef
BIN := /usr/bin
PRINTF := $(BIN)/printf
DF := /bin/df
AWK := $(BIN)/awk

Notice the declarations are in reverse order, but it works because make defines the variables on the first pass and expands the free-space macro on the second pass.

Which produces:

$make 
/usr/bin/printf "Free disk space "
Free disk space /bin/df . | /usr/bin/awk 'NR = 2 { print $4 }'
Available
647864492

Target- and Pattern-Specific Variables

Macros and variables are always defined once. Suppose you 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) $<

The solution was 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, pasting in this code in each spot would quickly become very tedious and error-prone (imagine a hundred files like this).

The target-specific variables attach to a target and are valid only while that target and its prerequisites are processed. Below is the same example using target-specific variables:

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

While the gui.o target is being processed, the variable CPPFLAGS will contain -DUSE_NEW_MALLOC=1 in addition to its original contents. When the gui.o target is finished, CPPFLAGS will revert to its original value.

The general syntax for target-specific variables is:

target...: variable = value
target...: variable := value
target...: variable += value
target...: variable ?= value

All the assignment forms are valid for target-specific variables. The variable does not need to exist before the assignment.

The variable assignment is not actually performed until the target processing begins. So the assignment's right-hand side can itself be a value set in another target-specific variable and all its prerequisites as well.

Where Variables Come From

Variables come from three sources.

  • File

From the makefile and any included makefiles.

  • 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 variable's value contains spaces, the argument must be surrounded by quotes or the spaces must be escaped.

Command line variable assignments override any value from the environment and any assignment in the makefile. They can be either simple or recursive variables by using := or =, respectively. It is possible using the override directive to allow a makefile assignment to be used to override a command-line assignment.

# Use big-endian objects or the program crashes!
override LDFLAGS = -EB
  • Environment

All the variables from the environment are automatically defined as make variables when it starts. These variables have very low precedence, so assignments within the makefile or command-line arguments will override an environment variable. The command-line argument --environment-overrides (or -e) will override makefile variables.

When make is invoked recursively, some variables from the parent make are passed through the environment to the child make. By default, only those variables that originally came from the environment are exported to the child’s environment, but any variable can be exported to the environment by using the export directive:

export CLASSPATH := $(HOME)/classes:$(PROJECT)/classes
SHELLOPTS = -x
export SHELLOPTS

All variables to be exported with:

export

Note that make will export variables with invalid shell variable characters. For example:

export valid-variable-in-make = Neat!
show-vars:
        env | grep '^valid-'
        valid_variable_in_shell=Great
        invalid-variable-in-shell=Sorry
$ make
env | grep '^valid-'
valid-variable-in-make=Neat!
valid_variable_in_shell=Great
invalid-variable-in-shell=Sorry
/bin/sh: line 1: invalid-variable-in-shell=Sorry: command not found
make: *** [show-vars] Error 127

An "invalid" shell variable was created by exporting valid-variable-in-make. This variable is not accessible through normal shell syntax, only through trickery such as running grep over the environment. Nevertheless, this variable is inherited by any sub-make where it is valid and accessible. Recursive make is covered in Part II.

Environment variable can be prevented from being exported to the subprocess by using:

unexport DISPLAY

The export and unexport directives work the same way their counterparts in sh work.

The conditional assignment operator interacts very nicely with environment variables. Suppose the default output directory set in the makefile, but you want users to override the default easily. The conditional assignment would be:

# Assume the output directory $(PROJECT_DIR)/out.
OUTPUT_DIR ?= $(PROJECT_DIR)/out

Here the assignment is performed only if OUTPUT_DIR has never been set. We can get nearly the same effect more verbosely with:

ifndef OUTPUT_DIR
  # Assume the output directory $(PROJECT_DIR)/out.
  OUTPUT_DIR = $(PROJECT_DIR)/out
endif

The difference is the conditional assignment operator will skip the assignment if the variable has been set in any way, even to the empty value, while the ifdef and ifndef operators test for a nonempty value. Thus, OUTPUT_DIR= is considered set by the conditional operator but not defined by ifdef.

It is important to note that excessive use of environment variables makes your makefile much less portable, since other users are not likely to have the same environment variables.

  • Automatic

Automatic variables are created immediately before executing the command script for a rule.

Traditionally, environment variables are used to help manage the differences between developer environments. For instance, it is common to create a development environment (source code, compiled output tree, and tools) based on environment variables referenced in the makefile. The makefile would refer to one environment variable for the root of each tree. If the source file tree is referenced from a variable PROJECT_SRC, binary output files from PROJECT_BIN, and libraries from PROJECT_LIB, then developers are free to place these trees wherever is appropriate.

A potential problem with this approach (and with the use of environment variables in general) occurs when these “root” variables are not set. One solution is to provide default values in the makefile using the $? assignment:

PROJECT_SRC ?= /dev/$(USER)/src
PROJECT_BIN ?= $(patsubst %/src,%/bin,$(PROJECT_SRC))
PROJECT_LIB ?= /net/server/project/lib

By using these variables to access project components, you can create a development environment that is adaptable to varying machine layouts. (We will see more comprehensive examples of this in Part II.) Beware of overreliance on environment variables, however. Generally, a makefile should be able to run with minimum support from the developer’s environment so be sure to provide reasonable defaults and check for critical components.

Conditional and include Processing

Sections in a makefile can be ignored or read using conditional processing directives. This can be done in many ways. The simplest uses a variable checks for its definition using ifdef and endif. 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 conditional branch if the variable COMSPEC is defined.

The basic syntax for 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:

ifdef variable-name
ifndef variable-name
ifeq test
ifneq test

The variable-name should not be surrounded by $( ) for the ifdef/ifndef test. Finally, the test can be expressed as either:

"a" "b"
(a,b)

in which single or double quotes can be used interchangeably (but the quotes must match).

The conditional processing directives can be used within macro definitions and command scripts as well as at the top level of makefiles:

libGui.a: $(gui_objects)
        $(AR) $(ARFLAGS) $@ $<
    ifdef RANLIB
        $(RANLIB) $@
    endif

Indenting makes for more readable code, but careless indentation can lead to errors. In the last example, the conditional directive are indented four spaces while the enclosed commands have a leading tab. If the enclosed commands didn't begin with a tab, they would not be recognized as commands by make. If the conditional directives had a leading tab, they would be misidentified as commands and passed to the subshell.

The ifeq and ifneq conditionals test if their arguments are equal or not equal. Whitespace in conditional processing can be tricky to handle. For instance, when using the parenthesis in the test, whitespace after the comma is ignored, but all other whitespace is significant:

ifeq (a, a)
  # These are equal
endif
ifneq ( b, b )
  # These are not equal - ' b' != 'b '
endif

Sticking with the quoted forms works best:

ifeq "a" "a"
  # These are equal
endif
ifeq 'b' 'b'
 # So are these
endif

Sometimes a variable expansion contains unexpected whitespace. This can cause problems since the comparison includes all the characters. Use the strip function to remove whitespace:

ifeq "$(strip $(OPTIONS)) "-d"
  COMPILATION_FLAGS += -DDEBUG
endif

The include Directive

A makefile can include other files. This is most commonly done to place common definitions in a make header file or to include automatically generated dependency information. The include directive is used like this:

include definitions.mk

The directive can be given a file list and shell wildcards and make variables are also allowed.

The include and Dependencies

When make encounters an include directive, it expands the wildcards and variable references, then tries to read the include file. If the file exists, execution continue normally. If the file does not exist, however, make reports the problem and continues reading through the makefile. After reading through the file, make searches the rules database for any rule to update the include files. If a match is found, make follows the normal process for updating a target. If an include file is updated by a rule, make then clears its internal database and rereads the entire makefile. If, after reading, updating, and rereading, there are still include directives that have failed due to missing files, make terminates with an error status.

The next example shows this process in action with two files. We use the warning built-in function to print a simple message from make. (This and other functions are described in Chapter 4.) Here is the makefile:

# Simple makefile including a generated file.
include foo.mk
$(warning Finished include)
foo.mk: bar.mk
        m4 --define=FILENAME=$@ bar.mk > $@

and here is bar.mk, the source for the included file:

# bar.mk - Report when I am being read.
$(warning Reading FILENAME)

The output looks like this:

$ make
Makefile:2: foo.mk: No such file or directory
Makefile:3: Finished include
m4 --define=FILENAME=foo.mk bar.mk > foo.mk
foo.mk:2: Reading foo.mk
Makefile:3: Finished include
make: `foo.mk' is up to date.

The first line shows that make cannot find the include file, but the second line shows that make keeps reading and executing the makefile. After completing the read, make discovers a rule to create the include file, foo.mk, and it does so. Then make starts the whole process again, this time without encountering any difficulty reading the include file.

The current makefile itself is a possible target. After the entire makefile has been read, make will look for a rule to remake the currently executing makefile. If it finds one, make will process the rule, then check if the makefile has been updated. If so, make will clear its internal state and reread the makefile, performing the whole analysis over again. Here is a silly example for an infinite loop based on this behavior:

.PHONY: dummy
makefile: dummy
        touch $@

When make executes this makefile, it sees that the makefile is out of date (because the .PHONY target, dummy, is out of date) so it executes the touch command, which updates the timestamp for the makefile. Then make rereads the file and discovers that the makefile is out of date... Ad infinitum.

Where does make look for included files? If the argument to include is an absolute file reference, make reads that file. If the file reference is relative, make first looks in its current working directory. If make cannot find the file, it then proceeds to search through any directories specified on the command line using the --include-dir (or -I) option. After that, make searches a compiled search path similar to: /usr/local/include, /usr/gnu/include, /usr/include. There may be slight variations of this path due to the way make was compiled.

If make cannot find the include file and it cannot create it using a rule, make exits with an error. If you want make to ignore include files it cannot load, add a leading dash to the include directive:

-include i-may-not-exist.mk

For compatibility with other makes , the word sinclude is an alias for -include.

Standard make Variables

Some standard variables for make are:

  1. MAKE_VERSION The revision for GNU make.
  2. CURDIR The current working directory.
  3. MAKEFILE_LIST List for each file read including all makefiles.
  4. MAKECMDGOALS List for all targets specified on the command-line.
  5. .VARIABLES List for all variables defined so far during execution.
Figure 2.2


Functions - Chapter 4

61/67

Internal Links

Parent Article: Main Page