Deconstructing Make

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

There is one simple rule you must know and never forget

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.

If the first character on a line is a <TAB> and your line contains anything else you will get an error message. If you feel especially cautious, you may execute
        cat -v -t -e makefile
which will display every <TAB> as ^I and place $ at the end of every newline. Shall we begin?

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

There are no commands following this line, there are no instructions given to make, immediately, to build this target all. You run make from the command line by executing the command make and specifying the target make is to build:
        make all
If you do not specify a target
        make
make will build the first target on the list. This brings us to our second rule of make:

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.

In order to build all make starts with myapp and tries to figure-out how to build it.

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

Building 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.
How does make know when a dependency has changed? Hint: stat. Make will build myapp after checking If make determines that myapp needs to be built, it does not execute the next line. Make proceeds to work its way to the end of the file to determine all dependencies, and only then will it start building files. It is important to realize, that make does not stop if main.o and mylib.a are older than myapp; make will proceed to check if any file these depend has been more recently changed as well. Make will not stop until it has checked every dependency on the file.

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.

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.

Using Make to Maintain a Library: Make isn't Stupid

Now we come to the power of make:

Make can Build

Make knows alot about building things.
Make knows how to build object files. When make sees a dependency line like

2.o: 2.c a.h b.h

without a command, make recognizes that it must build the object file 2.o by executing the command

gcc -c 2.o 2.c

So, make knows how to build all three targets

main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

The first line is really where the action takes place

$(MYLIB): $(MYLIB)(2.0) $(MYLIB)(3.o)

and the key to understanding it is the special way make treats the dependencies $(MYLIB)(2.0) and $(MYLIB)(3.o). Make expands the variable and knows $(MYLIB)(2.0) is really mylib.a(2.0); the use of (2.o) is a special signal to make that mylib.a is really a library and 2.o is an object file in that library. This triggers make to treat mylib.a specially.

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

Internal Make Macros

$@, $@ 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.

So, for us, $< in the suffixing rule refers to a .c file which will be used in building our .o (or .a) file. In fact, the suffixing rule is really shorthand in make:

.c.o:
really means
%.o: %.c

Let's go back to the construction of our library: mylib.a using make's internal suffixing rule

.c.a:
      $(CC) -c $(CFLAGS) $<
      ar rv $@ $*.o
      rm -f $*.o


We now know what the first two lines mean. The first line tells make how to build an archive from its object files. The second line says to compile each .c file which have been modified more recently than our library. This produces a .o file by default from the compiler (this is because of the -c option to gcc.) The third and fourth lines are simple Unix commands. The third line says to update the archive by placing the .o file just created ($*.o) into mylib.a ($@), while the fourth line removes the .o file (which is now a member of the library.) Make performs this task separately for each file that requires updating into the library. This means that if your library has a hundred object files, but only one source code file has been updated since the library was last built, only that file will be used to update the library.

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