make builder
When software is distributed in the form of source code, the package often
includes a file, conventionally named Makefile, containing
instructions for building and installing the software automatically.
Makefiles are essentially scripts written in a declarative language and
executed by an interpreter named make.
As in other declarative languages, control flow is not determined by
explicit constructions of the text of the the make script, but is
rather inferred by the interpreter from the dependency relationships that the
programmer specifies. Makefiles are composed primarily of constructions
called rules. A rule consists of one or more targets, zero or more prerequisites, and zero more more
actions, arranged in the following format:
If there are two or more targets, they are separated by spaces. If there are two or more prerequisites, they too are separated by spaces. If there are two or more actions, they may be separated by semicolons (in which case they will be executed in sequence in a shell that is launched solely to perform those actions), or they may be placed on separate lines (in which case they will still be executed in sequence, but a different shell is launched for each one).
Each action line must begin with a tab character -- not a sequence of eight spaces, but the actual horizontal tab character with ASCII code 0x09. Edit carefully.A typical target is the name of a file that should be rebuilt automatically each time some other file is revised. The prerequisite is then the name of the other file, the one from which the target file should be constructed. The action is the command line that one would use to construct it.
For example, a makefile for a Java project might include the rule
which directs make to build the target file HelloWorld.class (in the current working directory) from the prerequisite
file HelloWorld.java, by invoking the javac compiler,
provided that HelloWorld.java has been revised since HelloWorld.class was last constructed. The make utility determines
whether this condition is met by comparing the timestamps on the files. It
will also perform the action, of course, if HelloWorld.class
does not exist and HelloWorld.java does.
Exercise: Write a rule that directs make to run the gcc
compiler to obtain the object file queue.o from the source-code
file queue.c, provided that queue.o either does not
exist or is older than one or both of its prerequisite files queue.c and queue.h.
make
To run make, first create the Makefile in the directory
containing the source files. Use cd to make it the current working
directory, if you haven't already done so. Then, at the prompt in the
terminal window, type make and the target or targets that you want
the builder to construct. For instance, you would give the command
to direct make's attention to the rule for building HelloWorld.class.
You'll get a fairly clear error report from make if you specify a
target that doesn't exist:
If you don't specify any target at all, make assumes that you want
to build the target of the first rule in the Makefile.
If you neglect to create the Makefile, make will look at
the suffixes of the targets and try to guess what action you had in mind.
Although it recognizes a few simple cases (it knows enough to run the C
compiler in order to build a target file with a name ending in .o), for the most part it simply reports that it has no rule to apply.
Exercise: Create a folder make-lab for this lab and copy
queue.c, queue.h, and test-queues.c
from /home/stone/courses/software-design/code/ into that
folder. Create a Makefile in the same folder, containing the
rule you wrote in section 0 above. Don't forget to write comments into the
Makefile. (Any line beginning with a mesh character, #,
is a comment, and make will ignore it.) Run make to build
the queue.o file.
A file that is a target in one rule may be a prerequisite within some other
rule. For instance, documentation that is prepared with the TEX
typesetter and released in Portable Document Format might be processed by
three different utility programs along the way. The .pdf file
might be derived from a PostScript (.ps) version by means of
the special-purpose ps2pdf converter, as directed by the following
rule:
The PostScript file in turn comes from a "device-independent"
page-description file, depending on some program like a2ps to
perform the conversion:
And TEX is used to construct the .dvi file from the .tex input file or files:
If all three of these rules are placed in the Makefile, in any order, the command
tells make to consider frogs.pdf and its direct and
indirect prerequisites as targets, any or all of which might require its
intervention. If it finds that frogs.dvi is older than
frogs.tex, then it infers that it is necessary to run TEX in
order to get a newer version of frogs.dvi. The timestamp on
the new frogs.dvi then shows it to be more recent than
frogs.ps, so make runs a2ps in order to get a
revised frogs.ps file. This file is now more recent than
frogs.pdf, so make runs ps2pdf to build the
revised frogs.pdf file.
Exercise: Add to the Makefile that you created in section 1
above a second rule, directing make to recompile and re-link
the executable test-queues if it does not exist, or if any of
three files on which it depends -- test-queues.c,
queue.h, or queue.o -- has changed since
test-queues was constructed. Run make to build this
executable. Then delete queue.o and run make again.
Note that make rebuilds queue.o before recompiling
test-queues.c.
Makefiles in which all of the rules are written out in full, like those
shown above, are tedious to write and difficult to maintain. The make utility allows you to create and initialize string variables and to
use the values of those variables in subsequent rules.
To create and initialize a variable, put the variable at the beginning of a line, followed by an equals sign. The string comprising the characters to the right of the equals sign becomes the value of the variable (except that whitespace characters adjacent to the equals sign, on either side, are ignored).
For example, one might write
to create the variable JAVA_FLAGS and give it the string value
Note that quotation marks are not needed as delimiters for the string value.
To use the value of a variable in a rule, write the variable with a dollar sign and left brace in front of it and a right brace after it:
The make utility replaces the variable reference with its value
before executing the action. For historical reasons, make also
accepts parentheses rather than braces around the variable name, so that,
for instance, $(JAVAFLAGS) is the same as ${JAVAFLAGS}.
A few variables are maintained internally by make and have values
that are relative to the current rule. For instance, the variable
< refers to the first prerequisite of the current rule, so that one
could also write the rule shown just above as
(Actually, one could even abbreviate the second variable reference to $<. The braces are optional for variables with one-character names.)
Similarly, in the action part of a rule, the variable @ refers to
the target. Thus the rule for frogs.pdf in section 2 above could be
written as
Exercise: Add a CFLAGS variable at the beginning of your Makefile and use it in your rules to make sure that invocations of the C
compiler always turn on the -Wall and -ggdb3 options.
The make utility also recognizes several other ways of associating
values with variables. It predefines thirty or so of them, such as FC for the FORTRAN compiler; if a rule contains the reference ${FC}, make replaces it with the default value f77 even if
no explicit assignment appears in the Makefile.
The make utility also asks the shell that invokes it to pass along
the values of any shell variables that have been set, such as PATH
for the list of directories that the shell uses to search for executables
and PWD for the current working directory. These, too, are
available for use in rules.
Finally, the command line that you use to invoke make can include
assignments to variables, such as PREP=/usr/bin/m4. In this case,
you can't have whitespace on either side of the equals sign, and you'll
need delimiters that the shell can recognize on the right-hand side if it
contains spaces (as in A2PSFLAGS="-1 --landscape --no-header".
A variable assignment on the command line takes precedence over an explicit
assignments within the Makefile, which in turn takes precedence over
an assignment inherited from the corresponding shell variable, which in
turn takes precedence over an implicit predefinition supplied by make.
If you give make the command-line option -p, it will print
out (among other things) the values of all the variables it knows about,
whatever their source, before it proceeds to rebuild the target(s).
Exercise: In the rules in your Makefile, replace the name of the GNU
C compiler with a reference to the implicitly defined variable CC.
Delete the executable test-queues and run make to rebuild
it. What value does make supply for the variable CC? Does
this work? Why or why not?
Exercise: Delete test-queues and run make again, this time
specifying on the command line that the variable CC should have the
value /usr/bin/gcc. Delete test-queues again, edit the Makefile to place the same assignment to CC at the top, and then
run make again to rebuild the executable.
After listing all of the variables and their values, make -p also
prints out a number of suffix rules, supplying default actions
for frequently encountered targets. For instance, one of these is the rule
which says that, if the target is any file that ends in .dvi, its prerequisite is the corresponding file ending in .tex, and you can build it by running the executable whose name is the
value of the variable TEX on that prerequisite .tex
file. Since make preloads this suffix rule, we don't actually need
the explicit rule shown above for the target frogs.dvi -- make does the same thing by default (if TEX has the value /usr/bin/tex, at least).
Exercise: There is a similar rule for targets ending with .o and prerequisites ending in .c:
Looking back through the output from make -p, we can see how the
variables COMPILE.c and OUTPUT_OPTION were initialized. Note
how they refer to still other variables.
References to uninitialized variables, such as CPPFLAGS in this
example, are replaced with null strings when make processes the
rules containing them.
Does the presence of this suffix rule make any of the commands in the Makefile that you've been constructing superfluous? If so, rewrite the
Makefile, removing the superfluous commands. Then delete queue.o and test-queues and run make to rebuild them.
It is possible to use identifiers that are not file names as targets,
simply as triggers for actions that should be performed when one of the
prerequisites changes or even (without prerequisites) simply when it is
convenient to have make do them.
For instance, the Makefile in a software package that includes
source code often includes a target install, with all of the
executables in that the package provides as prerequisites, that copies
those executables into some appropriate directory such as /usr/local/bin/ and makes sure that they have the correct permissions and
ownership:
Exercise: Write a rule for a target called clean that removes all of
the .o files and the executable test-queues.
(This, too, is a common usage in makefiles for software packages. The idea
is to force a completely new recompilation the next time make is run
with any of the executables as targets.)
Full documentation for GNU make is available on line: