COP4610: Operating Systems & Concurrent Programming up ↑

Makefiles

 

The main purpose of the Unix make utility is is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them. However, it is a very powerful tool that can also be adapted for other purposes.

These notes are not intended to fully explain the make utility. To learn more about make, you can start with the man pages, but beware they may be slightly different on each system you use. This is because there are different versions of make on different systems, with various special features. If you want to write portable code you should use only the standard features that are documented in the UNIX standard. (See the on-line UNIX 98 standard for detailed explanation of make.)

To prepare to use make, you must write a file called a makefile that describes the relationships among files in your program and the commands for updating each file. Typically the makefile describes the source files, how to compile them to produce object files, and how to link the object files to produce one or more programs. By default, the make utility looks for a makefile file called "makefile" or "Makefile", though the user may specify another name for the makefile.

Once a suitable makefile exists, each time you change some source files, the simple shell command make suffices to perform all necessary recompilations. The make program uses the information in the makefile and the last-modification times of the files to decide which of the files need to be updated.


A Simple Example

# an example of a very simple makefile

PROGRAMS=prog1 prog2
CCOPTS=-Wall -ansi -pedantic

all: $(PROGRAMS)

prog1: shared.h prog1.c
        gcc -Wall -ansi -pedantic -o prog1 prog1.c
prog2: shared.h funcs.o prog2.c
        gcc -Wall -ansi -pedantic -o prog2 funcs.o prog1.c
clean:
        rm -f $(PROGRAMS) *~ core #*#

Observe:

The syntax of a makefile is not the same as a shell script, although there are some similarities and shell commands may be embedded in the construction rules of the makefile. In particular, in a makefile it is OK to have spaces in symbol definitions, such as CC = gcc, but if you leave spaces like that in a shell script the shell would try to execute gcc. Another difference is that to refer to the value of a make variable you use the syntax $(variablename) but to refer to the value of a shell variable you use the syntax ${variablename}.


A More Sophisticated Example

# a makefile that uses a suffix-based pattern
PROGRAMS=prog1 prog2
...
include Config
.c:
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ mylib.o $<
all: $(PROGRAMS)

Observe:

The example above could be simplified still further, if we did not want to always link in the special object file myobject.o. By default, make has certain built-in rules. One of these is:

.c:
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
T. P. Baker. ($Id)