UNIX/Linux & C Programming:
Chapter n: Compilation Management (Makefiles)



Coverage: [UPE] Chapter 8, §8.1 (pp. 241-242) and §8.3 (pp. 254-258) and [USP] Appendix A, §A.3 (pp. 807-809)


Compilation management

Overview of make


touch
  • change file access and modification times
  • if file exists, touch brings its timestamp up-to-date
  • if file does not exist, touch creates an empty file
  • $ touch foo.c
    

make is the standard compilation management tool for UNIX systems; it is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. This list is usually stored in a file named Makefile or makefile.

When files (say, one or more source files used to build a given program) are modified, you can use make to bring the executable version of the program up to date. make uses the dependencies specified in the Makefile to ensure that all derived files are brought up to date after source files change, while doing the minimum possible amount of work. make only recompiles when it is necessary.

Why Makefile? Naming your Makefile Makefile typically ensures that it will be listed first in an ls listing. However, a makefile can have any filename when you use the -f option to make (e.g., $ make -f mymakespecs)

Comment lines start with a # character (anywhere except in a command line), and continue until the end of the line.


Directives

A directive describes the relationship between a target (the file to be built), its dependencies or sources (the files used to create the target), and zero or more command lines which define the build process for the target:

target: source1 source2 ...
	command1
	command2

Long lists can be continued over multiple lines using \ as a line continuation character. Command lines must begin with a <TAB> character. By placing a @ character at the beginning of a command, you prevent it from being echoed to stdout by make as it is executed. By placing a - character at the beginning of a command, you can instruct make to ignore any non-zero exit status for that command.


What will make do?

To see what make will do without having it actually invoke any commands, use the -n command-line option. Then make will echo the commands it would have performed on stdout, but it will not actually invoke any of the commands. Use the -d to print debugging information in addition to normal processing. Using the -d and -n options together is helpful for debugging a Makefile.


Simple example

Create the following 3 files using touch: button.c window.c window.h

This is our source code. These are zero length files; we are simply using them for instructional purposes.

Dependency chart:

Assuming the following are the commands to build (make) the executable popup:

gcc -c button.c → button.o
gcc -c window.c → window.o
gcc -o popup button.o window.o → popup

Write a Makefile to build this program. Since these are files are not really source code, we must use make -n.

#Makefile

all: popup

popup: button.o window.o
	gcc -o popup button.o window.o

button.o: button.c
	gcc -c button.c

window.o: window.c window.h
	gcc -c window.c


[USP] §2.13 (pp. 55-56) dependency tree


[USP] §2.13 (pp. 55-56) Makefile

all: a.out

a.out: loggerlib.o logapp.o logapplib.o
        $(CC) loggerlib.o logapplib.o logapp.o

logapp.o: logapp.c
        $(CC) $(OPTS) logapp.c

logapplib.o: loggerlib.h logapplib.c
        $(CC) $(OPTS) logapplib.c

loggerlib.o: loggerlib.h loggerlib.c
        $(CC) $(OPTS) loggerlib.c

clean:
        @-rm *.o a.out


Variables

You can define variables in a Makefile and use them:

CC = gcc

LIST_OF_FILES = file1.c file2.c \
		file3.c file4.c

program1: $(LIST_OF_FILES)
	$(CC) $(LIST_OF_FILES) -o program1


Environment variables

You can refer to environment variables defined in your shell within a Makefile just as if they were declared right in the Makefile. Further, environment variable definitions override any global definitions appearing in the Makefile when make is invoked with the -e option.

$ export LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c"
$ make -e program1


Variables on the command line

You can also define/redefine variables on the make command line:

$ make LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c" program1

These definitions override whatever appears in the Makefile itself.


Default suffix rules

It is possible to define default rules for constructing certain kinds of files from their dependencies. Such rules will be used if no command lines are given for a particular target. For example, to say how a C program should be compiled into an object file:

.c.o:
	$(CC) $(CFLAGS) $< -o $@

Here, the $< and $@ are special variables which refer to the source file for the current line, and the current target file, respectively. The default suffix rules for updating an object library are:

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

Here $* refers to the filename part (without suffix) of the prerequisite. An example of a library reference in a Makefile is:

prog: lib(sub1) lib(sub2) lib((module1)) prog.o
	$(CC) -o $@ prog.o lib


A more general approach

CC = gcc
C_FLAGS = -c
OBJECTS = $(addsuffix .o,$(SRC)) # list of all .o files
PGM = keeplog
SRC1 = $(wildcard *.c) # list of all .c files
SRC = $(patsubst %.c,%,$(SRC1)) # list of all .c files with .c stripped off

$(OBJECTS): %.o: %.c
        $(CC) $(C_FLAGS) $< -o $@
        $(CC) -MM $< > $*.d

-include $(addsuffix .d,$(SRC))

clean:
        @-rm *.o *.d $(PGM)


System default make definitions

make actually reads its default rules and default macro definitions from system files stored in /usr/share/mk. The file sys.mk in this directory is normally read by make. It defines rules for C, C++, FORTRAN, Pascal, assembly, lex, yacc files. It also defines basic macros/variables for the compilers used for such files.


mkdep

By default, the FreeBSD version of make will read the file .depend in the current directory in addition to [Mm]akefile. It is normally invoked as:

   mkdep [cc-options] file1.c file2.c ...

It automatically produces directives (with no command lines) for all listed files, using the C preprocessor to determine the exact dependency list. Correct use of default rules and variables can then be used to direct the compilation of the corresponding C files. The GNU version of mkdep is gccmkdep.


Packaging and compression utilities


References

    [UPE] B.W. Kernighan and R. Pike. The UNIX Programming Environment. Prentice Hall, Upper Saddle River, NJ, Second edition, 1984.

Return Home