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
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 test
The variable-name should not be surrounded by $( ) for the ifdef / ifndef test. Finally, the test can be expressed as either of:
" a " " b " ( a , b )
in which single or double quotes can be used interchangeably (but the quotes you use must match).
The conditional processing directives can be used within macro definitions and command scripts as well as at the top level of makefile s:
libGui.a: $(gui_objects) $(AR) $(ARFLAGS) $@ $< ifdef RANLIB $(RANLIB) $@ endif
I like to indent my conditionals, but careless indentation can lead to errors. In the preceding lines, the conditional directives 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 form of the test, whitespace after the comma is ignored, but all other whitespace is significant:
ifeq (a, a) # These are equal endif ifeq ( b, b ) # These are not equal - ' b' != 'b ' endif
Personally, I stick with the quoted forms of equality:
ifeq "a" "a" # These are equal endif ifeq 'b' 'b' # So are these endif
Even so, it often occurs that a variable expansion contains unexpected whitespace. This can cause problems since the comparison includes all characters . To create more robust makefile s, use the strip function:
ifeq "$(strip $(OPTIONS)) "-d" COMPILATION_FLAGS += -DDEBUG endif
3.7.1 The include Directive
We first saw the include directive in Chapter 2, in Section 2.7. Now let's go over it in more detail.
A makefile can include other files. This is most commonly done to place common make definitions in a make header file or to include automatically generated dependency information. The include directive is used like this:
The directive can be given any number of files and shell wildcards and make variables are also allowed.
3.7.2 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, we continue normally. If the file does not exist, however, make reports the problem and continues reading the rest of the makefile . When all reading is complete, make looks in 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 any of the include files is updated by a rule, make then clears its internal database and rereads the entire makefile . If, after completing the process of reading, updating, and rereading, there are still include directives that have failed due to missing files, make terminates with an error status.
We can see this process in action with the following two-file example. We use the warning built-in function to print a simple message from make . (This and other functions are covered in detail 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)
When run, we see:
$ 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.
Now is a good time to mention that make will also treat the makefile itself as 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 of 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 of the makefile . Then make rereads the file and discovers that the makefile is out of date. . . . Well, you get the idea.
Where does make look for included files? Clearly, 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 you have 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:
For compatibility with other make s, the word sinclude is an alias for -include .