You want to use GNU make to build a simple “Hello, World” program, such as that in Example 1-4.
Before you write your first makefile, you’ll need to know a little terminology. A makefile consists of a collection of rules of the form
targets
:prerequisites
command-script
Here targets
and prerequisites
are space-separated strings, and command-script
consists of zero or more lines of text, each of which begins with a Tab character. Targets
and prerequisites are usually files names, but sometimes they are simply formal names for
actions for make to perform. The command script
consists of a sequence of commands to be passed to a shell. Roughly speaking, a rule tells
make to generate the collection of targets from the
collection of prerequisites by executing the command script.
Tip
Whitespace in makefiles is significant. Lines containing command scripts must begin with a Tab rather than a Space — this is a source of some of the most common beginner errors. In the following examples, lines which begin with a Tab are indicated by an indentation of four characters.
Now you’re ready to begin. Create a text file named makefile in the directory containing your source file. In this file, declare
four targets. Call the first target all
, and specify
the name of the executable you wish to build as its sole prerequisite. It should have no
command script. Give the second target the same name as your executable. Specify your
application’s source file as its prerequisite, and specify the command line needed to
build the executable from the source file as your target’s command script. The third
target should be called install
. It should have no
prerequisites, and should have a command script to copy the executable from the directory
containing the makefile to the directory where you want it installed. The last target
should be called clean
. Like install, it should have no
prerequisites. Its command script should remove the executable and the intermediate object
file from the current directory. The clean
and install
targets should both be labeled as
phonytargets, using the PHONY
attribute.
For example, to build an executable from the source code in Example 1-4 using GCC, your makefile might look as shown in Example 1-14.
Example 1-14. Makefile to build the executable hello with GCC
# This is the default target, which will be built when # you invoke make .PHONY: all all: hello # This rule tells make how to build hello from hello.cpp hello: hello.cpp g++ -o hello hello.cpp # This rule tells make to copy hello to the binaries subdirectory, # creating it if necessary .PHONY: install install: mkdir -p binaries cp -p hello binaries # This rule tells make to delete hello and hello.o .PHONY: clean clean: rm -f hello
To build an executable from the source code in Example 1-4 using Visual C++, you can use the following makefile shown in Example 1-15.
Tip
Commands and lists of targets or prerequisites can span more than one line of text in a makefile by using the continuation character \, just as in C++ source files.
To build your executable, set any environment variables required by your command-line
tools, change to the directory containing makefile
and enter make
. To copy your executable to the
binaries subdirectory, enter make
install
. To delete the executable and the
intermediate object file from the makefile directory, enter make
clean
.
Tip
If you have installed the Cygwin environment, described in Recipe Recipe 1.1, you can execute the makefile in Example 1-15 directly from the Windows shell cmd.exe.
Tip
You can also execute this makefile from the Cygwin shell, as follows. From cmd.exe, run vcvars32.bat to set Visual C++’s environment variables. Next, run
cygwin.bat to start the Cygwin shell. If you
place the Cygwin installation directory in your PATH
,
you can start the Cygwin shell from cmd.exe simply
by entering cygwin
. Finally, change to the
directory containing the makefile and enter make
.
Similarly, you can execute the makefile from the MSYS shell: run vcvars32.bat from cmd.exe, then run msys.bat to start the MSYS shell.
If your toolset provides a script to set environment variables, running a makefile from Cygwin or MSYS is slightly more involved than running it from cmd.exe. It’s necessary for some makefiles, however, since they simply won’t work from cmd.exe.
In the next few recipes, you’ll see that GNU make is a powerful tool for building complex projects. But what does it actually do? Here’s how it works. When make is invoked with no arguments, it looks in the current directory for a file named GNUmakefile, makefile or Makefile, and attempts to build the first target it contains, called the default target. If the default target is up to date — meaning that it exists, that all its prerequisites are up to date, and that none of its prerequisites has been modified more recently than it has — make’s job is done. Otherwise, it attempts to generate the default target from its prerequisites by executing its command script. Like the definition of up to date, this process is recursive: for each prerequisite which is not up to date, make searches for a rule having that prerequisite as a target, and starts the whole process again. This continues until the default target is up to date or until an error occurs.
It follows from the above description that a target having no prerequisites is up to date if and only if it corresponds to a file on the filesystem. Therefore, a target corresponding to a non-existent file is never up to date, and can be used to force a command script to be executed unconditionally. Such targets are called phony targets.
Tip
By labeling a target with the .PHONY attribute, as in Example 1-14 and Example 1-15, you can tell make that the target does not correspond to a file, and so should always be always rebuilt.
Conversely, a prerequisite corresponding to an existing file is always up to date, provided it doesn’t appear as the target of a rule.
Now let’s look at what happens when we execute the makefile in Example 1-14. The phony target all
is always out of date: its only purpose is to tell make to
build hello.exe. In such a simple makefile, there’s
no need for an all
target; in more complex examples,
the all
target may have several prerequisites. The rule
with target hello
tells make to build hello, if necessary, by
invoking g++. Assuming that the current directory is
empty except for makefile and hello.cpp, the target hello
is not up to date. The prerequisite is up to date,
however, because the file hello.cpp exists, and
because hello.cpp
does not appear as the target of any
rule. Consequently, make invokes g++ to compile and link hello.cpp, producing the file hello.
The prerequisite to the all
target is now up to date,
so make builds the all
target — by executing an empty command script — and exits.
When you invoke make with a command-line argument corresponding to a target, make
attempts to build that target. Therefore executing make
install
causes the following commands to be
executed:
mkdir -p binaries cp -p hello binaries
The first command creates the directory binaries,
if it doesn’t already exist; the second command copies hello
to that directory. Similarly, make
clean
invokes the command
rm -f hello
which deletes the hello.
Tip
If you’re using Windows, the mkdir
, cp
, and rm
commands used
by the install
and clean
targets refer to the GNU tools distributed with Cygwin or
MSYS.
Once you understand how make analyzes dependencies, Example 1-14 may seem pretty simple. In fact, however, it’s considerably more complicated than it needs to be; looking at the various ways it can be simplified is a good way to learn some of the rudiments of makefiles.
GNU make supports variables whose values are strings. The most common use of variables in makefiles is as symbolic constants; instead of hard-coding the name of a file or a shell command in several locations within a makefile, you can assign the file or command name to a variable and use the variable instead. This leads to simpler and easier to maintain makefiles. For example, you can rewrite the makefile from Example 1-14 using make variables, as shown in Example 1-16.
Example 1-16. Makefile to build the executable hello with GCC, modified to use make variables
# Specify the target file and the install directory OUTPUTFILE=hello INSTALLDIR=binaries # Default target .PHONY: all all: $(OUTPUTFILE) # Build hello from hello.cpp $(OUTPUTFILE): hello.cpp g++ -o hello hello.cpp # Copy hello to the binaries subdirectory .PHONY: install install: mkdir -p $(INSTALLDIR) cp -p $(OUTPUTFILE) $(INSTALLDIR) # Delete hello .PHONY: clean clean: rm -f $(OUTPUTFILE)
Here I’ve introduced two make variables, OUTPUTFILE
and INSTALLDIR
. As you can
see, make variables can be assigned values using the assignment operator =
, and they can be evaluated by enclosing them in
parentheses and prefixing a dollar sign.
You can also set the value of a make variable on the command line, using the syntax
make X
=Y
. In addition, when make
starts up, each environment variable is used to initialize a make variable with the same name and value. Values specified on the
command line take precedence over values inherited from the environment; values
specified in the makefile itself take precedence over values specified on the command
line.
GNU make also supports
automatic variables that take special values when
evaluated in a command script. Most importantly, the variable $@
represents the filename of the target, the variable $<
represents the filename of the first prerequisite, and
the variable $^
represents the sequence of
prerequisites, separated by spaces. Using these variables, we can further simplify the
makefile from Example 1-16, as shown in
Example 1-17.
Example 1-17. Makefile to build the executable hello with GCC, modified to use automatic variables
# Specify the target file and the install directory OUTPUTFILE=hello INSTALLDIR=binaries # Default target .PHONY: all all: $(OUTPUTFILE) # Build hello from hello.cpp $(OUTPUTFILE): hello.cpp g++ -o $@ $< # Install and clean targets as in Example 1-16
Within the command script g++
-o
$@
$<
, the variable $@
expands to hello and the variable $<
expands to hello.cpp
. Therefore
the makefile in Example 1-17 is
equivalent to that in Example 1-16, but
involves less code duplication.
The makefile in Example 1-17 can
still be radically simplified. In fact, the command script associated with
the target hello
is superfluous, as you can
demonstrate by executing the makefile in Example 1-18.
Example 1-18. Makefile to build the executable hello with GCC, modified to use implicit rules
# Specify the target file and the install directory OUTPUTFILE=hello INSTALLDIR=binaries # Default target .PHONY: all all: $(OUTPUTFILE) # Tell make to rebuild hello whenever hello.cpp is modified $(OUTPUTFILE): hello.cpp # Install and clean targets as in Example 1-16
How does make know how to build the executable hello from the source file hello.cpp, without being told? The answer is that make maintains an internal database of implicit rules representing operations commonly performed when building C and C++ applications. For example, the implicit rule to generate an executable file from a .cpp file looks like Example 1-19.
Example 1-19. A pattern rule from make’s internal database
%: %.cpp # commands to execute (built-in): $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
Rules with first lines of the form %
xxx
:%
yyy
are known as
pattern rules; the %
character act as a
wildcard. When no ordinary rule matches an out-of-date
prerequisite, make searches the available pattern
rules. For each pattern rule, make tries to find a string which when substituted for the
wildcard character in the target portion of the pattern rule yields the out-of-date
prerequisite. If make finds such a string,
make substitutes it for the wildcard character in
both the target and prerequisite portions of the pattern rule to produce a new rule.
make then attempts to build the out-of-date
prerequisite using the new rule.
For example, when the makefile in Example
1-18 is first executed, the prerequisite hello
of the default target all
is out
of date. Although hello
does appear as a target in
the rule $(OUTPUTFILE)
: hello.cpp
, this rule has no command script, and so is useless for building
the file hello.make therefore searches its internal database, and finds the rule shown in
Example 1-19. By substituting the
string hello
for the wildcard character in the rule
in Example 1-19, make generates the following rule, with hello
as its target:
hello: hello.cpp $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
So far, so good — but clearly there’s more to the story. Looking once again through
make’s internal database reveals that the
variable LINK.cpp
expands, by default, to $(LINK.cc)
. LINK.cc
, in
turn, expands by default to
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
Finally, the variable CXX
expands by default to
g++
, and the other four variables—$(CXXFLAGS)
, $(CPPFLAGS)
,
$(LDFLAGS)
, and $(TARGET_ARCH)
—expand to empty strings. When all these substitutions are
carried out, we’re left with the following rule, which by now may look familiar.
hello: hello.cpp g++ $^ -o $@
Now that you see how the pattern rule in Example 1-19 causes make to build the executable hello from the source file hello.cpp, you might well wonder why it was necessary to go through so many intermediate steps. Why not simply add the rule
%: %.cpp g++ $^ -o $@
to make’s internal database, instead of the
complex rule in Example 1-19? The
answer is that the intermediate variables such as $(CXX)
, $(CXXFLAGS)
, $(CPPFLAGS)
, and $(LDFLAGS)
, serve as user customization points. For example, you can specify
additional flags to be passed to the linker by specifying a value for LDFLAGS
on the command line, in a makefile, or by setting an
environment variable. The variables CPPFLAGS
and
CXXFLAGS
play a similar role for C++ preprocessor
and compiler options, respectively. You can even specify a compiler other than GCC by
setting the variable CXX
. For example, to build
hello with Intel for Linux using the makefile in
Example 1-18, you can enter
make
CXX=icpc
from the command line—assuming you’ve
set the environment variables required by the Intel compiler.
In Example 1-18, make is able to apply the correct pattern rule because the
.cpp file resides in the directory where the
output file is to created. If your source files are in a different directory, you can
use the VPATH
variable to tell make where to search for targets or
prerequisites:
VPATH = <path-to-cpp-files>
You can also use a vpath
directive to tell
make to look in a certain location for particular
types of files:
# look for .exp files in ../lib vpath %. exp../lib
Recipe 1.2 and Recipe 1.7
Get C++ Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.