Noeud:Make Variables, Noeud « Next »:, Noeud « Previous »:Make Rules, Noeud « Up »:The Makefile



Make Variables

The next basic building block used when creating a Makefile is a variable assignment. Assigning a value to a variable1 within a Makefile looks just as you would expect:

     variable-name = value
     
     Example 5.8: Format of Make variable assignment
     

Where variable-name is any string of characters not containing whitespace or other characters that are special to Make: = is used to separate a variable's name from its value, : is used to mark the end of a target name in a dependency rule, and # is used to begin a comment (voir Make Comments), so you cannot use any of those three characters in the variable-name itself. Obvious problems with readability aside, if you want your Makefile to work with other implementations of Make, you should probably limit yourself to variable-names made from upper and lower case letters, the digits 0 to 9 and the underscore character.

The value consists of the remaining characters up to the end of the line, or the first # character encountered - whichever comes first. Also, any leading or trailing whitespace around value does not become part of the variable's contents.

A variable assignment can extend across several lines if necessary by placing a \ at the end of each unfinished line:

     libm4_a_SOURCES         = builtin.c debug.c error.c eval.c hash.c \
                               input.c ltdl.c macro.c module.c output.c \
                               path.c regex.c symtab.c utility.c
     
     Example 5.9: Extending a Make variable assignment over several lines
     

Whenever a continuation backslash is encountered in a variable declaration like this, the backslash itself and all of the whitespace that surrounds it - including all of the leading whitespace on the following line - is deleted and replaced by a single space as it is assigned to variable-name.

Make will expand variable references by using the value currently stored in the variable where ever the following syntax is used:

     $(variable-name)
     
     Example 5.10: Format of Make variable expansion
     

By convention, the name of any external command used in a Makefile, and any option required by such a command is stored in a variable. For example, you would use $(CC) and $(CFLAGS) rather than writing gcc and -ggdb3 directly into the rules. Rewriting our Makefile to employ variables looks like this:

     CC       = gcc
     CFLAGS   = -ggdb3
     CPPFLAGS = -DDEBUG=1
     
     m4_OBJECTS = main.o freeze.o stackovf.o
     m4_LDADD   = ../lib/libm4.a
     
     m4: $(m4_OBJECTS) $(m4_LDADD)
             $(CC) $(CFLAGS) -o m4 $(m4_OBJECTS) $(m4_LDADD) $(LIBS)
     
     main.o: main.c
             $(CC) $(CFLAGS) $(CPPFLAGS) -c main.c
     
     freeze.o: freeze.c
             $(CC) $(CFLAGS) $(CPPFLAGS) -c freeze.c
     
     stackovf.o: stackovf.c
             $(CC) $(CFLAGS) $(CPPFLAGS) -c stackovf.c
     
     Example 5.11: Use of variables in a Makefile
     

This provides an obvious advantage: you can change the contents of a variable in one place, but affect all of the rules that use the variable. example 5.11 is set up to compile with debugging symbols (-ggdb3), and with additional debugging code enabled by the preprocessor (-DDEBUG=1). This is ideal while the code is under development. Later, when a project is ready to be released, it is very easy to change the variable assignments to compile a production version of the code. You simply change the values of these variables at their declaration near the top of the Makefile:

     CC       = gcc
     CFLAGS   = -O2
     CPPFLAGS =
     ...
     
     Example 5.12: Changing variable values in a Makefile
     

Actually, Make's internal database contains default definitions for many of these variables already, and uses them for the command part of its default rules. The default setting for $(CC) was used earlier, when we compiled hello.c without a Makefile in Targets and Dependencies. If you make use of any variable in your own commands, you should not fall into the habit of relying on there being a useful default definition built in to Make - one of your customers will probably try to run your Makefile through a version of make that has a different value, which breaks the assumptions you made. If you use $(CPPFLAGS) in your own rules, define CPPFLAGS somewhere in your Makefile, even if there is nothing ini it!

In any case, references to other Make variables in the value of a Make variable declaration are not expanded until they are actually needed. In other words you cannot work out the eventual result of a variable expansion from the order in which things are declared. At first glance, it looks as though the command in the m4 target in example 5.13 will execute gcc:

     LD       = gcc $(CFLAGS)
     LDFLAGS  = -static
     LINK     = $(LD) $(LDFLAGS)
     ...
     m4: $(m4_OBJECTS)
             $(LINK) -o m4 $(m4_OBJECTS) $(m4_LDADD) $(LIBS)
     
     LD = ld
     
     Example 5.13: Make variables are not expanded until they are used
     

Not so. Until the moment the value of $(LINK) is needed in the command associated with the m4 target, the values of $(LD) and $(LDFLAGS) are not expanded. By the time this happens, $(LD) contains a different value to what it held when LINK was declared:

     $ make m4
     ld -static -o m4 main.o freeze.o stackovf.o ../m4/libm4.a
     

So, as Make expands the variables in the command before passing them to the shell for execution, $(LINK) unrolls like this:

     $(LINK)
     ==> $(LD) $(LDFLAGS)
     => ld -static
     

As a consequence, there can be no loops in variable references, even indirectly, otherwise Make would get stuck in an infinite recursion loop. GNU Make is able to detect such loops, and will diagnose them for you. For example, here is a Makefile with a reference loop:

     FOO = foo $(BAR)
     BAR = $(FOO) bar
     all:
             echo $(FOO)
     
     Example 5.14: A Makefile with a variable reference loop
     

When asked to refresh the target, GNU Make bails out with the following error message:

     $ make
     Makefile:4: *** Recursive variable `FOO' references
     Makefile:4:     itself (eventually).  Stop.
     

While preparing the command (at line 4 of the Makefile) for processiong, Make will try to expand variable references, and notice that there are still unexpanded variables with a variable-name that has already been expanded:

     echo $(FOO)
     ==> echo foo $(BAR)
     ==> echo foo $(FOO) bar
     

Usually, it is very easy to remove the circular reference once GNU Make has given you the variable-name and Makefile line number where it detected the loop.


Notes de bas de page

  1. Other text books refer to Make variables as macros, but that term has fallen out of favour. To a computer scientist, the word macro describes something quite different to the functionality of Make variables.