How to use variables in makepp
define,
dependencies,
dependency,
foreach,
input,
inputs,
output,
outputs,
sorted_dependencies,
sorted_inputs,
stem,
target,
targets,
=
,
:=
,
+=
,
?=
,
!=
,
$@
,
$^
,
$*
,
$+
,
$?
,
$<
Makefiles typically use variables in many places. One important reason for using variables is to ensure that information is contained in only one place in the makefile, so that if it changes, there is no danger of the two copies of the information getting out of sync.
Variable names are case sensitive. In theory, variable names can be made of
any characters, but you will probably confuse the makefile parser if you do
anything other than alphanumeric characters, _
, and -
.
Each makefile has its own set of variables, and setting a variable in one makefile will have no effect on its value in any other makefile. If you want to have variables set in many makefiles, the best way to do it is to have each of them include a common definitions file (see the include statement).
A variable can assume a value in several different ways:
A variable may be set inside a makefile. There are a number of different ways to do this; see below.
A variable's value may be specified on the command line, like this:
makepp CFLAGS=-O2 my_program
If more than one makefile is loaded, the CFLAGS variable is propagated to all of the makefiles. Variables set on the command line automatically override any setting of the variable in any of the makefiles.
If a variable is set in the environment, it can be referenced as a makepp
variable. Ordinarily assignments to variables inside a makefile override
settings from the environment, but you can change this by using the -e
or
--environment-overrides
command line option.
Variables are assigned with one of several assignment expressions, like this
X = 1 MODULES := a b c d CC ?= gcc CFLAGS += -Wall define VAR var line 1 var line 2 enddef
Leading and trailing whitespace around values is always stripped off.
Variable values are always stored as ordinary perl scalars, so you can access them directly from perl code if you need to do any complicated manipulations with them; see the makepp_extending manpage for details.
The different assignment operators have somewhat different meanings.
VARIABLE = text string
This is the usual assignment statement that all implementations of make
support. The expression on the right hand side is not evaluated until the
value of $(VARIABLE)
is actually used somewhere. Thus, if you do the
following:
X = 1 Y = $(X) X = 2
Then $(Y)
later in the makefile will evaluate to "2".
In general, you usually want to use :=
(see below) instead of =
because it provides more predictable variable evaluation. However,
there are times when you need to defer the variable evaluation. Also,
if you're writing a makefile that must be backwards-compatible with some
version of make other than GNU make, then you have no choice: you may
only use =
.
VARIABLE := expr
This is the same as VARIABLE = expr
except that the right hand
side is evaluated at the time of the assignment. Thus if
X := 1 Y := $(X) X := 2
then $(Y)
later in the makefile will evaluate to "1" since that's what
$(X)
was when $(Y)
was defined.
VARIABLE += expr
Appends the string to the previous contents of the variable, separated by a
space. If the variable was previously assigned with :=
, then the right
hand side is evaluated before appending.
VARIABLE ?= expr
Sets the value of the variable, but only if the variable is not specified earlier in the makefile, on the command line, or in the environment. The above assignment is exactly equivalent to
ifndef VARIABLE VARIABLE = expr endif
VARIABLE != shell command
Runs the shell command and sets the variable to contain the command's standard output. This is exactly equivalent to
VARIABLE := $(shell command)
define VARIABLE first line of variable's value second line of variable's value third line of variable's value enddef
If you need a variable's value to contain newlines, you must use the define statement as shown (or you can assign the value directly in perl). This is primarily useful for "canned command sequences", e.g., something like this:
define COMPILE_C_PROGRAM @echo "Compiling $(input)" @$(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -c $(input) -o $(output) endef
Then you can use this multi-line variable in several rules, like this:
%.o : %.c $(COMPILE_C_PROGRAM)
$(ARCH)/%.o : $(ARCH)/%.c $(COMPILE_C_PROGRAM)
Note that you can often achieve the same effect by using a semicolon instead of a newline, because the shell interprets that as a command delimeter too. For example,
COMPILE_C_PROGRAM = @echo "Compiling $(input)"; \ $(CC) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -c $(input) -o $(output)
will have the same effect.
Note that variables assigned a value with define
are treated as if
they were assigned with =
and not :=
, i.e., expansion of
references to other variables is deferred until the variable is
referenced.
target : VARIABLE = string target : VARIABLE := string target : VARIABLE += string
Sets a target-specific value of the variable. A target-specific value is in effect only in an action which produces the given target. This is primarily used for things like this:
CFLAGS := -O2 my_prog: file1.o file2.o special_file.o special_file.o : CFLAGS := -g %.o: %.c $(CC) $(CFLAGS) -c $(input) -o $(output)
What happens here is that all .c
files will be compiled with
optimization (-O2
) except special_file.c
, which is compiled
in debug mode (-g
). This is a convenient way to specify different
compilation options for only a few files.
Target-specific variable assignments like this apply only to the actions of the rule; they are not in effect when evaluating the targets or the dependencies of a rule. If a rule has more than one target, target-specific variable assignents are taken only from the first target. Also note that makepp's target-specific variables are slightly different from GNU make's in that they only apply to the rule for the one file mentioned, and not to any of its predecessors; see makepp_incompatibilities for details.
Wildcard expansion is performed on the target, so you can do something like this:
test_*.o : CFLAGS += -DTEST
For compatibility with GNU make, %
may be used in place of *
.
Makepp's variable substitution rules are similar to those of other
makes, but somewhat more powerful. As in all makes, $(CC)
or
${CC}
both represent the value of the variable CC. If you need a
literal dollar sign, put in a double dollar sign ($$
), like this:
target: dep1 dep2 dep3 dep4 $(RM) -f $(output) for file in $(inputs); do cat $$file >> $(output); done
By default, makepp uses rc-style substitution (so called because it was pioneered by the rc shell). This is best illustrated by an example:
MODULES = a b c d mylib.a : module_dir/$(MODULES).o $(OTHER_OBJECTS) $(CXX) $(dependencies) -o $(target)
The prefix module_dir/
is prepended to each word in MODULES, and the suffix
.o
is appended to each word.
You can also use rc-style substitution without even putting the list of words
into a variable; the syntax is $( word1 word2)
. Note the space between
the parenthesis and the first word. So the above example could have been
written as:
mylib.a : module_dir/$( a b c d).o $(OTHER_OBJECTS) $(CXX) $(dependencies) -o $(target)
If you put several variables in the same word which expand to arrays of words, rc-style substitution actually takes the cartesian product, so you can do something like this if you want:
DIRS = s1 s2 MODULES = a b c SUFFIXES = .o .c FILES := $(DIRS)/$(MODULES)$(SUFFIXES)
and FILES will contain the string
s1/a.o s1/a.c s1/b.o s1/b.c s1/c.o s1/c.c s2/a.o s2/a.c s2/b.o s2/b.c s2/c.o s2/c.c
If rc-style substitution gets in the way, or if you need to have leading or
trailing whitespace in your make variables, then you can turn off rc-style
substitution with the --norc-substitution
command line option. You can
also turn it off on a per-makefile basis by setting the variable
rc_substitution=0
in your makefile. You should do this near the top of the
makefile, or else you may run into funny situations where rc-style
substitution is used for some evaluations and not others. (All expressions
evaluated before the assignment will use rc-style substitutions, and all
expressions evaluated after will not. Since the time of evaluation of
expressions in makefiles is complicated and not always obvious from the order
of statements in the makefile, it's best to set rc_substitution
as early as
possible.)
A substitution reference has the form $(VAR:A=B)
, where A is a pattern to
match and B is a pattern to replace it with. For example:
source_files = a.c b.c c.c d.c object_files = $(source_files:%.c=%.o)
will set $(object_files)
to a.o b.o c.o d.o
. The %
is a special
character matches any arbitrary string. Substitution references are an
abbreviation fot the patsubst
function.
If you need to control the whitespace in a variable, you must (currently)
disable rc-style substitution (using the command line option
--norc-substitution
or by setting rc_substitution:=0
in the makefile)
and then use a syntax like this:
null = T = -o $(null)
When you do this, the variable T
contains -o
followed by a space.
This kind of a technique to handle whitespace is not recommended. If
you need variables in your makefile to contain spaces, you should think
seriously about what you're doing. If you need to handle spaces, it is
usually much better to put perl code into your makefile to take care of
it (using the perl_begin
or sub
statements), or to handle it in
shell statements in the actions of rules.
These cases typically come up when people attempt to use the same rules for different architectures which do not use typical unix command syntax. E.g., sometimes one sees things like this in makefiles:
ifeq ($(ARCH),weirdarch) O := /OUTPUT= else null := O := -o $(null) fi %.o : %.c $(COMPILER) $(input) $(O)$(output)
You can do this with makepp if you really want to, but you will probably find that your makefiles are substantially more readable if you have less complicated variable stubstitution, e.g.,
ifeq ($(ARCH),weirdarch) %.o : %.c $(WEIRD_COMPILER) $(input) /OUTPUT=$(output) else %.o : %.c $(CC) -c $(input) -o $(output) fi
Whitespace is never allowed in variable names, only in their values. This is different from some make implementations.
Automatic variables are variables that assume different values depending
on which rule they are evaluated in. Makepp supports most of the
automatic variables that other versions of make use. In addition, it
has less cryptic, longer names for most of them that you can use
instead. (For legacy makefiles that happen to redefine these names, the
definition in the makefile overrides the default meaning. For example,
if you say target = abc
in your makefile, then $(target)
will
always expand to abc
, and will no longer be equivalent to $@
.)
The following is a complete list of all the automatic variables that makepp supports:
The target of the current rule. Actually, since makepp supports multiple targets for any rule, this is the first target. For example, in the following rule
y.tab.c y.tab.h : parser.y $(YACC) $(YFLAGS) $(input)
$(output)
will contain the value y.tab.c.
All targets of the current rule. Same as $(target)
unless there is
more than one target. In the above example, $(outputs)
will be
y.tab.c y.tab.h.
The first explicit dependency of the rule. For example, in this rule
%.o : %.c $(CC) $(CFLAGS) -c $(input) -o $(output)
$(input)
will be the name of the .c file, regardless of what .h
files makepp discovers.
All the explicit dependencies of the target, not including .h files discovered by the makepp_scanning manpage for includes.
For example, in the rule
myprog.o : *.o $(CC) $(CFLAGS) $(inputs) -o $(output)
$(inputs)
will be all the .o files in the directory.
All the dependencies of the target, in sorted order, with duplicates
removed. Equivalent to $(sort $(inputs))
.
In most make implementations, this is the dependencies of the target
which have changed. This does not fit into makepp's conceptual
framework, because on the next build it will make sure the command is
exactly identical to the previous build, or else it considers the file
out of date. So makepp attempts to support this variable for backward
compatibility by making it equivalent to all dependencies (the same as
$+
).
This variable has no long name because its use is highly discouraged. Ordinarily it is used in commands like this:
libmine.a : $(MODULES) $(AR) ru $@ $?
i.e., ar is told to replace only those modules that have changed. Makepp doesn't like this because it means that the command to build the library is different on each build, and makepp cannot guarantee that the builds are correct. In fact, this can often lead to incorrect builds: if you delete a source file, for example, the .o file will still be in the library. It's better to build a library like this:
libmine.a : $(MODULES) $(RM) -f $(output) $(AR) cr $(output) $(inputs)
because then it is guaranteed that there are no stale modules inside the library.
The stem in a pattern rule (i.e., whatever the '%' matched).
Alternatively, if this is not a pattern rule, returns the file name
without the extension (i.e., it's equivalent to
$(basename $(input))
.
This is mostly for backward compatibility. For example, in old versions of make the only way to tell it how to compile any .c file into the corresponding .o file was like this:
.c.o: $(CC) $(CFLAGS) -c $*.c -o $*.o
This is a lousy way to write the rule. It's much clearer to use GNU-make style pattern rules, like this:
%.o : %.c $(CC) $(CFLAGS) -c $(input) -o $(output)
The current filename from the foreach
clause. foreach
clauses are
rarely used, but they are the most general-purpose kind of pattern rule
that makepp supports. For example,
# # Build .c files with some sort of a special preprocessor: # %.c : %.k $(preprocessor) $(input) > $(output) # # Compile .c files into .o files: # %.o : %.c $(CC) $(CFLAGS) -c $(input) -o $(output) # # Special alternate compilation flags for .c files which are derived # from .k files: # $(foreach:%.k=%.o) : $(foreach:%.k=%.c) : foreach *.k $(CC) $(SPECIAL_K_FLAGS) -c $(input) -o $(output)
See the documentation on the foreach clause in rules for more details and examples.
Gary Holt (holt-makepp@gholt.net)