Noeud:Suffix Rules, Noeud « Next »:, Noeud « Up »:Special Targets



Suffix Rules

The Makefile in example 5.11 has a dependency rule for the compilation of each of the source files in the source directory. Remembering to add a new rule to the Makefile each time a new source file is added to the project would be an unwanted distraction from the real work of writing code. Accordingly, Make offers a way to specify how groups of similar files are kept up to date, which is especially useful in cases like this where the commands for each rule are almost identical.

Make will determine when to apply these rules based on the file suffix that follows the last dot in the pertinent file name. The explicit rules all follow this template:

     main.o: main.c
             $(CC) $(CFLAGS) $(CPPFLAGS) -c main.c
     
     Example 5.19: A Make rule for compiling main.c
     

The relationship between the target, main.o, and its dependency, main.c is characterised by the suffixes .o and .c. An equivalent suffix rule, which can also be used to update any target with a .o suffix from an existing .c suffixed filename is as follows:

     .c.o:
             $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
     
     Example 5.20: A suffix rule for compiling C source files
     

Confusingly, if you compare example 5.19 and example 5.20, the order of the suffixes to the left of the : above, is reversed with respect to how they would appear in the earlier explicit rule. This takes some getting used to, but follows the precedent set by prehistoric implementations of the Make utility. The odd looking variable reference, $< refers to the .c file that matched the rule, and is explained fully in Automatic Variables.

Suffix rules take a very similar form to normal dependency rules. However, the target-suffix follows the dependent-suffix immediately with no separation:

     dependent-suffixtarget-suffix:
             command
             ...
     
     Example 5.21: Format of a double suffix rule
     

Notice that unlike example 5.3 there is nowhere to list dependencies in a suffix rule. As a matter of fact, Make will not interpret a rule that does list dependencies as a suffix rule; it will be treated as a dependency rule with a target named, say, .c.o. GNU Make already knows about many of the common suffixes like .c and .o, and in fact has a fairly exhaustive list of suffixes and default suffix rules declared for you, as explained in Invoking Make. It uses this knowledge to distinguish between a suffix rule for generating .o files from similarly named .c files, and a standard dependency rule to create a wierd target file named .c.o. You must declare any additional suffixes used by the suffix rules in your Makefile. Make uses a special target named .SUFFIXES for this, like so:

     .SUFFIXES: .c .o
     

The default suffix list is a double edged sword though, and will occasionally elicit surprising behaviour from Make when some of your files happen to match one of the predeclared suffix rules. We advocate the principle of least surprise1, and as such recommend enabling only the suffix rules that are specifically required by each individual Makefile. First, empty the suffix list with an empty .SUFFIXES target, and then declare the suffixes you actually need in a second .SUFFIXES target.

This works because, unlike normal targets, when .SUFFIXES appears multiple times within a Makefile, the dependencies for each new appearance are added to a growing list of known file suffixes. Except that at any point .SUFFIXES is written with no dependencies, this growing list is reset by removing every suffix it contains - subsequent appearances will then be able to add more suffixes back in to the list.

Applying these idioms to our Makefile, we now have:

     CC       = gcc
     CFLAGS   = -ggdb3
     CPPFLAGS = -DDEBUG=1
     
     m4_OBJECTS = main.o freeze.o stackovf.o
     m4_LIBS    = ../lib/libm4.a
     
     .SUFFIXES:
     .SUFFIXES: .c .o
     
     m4: $(m4_OBJECTS) $(m4_LIBS)
             $(CC) $(CFLAGS) -o m4 $(m4_OBJECTS) $(m4_LDADD) $(LIBS)
     
     .c.o:
             $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
     
     Example 5.22: The complete Makefile with suffix rule
     

In general, you will need a mixture of suffix rules and target dependency rules to describe the entire build process required to transform your source code into an executable. Sometimes you might want to use a single suffix rule for the vast majority of your files, but make an exception for an odd one or two.

For example, one set CPPFLAGS might be fine for most of your C compilation, but you need to add an additional -DDEBUG=1 option to one or two special files:

     CC       = gcc
     CFLAGS   = -ggdb3
     CPPFLAGS =
     
     ...
     
     .c.o:
             $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
     
     stackovf.o: stackovf.c
             $(CC) $(CFLAGS) $(CPPFLAGS) -DDEBUG=1 -c stackovf.c
     
     Example 5.23: Compiling some files with different options
     

Here, the files compiled by the suffix rule use only the options specified in the variables, but stackovf.o is compiled by a specific rule which explicitly adds -DDEBUG=1. By listing the file that needs different options as a specific target, that rule overrides the suffix rule in that case.

Let's see this in action:

     $ rm *.o
     $ make m4
     gcc -ggdb3  -c main.c
     gcc -ggdb3  -c freeze.c
     gcc -ggdb3  -DDEBUG=1 -c stackovf.c.c
     gcc -o m4 main.o freeze.o stackovf.o ../lib/libm4.a
     

Notes de bas de page

  1. Complex systems are much easier to understand if they always do what you would expect.