Noeud:Make Variables, Noeud « Next »:Make Comments, Noeud « Previous »:Make Rules, Noeud « Up »:The Makefile
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.