2.1 Explicit Rules

     

Most rules you will write are explicit rules that specify particular files as targets and prerequisites. A rule can have more than one target. This means that each target has the same set of prerequisites as the others. If the targets are out of date, the same set of actions will be performed to update each one. For instance:

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

This indicates that both vpath.o and variable.o depend on the same set of C header files. This line has the same effect as:

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

The two targets are handled independently. If either object file is out of date with respect to any of its prerequisites (that is, any header file has a newer modification time than the object file), make will update the object file by executing the commands associated with the rule.

A rule does not have to be defined "all at once." Each time make sees a target file it adds the target and prerequisites to the dependency graph. If a target has already been seen and exists in the graph, any additional prerequisites are appended to the target file entry in make 's dependency graph. In the simple case, this is useful for breaking long lines naturally to improve the readability of the makefile :

 vpath.o: vpath.c make.h config.h getopt.h gettext.h dep.h vpath.o: filedef.h hash.h job.h commands.h variable.h vpath.h 

In more complex cases, the prerequisite list can be composed of files that are managed very differently:

 # Make sure lexer.c is created before vpath.c is compiled. vpath.o: lexer.c ... # Compile vpath.c with special flags. vpath.o: vpath.c         $(COMPILE.c) $(RULE_FLAGS) $(OUTPUT_OPTION) $< ... # Include dependencies generated by a program. include auto-generated-dependencies.d 

The first rule says that the vpath.o target must be updated whenever lexer.c is updated (perhaps because generating lexer.c has other side effects). The rule also works to ensure that a prerequisite is always updated before the target is updated. (Notice the bidirectional nature of rules. In the "forward" direction the rule says that if the lexer.c has been updated, perform the action to update vpath.o . In the "backward" direction, the rule says that if we need to make or use vpath.o ensure that lexer.c is up to date first.) This rule might be placed near the rules for managing lexer.c so developers are reminded of this subtle relationship. Later, the compilation rule for vpath.o is placed among other compilation rules. The command for this rule uses three make variables . You'll be seeing a lot of these, but for now you just need to know that a variable is either a dollar sign followed by a single character or a dollar sign followed by a word in parentheses. (I will explain more later in this chapter and a lot more in Chapter 3.) Finally, the .o / .h dependencies are included in the makefile from a separate file managed by an external program.

2.1.1 Wildcards

A makefile often contains long lists of files. To simplify this process make supports wildcards (also known as globbing ). make 's wildcards are identical to the Bourne shell's: ~ , * , ? , [...] , and [^...] . For instance, *.* expands to all the files containing a period. A question mark represents any single character, and [...] represents a character class . To select the " opposite " (negated) character class use [^...] .

In addition, the tilde ( ~ ) character can be used to represent the current user's home directory. A tilde followed by a user name represents that user's home directory.

Wildcards are automatically expanded by make whenever a wildcard appears in a target, prerequisite, or command script context. In other contexts, wildcards can be expanded explicitly by calling a function. Wildcards can be very useful for creating more adaptable makefile s. For instance, instead of listing all the files in a program explicitly, you can use wildcards: [1]

[1] In more controlled environments using wildcards to select the files in a program is considered bad practice because a rogue source file might be accidentally linked into a program.

 prog: *.c         $(CC) -o $@ $^ 

It is important to be careful with wildcards, however. It is easy to misuse them as the following example shows:

 *.o: constants.h 

The intent is clear: all object files depend on the header file constants.h , but consider how this expands on a clean directory without any object files:

 : constants.h 

This is a legal make expression and will not produce an error by itself, but it will also not provide the dependency the user wants. The proper way to implement this rule is to perform a wildcard on the source files (since they are always present) and transform that into a list of object files. We will cover this technique when we discuss make functions in Chapter 4.

Finally, it is worth noting that wildcard expansion is performed by make when the pattern appears as a target or prerequisite. However, when the pattern appears in a command, the expansion is performed by the subshell. This can occasionally be important because make will expand the wildcards immediately upon reading the makefile , but the shell will expand the wildcards in commands much later when the command is executed. When a lot of complex file manipulation is being done, the two wildcard expansions can be quite different.

2.1.2 Phony Targets

Until now all targets and prerequisites have been files to be created or updated. This is typically the case, but it is often useful for a target to be just a label representing a command script. For instance, earlier we noted that a standard first target in many makefile s is called all . Targets that do not represent files are known as phony targets . Another standard phony target is clean :

 clean:         rm -f *.o lexer.c 

Normally, phony targets will always be executed because the commands associated with the rule do not create the target name.

It is important to note that make cannot distinguish between a file target and phony target. If by chance the name of a phony target exists as a file, make will associate the file with the phony target name in its dependency graph. If, for example, the file clean happened to be created running make clean would yield the confusing message:

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

Since most phony targets do not have prerequisites, the clean target would always be considered up to date and would never execute.

To avoid this problem, GNU make includes a special target, .PHONY , to tell make that a target is not a real file. Any target can be declared phony by including it as a prerequisite of .PHONY :

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

Now make will always execute the commands associated with clean even if a file named clean exists. In addition to marking a target as always out of date, specifying that a target is phony tells make that this file does not follow the normal rules for making a target file from a source file. Therefore, make can optimize its normal rule search to improve performance.

It rarely makes sense to use a phony target as a prerequisite of a real file since the phony is always out of date and will always cause the target file to be remade. However, it is often useful to give phony targets prerequisites. For instance, the all target is usually given the list of programs to be built:

 .PHONY: all all: bash bashbug 

Here the all target creates the bash shell program and the bashbug error reporting tool.

Phony targets can also be thought of as shell scripts embedded in a makefile . Making a phony target a prerequisite of another target will invoke the phony target script before making the actual target. Suppose we are tight on disk space and before executing a disk- intensive task we want to display available disk space. We could write:

 .PHONY: make-documentation make-documentation:         df -k .  awk 'NR =  = 2 { printf( "%d available\n", $ ) }'         javadoc ... 

The problem here is that we may end up specifying the df and awk commands many times under different targets, which is a maintenance problem since we'll have to change every instance if we encounter a df on another system with a different format. Instead, we can place the df line in its own phony target:

 .PHONY: make-documentation make-documentation: df         javadoc ... .PHONY: df df:         df -k .  awk 'NR =  = 2 { printf( "%d available\n", $ ) }' 

We can cause make to invoke our df target before generating the documentation by making df a prerequisite of make-documentation . This works well because make-documentation is also a phony target. Now I can easily reuse df in other targets.

There are a number of other good uses for phony targets.

The output of make can be confusing to read and debug. There are several reasons for this: makefile s are written top-down but the commands are executed by make bottom-up; also, there is no indication which rule is currently being evaluated. The output of make can be made much easier to read if major targets are commented in the make output. Phony targets are a useful way to accomplish this. Here is an example taken from the bash makefile :

 $(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP)         $(RM) $@         $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS)         ls -l $(Program)         size $(Program) .PHONY: build_msg build_msg:         @printf "#\n# Building $(Program)\n#\n" 

Because the printf is in a phony target, the message is printed immediately before any prerequisites are updated. If the build message were instead placed as the first command of the $(Program) command script, then it would be executed after all compilation and dependency generation. It is important to note that because phony targets are always out of date, the phony build_msg target causes $(Program) to be regenerated even when it is not out of date. In this case, it seems a reasonable choice since most of the computation is performed while compiling the object files so only the final link will always be performed.

Phony targets can also be used to improve the "user interface" of a makefile . Often targets are complex strings containing directory path elements, additional filename components (such as version numbers ) and standard suffixes. This can make specifying a target filename on the command line a challenge. The problem can be avoided by adding a simple phony target whose prerequisite is the actual target file.

By convention there are a set of more or less standard phony targets that many makefile s include. Table 2-1 lists these standard targets.

Table 2-1. Standard phony targets

Target

Function

all

Perform all tasks to build the application

install

Create an installation of the application from the compiled binaries

clean

Delete the binary files generated from sources

distclean

Delete all the 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


The target TAGS is not really a phony since the output of the ctags and etags programs is a file named TAGS . It is included here because it is the only standard nonphony target we know of.

2.1.3 Empty Targets

Empty targets are similar to phony targets in that the target itself is used as a device to leverage the capabilities of make . Phony targets are always out of date, so they always execute and they always cause their dependent (the target associated with the prerequisite) to be remade. But suppose we have some command, with no output file, that needs to be performed only occasionally and we don't want our dependents updated? For this, we can make a rule whose target is an empty file (sometimes referred to as a cookie):

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

Notice that the size rule uses touch to create an empty file named size after it completes. This empty file is used for its timestamp so that make will execute the size rule only when prog.o has been updated. Furthermore, the size prerequisite to prog will not force an update of prog unless its object file is also newer.

Empty files are particularly useful when combined with the automatic variable $? . We discuss automatic variables in Section 2.2.1, but a preview of this variable won't hurt. Within the command script part of a rule, make defines the variable $? to be the set of prerequisites that are newer than the target. Here is a rule to print all the files that have changed since the last time make print was executed:

 print: *.[hc]         lpr $?         touch $@ 

Generally, empty files can be used to mark the last time a particular event has taken place.



Managing Projects with GNU make
Managing Projects with GNU Make (Nutshell Handbooks)
ISBN: 0596006101
EAN: 2147483647
Year: 2003
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net