Make is a command generator: using a description file it generates a sequence of commands
to be executed by the Unix shell. Make is most naturally used to sort out the dependency relations
among files, so that it executes only the commands that need to be executed, in the order they must be
executed. You record once and for all the relationships between files, and from then on merely
type
make myprogram
and let make build your project. Make saved my life, and it started with my trying
to understand the Makefiles I needed to do this project. Perhaps, I can pass some of this on to you.
Make builds a target and anything that is required to build that target, by following a sequence of dependency relations which layout the commands that must be executed and the order those commands are to be executed. Make assumes
Tab Rule
If the first character on a line is a <TAB> then what follows the <TAB> is a command to be executed by the Unix Shell.
You will want the Makefile for the static library in front of you. The first line is
all: myapp
A makefile consists of lines which display dependencies needed to build a target
and lines which display the commands to be executed. This first line is a dependency line
and consists of
Order of Construction
Make starts with the target it is to build and works downward in the file in determining all prerequisites which must be built, to build the target. Once make reaches the bottom of the file, it proceeds to build the target by executing all commands required.
The next pair of lines in Makefile are
#Which compiler
CC = gcc
The first line is a comment. All comments begin with #
and extend to the end of the line. The second line is a macro definition. A macro definition
consists of : name = string. The
name is then used by typing either ${name} or $(name); every occurrence
of $(name) in the file is then replaced with string. So, everywhere in the file you
now see $(CC), make will replace with gcc.
Make follows simple rules with the string on the right-hand side:
what-you-see-is-what-you-get. Make treats all space immmediately preceeding the
= as a single space, everything else is exactly as it
appears.
I'll skip past several more macro definitions, and arrive at our first interesting line
myapp: main.o $(MYLIB)
$(CC) -o myapp main.o $(MYLIB)
The first line is a dependency line: it says myapp
depends on the following files. The second line begins with a <TAB>, and so
is a command line. This brings us to another fundamental rule of make
How does make know when a dependency has changed? Hint: stat. Make will build myapp after checkingBuilding Targets
Make assumes all targets are files to be built. If the file does not exist, make builds it. If the file does exist make checks all dependencies: if any dependency has been changed since the target was built, make rebuilds the target. If there have been no changes in the dependencies since the target was last built, make skips the target.
The next four lines go together, and are quite cryptic. It is time to stop with make, and take a short discursion into static libraries.
Libraries are files created with an archive, ar, utility. Libraries are really large collections of files which have been organized into a single file. They are used by a linker, which extracts modules that are used by executable files. Libraries are typically found in a common directory such as /user/lib/. Libraries typically have a suffix .a (for archive) to identify them, and often go by a name like libc.a or libmath.a.
A library is a single file, but it is made-up of other files with contain binary code. These other files are called object files, and usually have the suffix .o. They are generated by the compiler using the -c option. Object files define functions, which may then be used by other programs. These functions are called modules, and a linker is a special program which searches for the proper module in a library specified by a program, and links the module into the program. This is precisely what happens when you call a function like printf in C: the linker searches the known libraries for a module which is probably called __printf.
Libraries may be composed of many dozens of object files. When one file is changed, the library does not need to be recreated, it only needs to be updated to include the new file. This is where make comes in: make can be told what files the library depends upon, and then make will maintain the library by only updating those files which have changed since the library was last built.
Now we come to the power of make:
Make knows how to build object files. When make sees a dependency line likeMake can Build
Make knows alot about building things.
Make knows how to build libraries. It has a built in rule for constructing them
.c.a:
$(CC) -c $(CFLAGS) $<
ar rv $@ $*.o
rm -f $*.o
The first line is a suffixing rule: it tells make how to build
a target file which
ends in .a from a dependent file
that ends with
.c. Make also used an implicit
suffixing rule when
it constructs a .o file from a
.c file:
.c.o:
$(CC) -c $(CFLAGS) $<
The second line in both rules is the same. Make has several built in macro definitions which
it uses by default, unless you specifically overide the default definition (which has been done in
this file.) The default definitions are
CC = cc
CFLAGS = -O
Check yourself: what are the values for these macros in the current file? The other odd feature in
the command line following the suffix rule is $<,
which is another specially defined macro definition. There are several special macro definitions, so
it is worth adding a new rule to cover them
$@, $@ | The name of the current target. $@ is used in the command line, $@ is used on the description line (to the right of the colon.) |
$? | The list of prerequisites which have been changed more recently than the current target. Cannot be used in suffixing rule, but only in command line to a normal description line. |
$ | The current prerequisite which has been changed more recently than the current target. This may only be used in suffixing rules where the suffix of the prerequisite is given. |
$* | The name--without the suffix--of the current prerequisite that has been modified more recently than the current target. This may only be used in suffixing rules. |
% | Wildcard which will match any string. But multiple occurrences on the same line refer to the same string. |
There last two description lines should be simple now that we know make, but there are a
couple new features that you need to know. In the lines
clean:
-rm main.o 2.o 3.o $(MYLIB)
The first line contains a single target without a dependency. This will only get executed if you specify
make to build this target:
make clean
In this case make will always build this target--i.e execute the command to remove the files.
This is because make thinks all targets are files, and will build the target (if it determines
the target must be built) if the target file does not exist. In the case of
clean, as long as you have not created a file in your
directory by this name, make will build the target (execute the command) every time it is requested
to do so.
By default make prints the output of every command it executes, and
stops whenever an error is encountered. A leading hyphen before a command tells
make to continue building, even when there are
errors (such as the file to be removed does not exist.) Another useful prefix
is the
@ sign prefixed before a command,
which tells make to suppress the normal output when executing the command. Makealso recognizes an continuation of the current line onto the next line by using
a
backslash, as the last character on a line before the newline. You
must not have any
spaces between the backslash and the newline (hence, the option
-e to cat to
help you debug your description file--remember.) You can apply your understanding
of these features of make
to the last dependency line in the file
install: myapp
This line is used for installing the executable myapp
into the directory /usr/local/bin
when it has been completely debugged and is ready to be used.
What follows is a Bash shell script which checks to see if the directory
/usr/local/bin really exists, then proceeds to install
the executable myapp into /usr/local/bin, changing its
privileges to allow anyone to execute it.
finis