make command is a *NIX tool that allows you manage
the compilation of one or more source files. For an application
with just one source file, make does not improve the
compilation efficiency. However, for projects made up of multiple
source files, recompiling all files every time a change is made
can take a lot of time.
Consider a project where you have 3 source files, source1.C,
source2.C, and source3.C; and 3 header
files, header1.h, header2.h, header3.h.
To compile such a program, you might type:
g++ -Wall source1.C source2.C source3.C -o project
To get an executable program named project. Now
imagine that during testing, you found a bug in source2.C.
You edit that file, fix the bug, and are now ready to recompile
your program. Using the command used earlier, all source
files are recompiled, even though source2.C is the only
one that was changed.
make is designed to identify files that have been changed,
and only recompile those files. As files are recompiled, other dependent
files may be recompiled. At the end of the process, the changed files
and any dependent files have been recompiled, but other unchanged files
are not recompiled.
This tutorial is not intended to be an exhaustive document on what
make can do. It is intended to be an introduction to
the basic features make provides. See the References
section for links to more detailed tutorials.
make, we need to break these
steps apart.
2.1 Multi-Step Compilation
To make our compiler stop after the assembler has run, we can specify the-cswitch.% g++ -Wall -c source1.C -o source1.o % g++ -Wall -c source2.C -o source2.o % g++ -Wall -c source3.C -o source3.oHere,source1.Cwas compiled into an object file namedsource1.o.source2.C, which was then compiled into an object file namedsource2.o. Finally,source3.Cwas then compiled into an object file namedsource3.o.Note that none of these .o files are executable. A file step must be executed to executed to link all of these individual object files into a single executable.
% g++ -Wall source1.o source2.o source3.o -o projectNow, we haveprojectwhich is an executable program.
2.2 Taking Advantage of Multi-Step Compilation
Okay, so we can turn one command for compiling a program into four. How does that help me? Well, here again we'll look at making a code change to one of the source files. Say we change something insource2.C. What do we need to do to build our application?Well,
source1.Cdoes not depend onsource2.Cand we already havesource1.oso we do not have to recompilesource1.C.% g++ -Wall -c source2.C -o source2.o % g++ -Wall -c source3.C -o source3.o % g++ -Wall source1.o source2.o source3.o -o projectNow, we haveprojectwhich is an executable program and we did not recompilesource1.C.As you can see, it is possible to use multi-step compilation manually to prevent unnecessary recompilations. However, as project grow larger and larger, we'd like a way to automate this process.
maketo the rescue!
makefile is a file that contains a set of rules governing
commands to be executed. The basic form of these rules is:
<target>: <dependent files> <tab><commands to build target> ...Where target is the name associated with this specific build rule, dependent files is a list of files that this target depends on, and the list of commands to build the target are programs that will be executed to build the target. Notice that the tab character has to prefix each command line.
Let's consider our previous example. We want to make a makefile to build our project. Makefiles are usually named either "makefile" or "Makefile" Such a makefile might look like the following:
all: project
project: source1.o source2.o source3.o
g++ -Wall source1.o source2.o source3.o -o project
source1.o: source1.C source1.h
g++ -Wall -c source1.C -o source1.o
source2.o: source2.C source2.h source1.o
g++ -Wall -c source2.C -o source2.o
source3.o: source3.C source3.h source1.o source2.o
g++ -Wall -c source3.C -o source3.o
Now, if our project has never been built before, if we type
make all on the command line, where all is the
target to be built, make will determine that the target all is dependent
on the file project. The file project does not exist, so it needs to
be built. make then looks for a rule named project.
It sees that the file project is dependent upon three other
file, source1.o, source2.o, and
source3.o. It looks for those file, sees that they are not
there so it must build them. It repeats this process until all files
are built.
In the future, if a modification is made to source2.C, when
make evaluates the dependency list, it will see that all
depends on project, project depends on
source2.o, and source2.o depends on
source2.C. source2.C has been modified since
source2.o was built (make determines this by the timestamps
on the files), so it must be rebuilt. Then everything depending on that
must be rebuilt. Then everything depending on that must be rebuilt.
This continues until everything that needs to be rebuilt is rebuilt.
The make process is not limited to compiling. For example
one frequent rule in makefiles is one to clean up all the generated
files. In our example, such a rule might look like:
clean:
rm *.o project
Here, if we type make clean, all the object files and
the executable will be removed.
Let's again take a look at our example.
#
# This is a comment. Comments should be used to document
# your makefile just like one of your programs.
#
CC=g++
CC_OPTS=-Wall
PROJECT=project
RM=/bin/rm
all: $(PROJECT)
$(PROJECT): source1.o source2.o source3.o
$(CC) $(CC_OPTS) source1.o source2.o source3.o -o $(PROJECT)
source1.o: source1.C source1.h
$(CC) $(CC_OPTS) -c source1.C -o source1.o
source2.o: source2.C source2.h source1.o
$(CC) $(CC_OPTS) -c source2.C -o source2.o
source3.o: source3.C source3.h source1.o source2.o
$(CC) $(CC_OPTS) -c source3.C -o source3.o
clean:
$(RM) *.o project
This is basically the same make file as we saw before. This time,
however, we're using variables. For example, the CC_OPTS
variable hold options we're passing to the compiler. If we wanted
to add an additional options, say -g, we could add it
in that one place, instead of five in the previous.
CC_OPTS=-Wall -g