Making a Makefile

Without a makefile:
$ ls
foo.c

$ make foo
cc foo.c -o foo

Useful Make options: (For details: man make or pinfo make)

-B, –always-make           Unconditionally make all targets.

-C DIRECTORY, –directory=DIRECTORY
Change to DIRECTORY before doing anything.

-d                          Print lots of debugging information.

-i, –ignore-errors         Ignore errors from commands.

-j [N], –jobs[=N]          Allow N jobs at once; infinite jobs with no arg.

-k, –keep-going            Keep going when some targets can’t be made.

-n, –just-print, –dry-run, –recon
Don’t actually run any commands; just print them.

-s, –silent, –quiet       Don’t echo commands.

A Make rule is composed of:

target: prerequisites
commands

A target is considered “up to date” if it exists and is newer than its prerequisites. A rule tells make two things: when the targets are out of date, and how to update them when necessary.

Use .DEFAULT_GOAL to override the default goal. MAKECMDGOALS gives the list of targets specified with make (eg. make clean – $(MAKECMDGOALS) = clean)

Within a command script (if the line begins with a TAB character) the entire line is passed to the shell, just as with any other line that begins with a TAB. The shell decides how to interpret the text. Prefixing a command with ‘@’ suppresses echoing (printing of the command during make) while prefixing ‘-‘ ignores errors on executing command.

By default, when make looks for the makefile, it tries the following names, in order:
‘GNUmakefile’, ‘makefile’ and ‘Makefile’.

Makefile is recommend as prominent along with README and the likes.

Makefile Variables

As a project gets larger, more files are usually added. If you repeat a list of files, you can accidentally leave files out of the list. It’s simpler to make use of a variable that expands into the list.

The syntax for declaring and setting a makefile variable is varname = variable contents. To call the variable, use $(varname).

OBJECTS := $(wildcard *.o)               # Also an example of wildcard
OBJECTS := $(patsubst %.c,%.o,$(wildcard *.c))  # using functions

Variable Assignment

Assignment Types:
=  for delayed assignment – recursively expanded variables.
:= assigns immmediately – simply expanded variables. Recommended for faster Makefiles.
?= if you want to conditionally assign a variable – assigned only if not defined already.
+= is used for appending.

Variable definitions are parsed as follows:
immediate   = deferred
immediate   ?= deferred
immediate   := immediate
immediate   += deferred or immediate
define immediate                # same as immediate = deferred
deferred
endef
For the append operator, ‘+=’, the right-hand side is considered immediate if the variable was previously set as a simple variable (‘:=’), and deferred otherwise.

If you want to export specific variables to a sub-make, use the export directive, like this:
export variable …
export variable = value
export variable := value
If you want to prevent a variable from being exported, use the unexport directive, like this:
unexport variable …

Some pre-defined variables:

Variable    Description
$@    This will always expand to the current target.
$<    The name of the first prerequisite. This is the first item listed after the colon.
$?    The names of all the prerequisites that are newer than the target.
$^    The names of all the prerequisites, with spaces between them. No duplicates
$+    Like $^, but with duplicates. Items are listed in the order they were specified in the rule.

Environment Variables
Variable can also be exported as environment variables that can be checked in the Makefile e.g LDFLAGS could be exported as an environment variable with desired values.

You can also check if an environment variable has been set and initialise it to something if it has not. ie.

DESTDIR ?= /usr/local

will set DESTDIR to /usr/local if it is not already defined

To append to the environment variables use the += operator:

CFLAGS += -g -Wall

Functions

It is possible to call some predefined functions in makefiles. A full list of them can be found in the manual, of course (http://www.gnu.org/software/make/manual/html_chapter/make_8.html#SEC83).

Perhaps you want to find all the .c files in directory for later use:

SOURCES := $(wildcard *.c)

Given these, maybe you want to know the names of their corresponding .o files:

OBJS := $(patsubst %.c, %.o, $(SOURCES))

You can do things like adding prefixes and suffixes, which comes in handy quite often. For example, you could have at the top of the makefile a variable where you set the libraries to be included:

LIBS := GL SDL stlport

And then use

$(addprefix -l,$(LIBS))

in a later rule to add a -l prefix for every library mentioned in LIBS above.

Finding files in multiple directories is a good example of the usage of foreach

DIRS := src obj headers
FILES := $(foreach dir, $(DIRS), $(wildcard $(dir)/*))

Substitution References
foo := a.o b.o c.o
bar := $(foo:.o=.c)  or
bar := $(foo:%.o=%.c)  # same as $(patsubst %.o,%.c,foo)
Gives bar = a.c b.c c.c

List of Functions:
$(subst from,to,text)            – Replace from with to in text
$(patsubst pattern,replacement,text)    – Replace matched part with replacement in text
$(var :suffix=replacement)        – Replace suufix with replacement in var
$(strip string)             – Remove white spaces
$(findstring find,in)            – Search find in in
$(filter pattern…,text)        – Returns matched patterns in text
$(filter-out pattern…,text)        – Returns all that don’t match
$(sort list)                – Sort the elements in list
$(word n,text)                – Returns nth word of text
$(words text)                – Returns words in text
$(firstword names…)            – Returns fist name in the series of names
$(lastword names…)            – Returns the last name in the series of names
$(foreach var,list,text)        – Loop over list assigning var to each name and then expand text
$(call variable,param,param,…)    – call variable with arguments stored in $1, $2, etc.
File related:
$(dir names…)                – Extracts directory part of the path in names
$(notdir names…)            – Extracts filename from the path in names
$(suffix names…)            – Extracts suffixes from names (following ‘.’)
$(basename names…)            – Extracts all but suffix (preceding ‘.’)
$(addsuffix suffix,names…)        – Appends suffix to the names
$(addprefix prefix,names …)        – Prepends prefix to the names
$(join list1,list2 )            – Concatenates each word of list1 with corresponding word of list2
$(wildcard pattern )            – Retursn space separated list of matches
$(realpath names …)            – Absolute path resolving symbolic links
$(abspath names …)            – Absolute path
Non reference:
$(value variable)            – Exact value of the name (not reference without ‘$’)
$(eval varirable)            – Creating new constructs
$(origin variable)            – Origin of variable – default, environment, command line, etc
$(flavor variable)            – Type of variable – undefined, simple or recursive.
Misc:
$(shell command)            – Space separated output of command on shell
$(error text …)            – Generates an error displaying text
$(warning text …)            – Same as error except processing continues
$(info text …)

Phony targets

A phony target is one that is not really the name of a file. It is just a name for some commands to be executed when you make an explicit request. There are two reasons to use a phony target: to avoid a conflict with a file of the same name, and to improve performance.

You can have “phony” targets — targets which don’t actually create a file, but do something. These are created like normal targets: for instance, to add a “all” target to our makefile we’d add (probably at the top, so it becomes the default target):

all: foo

This rule won’t run if there exists a file called “all” in the directory (if someone was stupid enough to create one somehow). So we can tell make(1) that this is a phony target and should be rebuilt always this is by using the target .PHONY. so, we can add to our Makefile:

.PHONY: all

To add a clean target is fairly simple too, add:

.PHONY : clean
clean :
-rm edit $(objects)

and add clean to the list of phony targets:

# Installing the final product
install:
cp sample /usr/local
echo install: make complete

.PHONY: all clean install

Another interesting example for PHONY is subdirectories:

SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done

Issues with the above method the error from a make in the loop is lost. If we do check then ability to work with -k would be lost. Targets can’t be built in parallel since there is only one rule.

By declaring the subdirectories as phony targets (you must do this as the subdirectory obviously always exists; otherwise it won’t be built) you can remove these problems:

SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz

Here we have also declared that the ‘foo’ subdirectory cannot be built until after the ‘baz’ subdirectory is complete; this kind of relationship declaration is particularly important when attempting parallel builds.

A phony target should not be a prerequisite of a real target file; if it is, its commands are run every time make goes to update that file. As long as a phony target is never a prerequisite of a real target, the phony target commands will be executed only when the phony target is a specified goal.

Target-specific Variable Values

target … : variable-assignment

The variable-assignment can be any valid form of assignment; recursive (‘=’), static (‘:=’), appending (‘+=’), or conditional (‘?=’). All variables that appear within the variable-assignment are evaluated within the context of the target: thus, any previously-defined target-specific variable values will be in effect.

There is one more special feature of target-specific variables: when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and
all their prerequisites, etc. (unless those prerequisites override that variable with their own target-specific variable value). So, for example,

We want to write a recursive makefile to enter sub-dirs then instead of duplicating rules as below:

SUBDIRS = src test

SUBDIRS_DEBUG = $(addsuffix .debug, $(SUBDIRS))
SUBDIRS_CLEAN = $(addsuffix .clean, $(SUBDIRS))

.PHONY: $(SUBDIRS) $(SUBDIRS_DEBUG) $(SUBDIRS_CLEAN) all debug clean

all:    $(SUBDIRS)
$(SUBDIRS):
@echo
@echo make in $@…
$(MAKE) -C $@

debug:    $(SUBDIRS_DEBUG)
$(SUBDIRS_DEBUG):
@echo
@echo make debug in $@…
$(MAKE) -C $(basename $@) debug

clean:    $(SUBDIRS_CLEAN)
$(SUBDIRS_CLEAN):
@echo
@echo make clean in $@…
$(MAKE) -C $(basename $@) clean

We could use the target specific variable $(target) as shown below:

#Entering recursively into subdirectories and executing different make commands without duplication
target :=

subdirs := src test

.PHONY: $(subdirs) all clean

#For all target is NULL
all :    $(subdirs)

clean :    target := clean
clean :    $(subdirs)

debug : target := debug
debug : $(subdirs)

#Recursively run the make through the subdirs with the same target
$(subdirs):
@echo
$(MAKE) -C $@ $(target)

Pattern-specific Variable Values

pattern … : variable-assignment

As with target-specific variable values, multiple pattern values create a pattern-specific variable value for each pattern individually. The variable-assignment can be any valid form of assignment. Any command-line variable setting will take precedence, unless override is specified.

For example:
%.o : CFLAGS = -O
will assign CFLAGS the value of ‘-O’ for all targets matching the pattern %.o.

VPATH: Search Path for All Prerequisites
The value of the make variable VPATH specifies a list of directories that make should search. Most often, the directories are expected to contain prerequisite files that are not in the current directory; however, make uses VPATH as a search list for both prerequisites and targets of rules.

For example,
VPATH = src:../headers
specifies a path containing two directories, ‘src’ and ‘../headers’, which make searches in
that order.

The vpath Directive
Similar to the VPATH variable, but more selective, is the vpath directive (note lower case), which allows you to specify a search path for a particular class of file names: those that match a particular pattern.

For example,
vpath %.h ../headers
vpath %.c foo

So .h files will be searched in ../headers & .c in foo.

Automatic dependency calculation

If you are creating a Makefile for C/C++ gcc can calculate dependency information for you. The quickest way to get this going is to add the -MD flag to your CFLAGS first. You will then need to know the names of the .d files in your makefile. I do something like this:

DEPS := $(patsubst %.o,%.d,$(OBJS))

Then near the end of the makefile, add an

-include $(DEPS)

It might also help to make a ‘deps’ target:

deps: $(SOURCES)
$(CC) -MD -E $(SOURCES) > /dev/null

‘-E’ tells gcc to stop after preprocessing. When using -E, the processed C file is sent to STDOUT. Therefore to avoid the mess on the screen, send it to /dev/null instead. Using this command all of the *.d files will be made.

The configure script and the Makefile rules for building and installation should not use any utilities directly except these:

cat cmp cp echo egrep expr grep ln mkdir mv pwd rm rmdir sed test touch

Stick to the generally supported options for these programs. For example, don’t use `mkdir -p’, convenient as it may be, because most systems don’t support it.

The Makefile rules for building and installation can also use compilers and related programs, but should do so via make variables so that the user can substitute alternatives. Here are some of the programs we mean:

ar bison cc flex install ld lex make makeinfo ranlib texi2dvi yacc

References:

Linux Kernel Makefile:
This is one of the best reference as it contains an example for almost every feature of make! 🙂

GNU Make Manual:
http://www.gnu.org/software/make/manual/make.pdf

Conventions:
http://www.math.utah.edu/docs/info/make-stds_1.html#SEC1

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s