
# 25 Managing projects with Make

The Make utility helps you manage the building of

projects: its main task is to facilitate rebuilding only those parts of a multi-file project that need to be recompiled or rebuilt. This can save lots of time, since it can replace a minutes-long full installation by a single file compilation. Make can also help maintaining multiple installations of a program on a single machine, for instance compiling a library with more than one compiler, or compiling a program in debug and optimized mode.

Make is a Unix utility with a long history, and traditionally

there are variants with slightly different behaviour, for instance on the various flavours of Unix such as HP-UX, AUX, IRIX. These days, it is advisable, no matter the platform, to use the GNU version of Make which has some very powerful extensions; it is available on all Unix platforms (on Linux it is the only available variant), and it is a de facto standard. The manual is available at \url{http://www.gnu.org/software/make/manual/make.html}, or you can read the book  [OReilly-GnuMake] .

There are other build systems, most notably Scons and Bjam . We will not discuss those here. The examples in this tutorial will be for the C and Fortran languages, but Make can work with any language, and in fact with things like \TeX\ that are not really a language at all; see section  25.6 .

# 25.1 A simple example

Top > A simple example
Purpose

In this section you will see a simple example, just to give the flavour of \emph{Make}.

## 25.1.1 C

Top > A simple example > C

Make the following files:

\listing{foo.c}{tutorials/makefiles/1c/foo.c} \listing{bar.c}{tutorials/makefiles/1c/bar.c} \listing{bar.h}{tutorials/makefiles/1c/bar.h} and a makefile: \listing{Makefile}{tutorials/makefiles/1c/Makefile}

The makefile has a number of rules like

foo.o : foo.c
<TAB>cc -c foo.c

which have the general form
target : prerequisite(s)
<TAB>rule(s)

where the rule lines are indented by a TAB character.

A rule, such as above, states that a target' file foo.o is made from a prerequisite' foo.c, namely by executing the command \n{cc

-c foo.c}. The precise definition of the rule is:

• if the target foo.o does not exist or is older than the prerequisite foo.c,
• then the command part of the rule is executed: cc -c foo.c
• If the prerequisite is itself the target of another rule, than that rule is executed first.

Probably the best way to interpret a rule is:

• if any prerequisite has changed,

• then the target needs to be remade,

• and that is done by executing the commands of the rule;

• checking the prerequisite requires a recursive application of make:

• if the prerequisite does not exist, find a rule to create it;

• if the prerequisity already exists, check applicable rules to see if it needs to be remade.

If you call make without any arguments,

the first rule in the makefile is evaluated. You can execute other rules by explicitly invoking them, for instance make foo.o to compile a single file.

\practical{Call make.} {The above rules are applied: make without arguments tries to build the first target, fooprog. In order to build this, it needs the prerequisites \n{foo.o} and bar.o, which do not exist. However, there are rules for making them, which make recursively invokes. Hence you see two compilations, for foo.o and \n{bar.o}, and a link command for fooprog.}

{Typos in the makefile or in file names can cause various errors. In particular, make sure you use tabs and not spaces for the rule lines. Unfortunately, debugging a makefile is not simple. Make's error message will usually give you the line number in the make file where the error was detected.}

\practical{Do \n{make clean}, followed by mv foo.c boo.c and make again. Explain the error message. Restore the original file

name.} {Make will complain that there is no rule to make \n{foo.c}. This error was caused when foo.c was a prerequisite for making foo.o, and was found not to exist. Make then went looking for a rule to make it and no rule for creating .c files exists.}{}

Now add a second argument to the function bar. This requires you to edit \n{bar.c} and bar.h: go ahead and make these edits. However, it also requires you to edit foo.c, but let us for now forget' to do that. We will see how Make can help you find

the resulting error.

\practical{Call make to recompile your program. Did it recompile \n{foo.c}?}{Even through conceptually foo.c would need to be recompiled since it uses the bar function, Make did not do so because the makefile had no

rule that forced it.}{}

In the makefile, change the line

foo.o : foo.c

to
foo.o : foo.c bar.h

which adds \n{bar.h} as a prerequisite for foo.o. This means that, in this case where \n{foo.o} already exists, Make will check that foo.o is not older than any of its prerequisites. Since \n{bar.h} has been edited, it is younger than \n{foo.o}, so foo.o needs to be reconstructed.

\practical{Confirm that the new makefile indeed causes foo.o to be recompiled if bar.h is changed. This compilation will now give an error, since you forgot' to edit the use of the bar function.}{}{}

## 25.1.2 Fortran

Top > A simple example > Fortran

Make the following files:

\listing{foomain.F}{tutorials/makefiles/1f/foomain.F} \listing{foomod.F}{tutorials/makefiles/1f/foomod.F} and a makefile: \listing{Makefile}{tutorials/makefiles/1f/Makefile} If you call make, the first rule in the makefile is executed. Do this, and explain what happens.

\practical{Call make.} {The above rules are applied: make without arguments tries to build the first target, foomain. In order to build this, it needs the prerequisites \n{foomain.o} and foomod.o, which do not exist. However, there are rules for making them, which make recursively invokes. Hence you see two compilations, for foomain.o and \n{foomod.o}, and a link command for fooprog.}

{Typos in the makefile or in file names can cause various errors. Unfortunately, debugging a makefile is not simple. You will just have to understand the errors, and make the corrections.}

\practical{Do \n{make clean}, followed by mv foomod.c boomod.c and make again. Explain the error message. Restore the original file

name.} {Make will complain that there is no rule to make \n{foomod.c}. This error was caused when foomod.c was a prerequisite for foomod.o, and was found not to exist. Make then went looking for a rule to make it, and no rule for making .F files exists.}{}

Now add an extra parameter to \n{func} in foomod.F and recompile.

\practical{Call make to recompile your program. Did it recompile \n{foomain.F}?}{Even through conceptually foomain.F would need to be recompiled, Make did not do so because the makefile had no

rule that forced it.}{}

Change the line

foomain.o : foomain.F

to
foomain.o : foomain.F foomod.o

which adds \n{foomod.o} as a prerequisite for foomain.o. This means that, in this case where foomain.o already exists, Make will check that \n{foomain.o} is not older than any of its prerequisites. Recursively, Make will then check if foomode.o needs to be updated, which is indeed the case. After recompiling \n{foomode.F}, foomode.o is younger than \n{foomain.o}, so foomain.o will be reconstructed.

\practical{Confirm that the corrected makefile indeed causes foomain.F to be recompiled.}{}{}

## 25.1.3 About the make file

Top > A simple example > About the make file The make file needs to be called makefile or Makefile; it is not a good idea to have files with both names in

the same directory. If you want Make to use a different file as make file, use the syntax make -f My_Makefile.

# 25.2 Variables and template rules

Top > Variables and template rules
Purpose

In this section you will learn various work-saving mechanisms in \emph{Make}, such as the use of variables and of template rules.

## 25.2.1 Makefile variables

Top > Variables and template rules > Makefile variables

It is convenient to introduce variables in your makefile. For instance, instead of spelling out the compiler explicitly every time, introduce a variable in the makefile:

CC = gcc
FC = gfortran

and use \verb+${CC}+ or \verb+${FC}+ on the compile lines:
foo.o : foo.c
${CC} -c foo.c foomain.o : foomain.F${FC} -c foomain.F

\practical{Edit your makefile as indicated. First do make clean, then \n{make foo} (C) or make fooprog (Fortran).} {You should see the exact same compile and link lines as before.} {Unlike in the shell, where braces are optional, variable names in a makefile have to be in braces or parentheses. Experiment with what hapens if you forget the braces around a variable name.}

One advantage of using variables is that you can now change the compiler from the commandline:

make CC="icc -O2"
make FC="gfortran -g"


\practical{Invoke Make as suggested (after \n{make clean}). Do

you see the difference in your screen output?} {The compile lines now show the added compiler option \n{-O2} or -g.}{}

\emph{Make} also has automatic variables

:

• [\$@] The target. Use this in the link line for the main program. % (See section for some trickery • [\$\char\^] The list of prerequisites. Use this also in the link

line for the program.

• [\$<] The first prerequisite. Use this in the compile commands for the individual object files. • [\n{\$*}] In template rules

(section  25.2.2 ) this matches the template part, the part corresponding to the~\char37.

Using these variables, the rule for fooprog becomes
fooprog : foo.o bar.o
${CC} -o$@ $^  and a typical compile line becomes foo.o : foo.c bar.h${CC} -c $<  You can also declare a variable THEPROGRAM = fooprog  and use this variable instead of the program name in your makefile. This makes it easier to change your mind about the name of the executable later. \practical{Edit your makefile to add this variable definition, and use it instead of the literal program name. Construct a commandline so that your makefile will build the executable fooprog\_v2.}{You need to specify the THEPROGRAM variable on the commandline using the syntax make VAR=value.} {Make sure that there are no spaces around the equals sign in your commandline.} The full list of these automatic variables can be found at \url{https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html}. ## 25.2.2 Template rules Top > Variables and template rules > Template rules So far, you wrote a separate rule for each file that needed to be compiled. However, the rules for the various .c files are very similar: • the rule header (foo.o : foo.c) states that a source file is a prerequisite for the object file with the same base name; • and the instructions for compiling (\n{\$\{CC\} -c \$<}) are even character-for-character the same, now that you are using Make's built-in variables; • the only rule with a difference is foo.o : foo.c bar.h${CC} -c $<  where the object file depends on the source file and another file. We can take the commonalities and summarize them in one template rule \footnote {This mechanism is the first instance you'll see that only exists in GNU make, though in this particular case there is a similar mechanism in standard make. That will not be the case for the wildcard mechanism in the next section.}: ${CC} -c $<${FC} -c $<  This states that any object file depends on the C or Fortran file with the same base name. To regenerate the object file, invoke the C or Fortran compiler with the -c flag. These template rules can function as a replacement for the multiple specific targets in the makefiles above, except for the rule for foo.o. The dependence of \n{foo.o} on \n{bar.h}, or foomain.o on foomod.o, can be handled by adding a rule  # C foo.o : bar.h # Fortran foomain.o : foomod.o  with no further instructions. This rule states, if file \n{bar.h} or \n{foomod.o} changed, file \n{foo.o} or foomain.o needs updating' too. Make will then search the makefile for a different rule that states how this updating is done, and it will find the template rule. \practical{Change your makefile to incorporate these ideas, and test.}{}{} ## 25.2.3 Wildcards Top > Variables and template rules > Wildcards Your makefile now uses one general rule for compiling any source file. Often, your source files will be all the \n{.c} or .F files in your directory, so is there a way to state compile everything in this directory'? Indeed there is. Add the following lines to your makefile, and use the variable \n{COBJECTS} or FOBJECTS wherever appropriate. The command \indextermtt{wildcard} gives the result of ls, and you can manipulate file names with \indextermtt{patsubst}. # wildcard: find all files that match a pattern CSOURCES :=${wildcard *.c}
# pattern substitution: replace one pattern string by another
COBJECTS := ${patsubst %.c,%.o,${SRC}}

FSOURCES := ${wildcard *.F} FOBJECTS :=${patsubst %.F,%.o,${SRC}}  ## 25.2.4 Conditionals Top > Variables and template rules > Conditionals There are various ways of making the behaviour of a makefile dynamic. You can for instance put a shell conditional in an action line. However, this can make for a cluttered makefile; an easier way is to use makefile conditionals. There are two types of conditionals: tests on string equality, and tests on environment variables. The first type looks like ifeq "${HOME}" "/home/thisisme"
# case where the executing user is me
else
# case where it's someone else
endif

and in the second case the test looks like
ifdef SOME_VARIABLE

The text in the true and false part can be most any part of a makefile. For instance, it is possible to let one of the action lines in a rule be conditionally included. However, most of the times you will use conditionals to make the definition of variables dependent on some condition.

\practical{Let's say you want to use your makefile at home and at work. At work, your employer has a paid license to the Intel compiler icc, but at home you use the open source Gnu compiler gcc. Write a makefile that will work in both places, setting the appropriate value for CC.}{}{}

# 25.3 Miscellania

Top > Miscellania

## 25.3.1 What does this makefile do?

Top > Miscellania > What does this makefile do? Above you learned that issuing the make command will automatically

execute the first rule in the makefile. This is convenient in one sense\footnote {There is a convention among software developers that a package can be installed by the sequence \n{./configure ; make ; make install}, meaning: Configure the build process for this computer, Do the actual build, Copy files to some system directory such as /usr/bin.}, and inconvenient in another: the only way to find out what possible actions a makefile allows is to read the makefile itself, or the -- usually insufficient -- documentation.

A better idea is to start the makefile with a target

info :
@echo "The following are possible:"
@echo "  make"
@echo "  make clean"

Now make without explicit targets informs you of the capabilities of the makefile.

If your makefile gets longer, you might want to document each section like this. This runs into a problem: you can not have two rules with the same target, info in this case. However, if you use a double colon it is possible. Your makefile would have the following structure:

info ::
@echo "The following target are available:"
@echo "  make install"
install :
# ..... instructions for installing
info ::
@echo "  make clean"
clean :
# ..... instructions for cleaning


## 25.3.2 Phony targets

Top > Miscellania > Phony targets The example makefile contained a target clean. This uses the Make mechanisms to accomplish some actions that are not related to file creation: calling \n{make clean} causes Make to reason there is no file called clean, so the following

instructions need to be performed'. However, this does not actually cause a file \n{clean} to spring into being, so calling make clean again will make the same instructions being executed.

To indicate that this rule does not actually make the target, declare

.PHONY : clean

One benefit of declaring a target to be phony, is that the Make rule will still work, even if you have a file named clean.

## 25.3.3 Predefined variables and rules

Top > Miscellania > Predefined variables and rules Calling make -p yourtarget causes make to print out all its

actions, as well as the values of all variables and rules, both in your makefile and ones that are predefined. If you do this in a directory where there is no makefile, you'll see that make actually already knows how to compile \n{.c} or .F files. Find this rule and find the definition of the variables in it.

You see that you can customize make by setting such variables as \n{CFLAGS} or FFLAGS. Confirm this with some experimentation. If you want to make a second makefile for the same sources, you can call make -f othermakefile to use this instead of the default Makefile.

Note, by the way, that both \n{makefile} and Makefile are

legitimate names for the default makefile. It is not a good idea to have both \n{makefile} and Makefile in your directory.

## 25.3.4 Using the target as prerequisite

Top > Miscellania > Using the target as prerequisite

Suppose you have two different targets that are treated largely the same. You would want to write:

PROGS = myfoo other
${PROGS} :$@.o # this is wrong!!
${CC} -o$@ $@.o${list of libraries goes here}

and saying make myfoo would cause
cc -c myfoo.c
cc -o myfoo myfoo.o ${list of libraries}  and likewise for make other. What goes wrong here is the use of \verb+$@.o+ as prerequisite. In Gnu Make, you can repair this as follows\footnote{Technical explanation: Make will now look at lines twice: the first time \n{\$\$} gets converted to a single~\$, and in the second pass \$@ becomes the name of the target.}:
.SECONDEXPANSION:

# 25.5 Practical tips for using Make

Top > Practical tips for using Make

Here are a couple of practical tips.

• Debugging

a makefile is often frustratingly hard. Just about the only tool is the -p option, which prints out all the rules that Make is using, based on the current makefile.

• You will often find yourself first typing a make command, and then invoking the program. Most Unix shells allow you to use commands from the shell command history by using the up arrow key. Still, this may get tiresome, so you may be tempted to write

make myprogram ; ./myprogram -options

and keep repeating this. There is a danger in this: if the make fails, for instance because of compilation problems, your program will still be executed. Instead, write
make myprogram && ./myprogram -options

which executes the program conditional upon make concluding successfully.

# 25.6 A Makefile for \LaTeX

Top > A Makefile for \LaTeX The Make utility is typically used for compiling programs, but

other uses are possible too. In this section, we will discuss a makefile for \LaTeX\ documents.

info :
@echo "Usage: make foo"
@echo "where foo.tex is a LaTeX input file"

%.pdf : %.tex
pdflatex $<  The command \n{make myfile.pdf} will invoke pdflatex myfile.tex, if needed, once. Next we repeat invoking pdflatex until the log file no longer reports that further runs are needed: %.pdf : %.tex pdflatex$<
while [ cat ${basename$@}.log | grep "Rerun to get" \
| wc -l -gt 0 ] ; do \
pdflatex $< ; \ done  We use the \verb+${basename fn}+ macro to extract the base name without extension from the target name.

In case the document has a bibliography or index, we run bibtex and makeindex.
%.pdf : %.tex
pdflatex ${basename$@}
-bibtex ${basename$@}
-makeindex ${basename$@}
while [ cat ${basename$@}.log | grep "Rerun to get" \
| wc -l -gt 0 ] ; do \
pdflatex ${basename$@} ; \
done



The minus sign at the start of the line means that Make should not abort if these commands fail.

Finally, we would like to use Make's facility for taking

dependencies into account. We could write a makefile that has the usual rules

mainfile.pdf : mainfile.tex includefile.tex

but we can also discover the include files explicitly. The following makefile is invoked with
  make pdf TEXTFILE=mainfile

The pdf rule then uses some shell scripting to discover the include files (but not recursively), and it calls Make again, invoking another rule, and passing the dependencies explicitly.
pdf :
export includes=grep "^.input " ${TEXFILE}.tex \ | awk '{v=v FS $$2".tex"} END {print v}' ; \ {MAKE} {TEXFILE}.pdf INCLUDES="$$includes" %.pdf : %.tex${INCLUDES}
pdflatex $< ; \ while [ cat${basename $@}.log \ | grep "Rerun to get" | wc -l -gt 0 ] ; do \ pdflatex$< ; \
done


This shell scripting can also be done outside the makefile, generating the makefile dynamically.