Having one rule for each compilation command is fine when there are only a few files, but what if your program consists of dozens of source files? Most of them have to be compiled with very similar commands. It is tedious to type in a separate rule for each source file, and then if you decide to change the rules, you have to change the makefile in a dozen places. A better solution to this problem is to use a pattern rule.
A pattern rule is a concise way of specifying a rule for many
files at once. The rule will depend on the file names, but usually
it depends on them in a simple way. You specify a pattern by using
the %
wildcard. When present in the dependency list,
%
matches any string of any length; when present in the
list of targets, %
stands for the string that
%
in the dependency list matched.
The following pattern rule will take any .c
file and
compile it into a .o
file:
%.o: %.c $(CC) $(CFLAGS) $(INCLUDES) -c $(input) -o $(output)
(This assumes that you have the variables CC
,
CFLAGS
, and INCLUDES
defined to be
something suitable. Makepp will guess a value for
CC
and CFLAGS
.)
The first line of the rule says that it applies to every possible
input file that matches the pattern %.c
. These
.c
files can be transformed into the corresponding
.o
file using the specified actions.
The action of rule is quite similar to the other actions we've seen previously, except that it uses automatic variables. An automatic variable is a variable whose value is automatically set by makepp depending on the rule that it appears in. Some useful automatic variables are:
$(input)
%.c
pattern.
$(dependency)
is a synonymn for
$(input)
. In older makefiles, you will also see
the cryptic symbol $<
used as well.
$(output)
%.o
pattern.
$(target)
and $@
are synonymns.
$(inputs)
$(inputs)
is equivalent to
$(input)
. $(dependencies)
and
$^
are synonymns.
$(outputs)
$(outputs)
is equivalent
to $(output)
. $(targets)
is a
synonymn for $(outputs)
.
Note that these variables are lower case.
You can use these automatic variables even for non-pattern rules. This avoids repeating target filenames.
You can actually do considerably more complicated things with pattern rules. For example,
# Put the object files into a separate directory: objects/%.o: %.cpp $(CXX) $(CXXFLAGS) -c $(input) -o $(output) # Run a preprocessor to make source files: moc_%.cxx: %.h $(MOC) $(input) -o $(output)
Using pattern rules and automatic variables, we'd probably rewrite our makefile for our simple program like this:
CXX := c++ CXXFLAGS := -g INCLUDES := -I. # This would contain any -I options to the # compiler, if there are any. LIBS := -L/usr/X11R6/lib -lX11 # Contains libraries we need to link in. OBJECTS := processing.o gui.o my_program: $(OBJECTS) $(CXX) $(inputs) -o $(output) $(LIBS) %.o: %.cxx $(CXX) $(INCLUDES) $(CXXFLAGS) -c $(input) -o $(output)
Now we don't have to have an explicit rule for each object file
we need to produce. If we want to add another module to our
program, we only have to change the one line that defines the
OBJECTS
variable. Note that this makefile is now much
more concise than our original makefile. Each piece of information
occurs only once so there is no possibility of making a mistake by
changing information in one place and forgetting to change it in
others.
When you use pattern rules, it's not uncommon for there to be two different rules that can produce the same file. If both rules are pattern rules, then the one that occurs later in the makefile is actually used. If one rule is a pattern rule, and the other is an explicit rule (one that actually names the target file explicitly), then the explicit rule is used. This is often helpful if you want to compile most modules with the same command, but there is one module that needs slightly different compilation options, as shown in this makefile fragment:
CXXFLAGS := -g -O2 FAST_CXXFLAGS := -DNO_DEBUG -O6 -malign-double -funroll-all-loops %.o: %.cpp $(CXX) $(CXXFLAGS) -c $(input) -o $(output) time_critical_subs.o: time_critical_subs.cpp $(CXX) $(FAST_CXXFLAGS) -c $(input) -o $(output)
If you find yourself wanting to do something with patterns that
isn't expressed easily using the %
wildcard,
makepp has another syntax which is somewhat harder to read,
but considerably more powerful. See the :foreach clause for
more details.
Makepp actually has builtin rules for compiling C or C++ or Fortran code, which are available if you don't override them with your own rules. The builtin rules are almost identical to the examples above. Most makefiles contain pattern rules for compilation, but you can depend on the builtin rules if you want.