Make for dummies

Introduction

This tutorial explains the main features of GNU make.

Makefile-less compiling

Plain Executable

gcc -O2 -Wall -o app app.c

Static Library

gcc -O2 -Wall -c mylib.c -o mylib.o

ar rcu libmylib.a mylib.o

ranlib libmylib.a

Dynamic (shared) Library

gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.1 mylib.o -lsomelib

Executable linked with linked library

"static" is required to tell gcc to compile libraries statically in the application:

gcc -O2 -Wall -static  -o app app.c -L. -lmylib

Executable linked with shared library

Unless told with "static", gcc uses shared libraries:

gcc -O2 -Wall -o app app.c -L. -lmylib

Basic Makefile

#export PATH=$PATH:/opt/uClinux/bin-linux-uclibc/bin

CC=bfin-linux-uclibc-gcc

INCLUDES=-I. -Ilibbb -I/usr/src/uClinux-dist/linux-2.6.x/include -I/usr/src/uClinux-dist/staging/usr/include

CFLAGS=-O2 -Wall $(INCLUDES)

myapp: libbb/libbb.so

        $(CC) $(CFLAGS) -o $@ *.c lib/mylib.so

lib/mylib.so:

        $(CC) $(CFLAGS) -shared -fPIC -o $@ lib/*.c

clean:

        -rm -R *.o

Reading Notes from "Managing Projects with GNU Make 3e"

Important: make will first process a whole Makefile before running it, which means that variables can be set after being used, eg.

COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) -c
INCLUDES = -I project/include

Here's a verbose example:

count_words: count_words.o lexer.o -lfl
    gcc count_words.o lexer.o -lfl -ocount_words
 
count_words.o: count_words.c
    gcc -c count_words.c
 
lexer.o: lexer.c
    gcc -c lexer.c
 
lexer.c: lexer.l
    flex -t lexer.l > lexer.c

The -l option to gcc indicates a system library that must be linked into the application. The actual library name indicated by “fl” is libfl.a. GNU make includes special support for this syntax. When a prerequisite of the form l<NAME> is seen, make searches for a file of the form libNAME.so; if no match is found, it then searches for libNAME.a. Here make finds /usr/lib/libfl.a and proceeds with the final action, linking.

make has many command-line options. One of the most useful is --just-print (or -n) which tells make to display the commands it would execute for a particular target without actually executing them. This is particularly valuable while writing makefiles. It is also possible to set almost any makefile variable on the command line to override the default value or the value set in the makefile.

Targets do not have to be actual files, any name will do.

One or more targets appear to the left of the colon and zero or more prerequisites can appear to the right of the colon. If no prerequisites are listed to the right, then only the target(s) that do not exist are updated. The set of commands executed to update a target are sometimes called the command script, but most often just the commands.

Explicit rules, like the ones in the previous chapter, indicate a specific target to be updated if it is out of date with respect to any of its prerequisites. This is the most common type of rule you will be writing. Pattern rules use wildcards instead of explicit filenames. This allows make to apply the rule any time a target file matching the pattern needs to updated. Implicit rules are either pattern rules or suffix rules found in the rules database built-in to make. Having a built-in database of rules makes writing makefiles easier since for many common tasks make already knows the file types, suffixes, and programs for updating targets. Static pattern rules are like regular pattern rules except they apply only to a specific list of target files.

Wildcard expansion is performed by make when the pattern appears as a target or prerequisite. However, when the pattern appears in a command, the expansion is performed by the subshell. This can occasionally be important because make will expand the wildcards immediately upon reading the makefile, but the shell will expand the wildcards in commands much later when the command is executed.

Targets that do not represent files are known as phony targets. It is important to note that make cannot distinguish between a file target and phony target. If by chance the name of a phony target exists as a file, make will associate the file with the phony target name in its dependency graph. To avoid this problem, GNU make includes a special target, .PHONY, to tell make that a target is not a real file. Any target can be declared phony by including it as a prerequisite of .PHONY:

.PHONY: clean
clean:
    rm -f *.o lexer.c

Phony targets can also be thought of as shell scripts embedded in a makefile.

$(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP)
    $(RM) $@
    $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS)
    ls -l $(Program)
    size $(Program)
 
.PHONY: build_msg
build_msg:
    @printf "#\n# Building $(Program)\n#\n"

It is important to note that because phony targets are always out of date, the phony build_msg target causes $(Program) to be regenerated even when it is not out of date.

Phony targets are always out of date, so they always execute and they always cause their dependent (the target associated with the prerequisite) to be remade. But suppose we have some command, with no output file, that needs to be performed only occasionally and we don’t want our dependents updated? For this, we can make a rule whose target is an empty file (sometimes referred to as a cookie):

prog: size prog.o
    $(CC) $(LDFLAGS) -o $@ $^
 
size: prog.o
    size $^
    touch size

Notice that the size rule uses touch to create an empty file named size after it completes. This empty file is used for its timestamp so that make will execute the size rule only when prog.o has been updated. Generally, empty files can be used to mark the last time a particular event has taken place.

As a special case, a single character variable name does not require the parentheses.

There are six “core” automatic variables:

In addition, each of the above variables has two variants for compatibility with other makes. One variant returns only the directory portion of the value. This is indicated by appending a “D” to the symbol, $(@D), $(<D), etc. The other variant returns only the file portion of the value. This is indicated by appending an F to the symbol, $(@F), $(<F), etc.

The VPATH variable consists of a list of directories to search when make needs a file, while CPPFLAGS can be used to tell GCC where to find include files (CPPFLAGS = -I include). The VPATH variable is good because it solved our searching problem above, but it is a rather large hammer.

The vpath directive is a more precise way to achieve our goals. The syntax of this directive is:

vpath pattern directory-list

So our previous VPATH use can be rewritten as:

vpath %.c src
vpath %.h include

Rule creation is made easier by relying on conventions, eg. .o is obtained by comping a corresponding .c. The built-in rules are all instances of pattern rules. A pattern rule looks like the normal rules you have already seen except the stem of the file (the portion before the suffix) is represented by a % character:

%.o: %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<
 
%: %.c
    $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@

Having a built-in database of common rules makes many types of makefile specifications very simple. You can look at make’s default set of rules (and variables) by running "make --print-data-base".

The percent character in a pattern rule is roughly equivalent to * in a Unix shell. It represents any number of any characters. The percent character can be placed anywhere within the pattern but can occur only once.

It is also possible to have a pattern containing only a percent character. The most common use of this pattern is to build a Unix executable program.

A static pattern rule is one that applies only to a specific list of targets.

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

The only difference between this rule and an ordinary pattern rule is the initial $(OBJECTS): specification. This limits the rule to the files listed in the $(OBJECTS) variable.

Use static pattern rules whenever it is easier to list the target files explicitly than to identify them by a suffix or other pattern.

A single-suffix rule contains only one suffix, the suffix of the source file. These rules are used to create executables since Unix executables do not have a suffix:

.p:
        $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@

This rule produces an executable image from a Pascal source file. This is completely analogous to the pattern rule:

%: %.p
        $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@

You can add your own suffixes by simply adding a .SUFFIXES rule to your makefile:

.SUFFIXES: .pdf .fo .html .xml

If you want to delete all the known suffixes (because they are interfering with your special suffixes) simply specify no prerequisites:

.SUFFIXES:

You can also use the command-line option --no-builtin-rules (or -r).

The C compiler itself can be changed by altering the value of the CC variable. The other variables are used for setting compilation options (CFLAGS), preprocessor options (CPPFLAGS), and architecture-specific options (TARGET_ARCH).

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
CC = gcc

Here's how to create a static library from two object files previously compiled:

$ ar rv libcounter.a counter.o lexer.o

The same, in a Makefile:

libcounter.a: counter.o lexer.o
    $(AR) $(ARFLGS) $@ $?

If you use $? instead of $^, make will pass only those objects files that are newer than the target to ar.

An archive library contains an index of the symbols it contains. Newer archive programs such as GNU ar manage this index automatically when a new module is added to the archive. However, many older versions of ar do not. To create or update the index of an archive another program ranlib is used. On these systems, the built-in implicit rule for updating archives is insufficient.

And here's how to compile a static library and generate an executable from an object file previously compiled:

cc count_words.o libcounter.a /lib/libfl.a -o count_words

Cc is smart enough to understand that the two .a files are libraries. Alternatively, you can use the "-l" switch to be more explicit:

cc count_words.o -lcounter -lfl -o count_words

Note that "-l" will search for both static and shared libraries, in the default directories. The search path can be changed through the "-L" switch, eg. "-L." to search in the local directory.

Important: The value of a variable consists of all the words to the right of the assignment symbol with leading space trimmed. Trailing spaces are not trimmed.

There are two types of variables: "Simply expanded" (or "simple") variables use the ":=" syntax and are similar to how variables work in most computer languages in that the variable is set to whatever the right-side contains at this particular time in the script, while "recursively expanded" (or "recursive") variables use the "=" syntax and has its value set only when the variable is used.

If CC is empty at this time, MAKE_DEPEND will simply be set to "-M":

MAKE_DEPEND := $(CC) -M

Recursive variables make it possible to actually define CC after it's used to define MAKE_DEPEND:

MAKE_DEPEND = $(CC) -M
...
# Some time later
CC = gcc

Recursive variables can be useful to display eg. the date/time: Every time the variable is used, its contents is expanded and the variable ends up containing a different value.

make provides target-specific variables. These are variable definitions attached to a target that are valid only during the processing of that target and any of its prerequisites:

gui.o: CPPFLAGS += -DUSE_NEW_MALLOC=1
gui.o: gui.h

 

Tips

No linker?

As with most compilers, gcc calls the linker for you, so there's no need to call "ld" manually unless you have a reason to, ie. this is uselessly complicated:

main: main.o
        ld main.o ... -o main
 
main.o: main.c
        gcc -c -g -O2 -o main.o main.c

Checking what make does

In case you are dealing with a top-most Makefile calling multiple Makefiles through "include", it can be hard to figure out what it does. Most implementations of make offer the -d flag which will cause the program to print out everything it is doing, in great detail. The -n flag will cause make to do a dry-run, ie report what it would do but not actually do it.

Parameters

Note that "make" can be called with variables and targets: If the item is A=B, make considers this to be a variable definition; If the item is a plain word, make considers this to be a target:

.PHONY: all here there
#make
all:
        @echo "Here"
 
#make DUMMY=DummyVar here
here:
        @echo $(DUMMY)
 
#make DUMMY=DummyVar there
there:
        @echo "There"
 
#make none
#make: *** No rule to make target `none', needed by `all'.  Stop.

Q&A

What's the difference between linking with .o and linking with .a?

A static library, ie. mylib.a, contains a bunch of object files (.o) which have been compiled from .c source files.

It's better to link a .a file because the linker will only include the routines that are actually used by the application, while linking with .o files will include all the routines, even those that aren't used by the application.

This is the reason why you need to run "ar" (and possibly "ranlib", in case "ar" is a bit old) on the object file of a library: This is used to create an index of all the routines that live in all the object files that were used to compile the .a library file.

In a target, how to run "strip" depending on the compiler used?

The Blackfin toolchain contains two versions of the C compiler: One to create FLAT binaries, the other to create FDPIC ELF binaries, and the latter need to be stripped.

test: test.c

    ...

    #FDPIC ELF

ifeq ($(CROSS),bfin-linux-uclibc-)

    $(STRIP) $@

endif

How to use two different compilers?

How to use a single Makefile to compile files with either bfin-uclinux-gcc and bfin-linux-uclibc-gcc?

:= vs. = ?

When defining variables, ":=" works as expected, ie. the value on the right-side will be copied into the left-side right away, while when using "=", the value can be in code that can live even lower in the Makefile:

LIBS = $(MYLIBS)
...
linux:
    $(MAKE) all MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"

Where is MAKE defined?

http://www.gnu.org/software/make/manual/make.html#MAKE-Variable

A few questions while reading Lua's Makefile

What are AR and RANLIB?

ar rcu liblua.a lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o lstrlib.o loadlib.o linit.o

ranlib liblua.a

ar and ranlib seem to be used to create libraries (static? shared?)

How to define variables in Makefile and through export?

Resources