makefiles

makefiles

[ makefiles | Structure | Comments | Explicit rules | Implicit rules | Variables | Automatic Variables | Examples | Links ]

The purpose of makefiles is to help you with your coding projects. One will often have a program which have, say, several header files and a number of C source files. If you then make a change to one of the files, you will have to recompile all the files. This is handled by the program make, following the commands in your makefile - it will only recompile the files which contains changes, and also makes it easier and faster to give the order to compile everything. You no longer have to write long and complex compiler commands every time.

Usually, the file that contains all the commands to make is called makefile or Makefile. You can then just invoke make and it will follow these instructions. You can, however, give the file any name. If you call it something else you will have to use make -f <name of makefile> to call it.

Structure

A makefile consists of three sections: target, dependencies, and rules. The target is normally either an executable or object file name. The dependencies are source code or other things needed to make the target. The rules are the commands needed to make the target.

A simple makefile might be structured like this:

# Comments are preceded by the hash symbol
target: dependencies
        command 1
        command 2
        ...
        command n

A makefile can also contain definitions of variables and inclusion of other makefiles. The variables in makefiles may be overridden in the command-line arguments that are passed to the make utility. An example is that the variable "CC" is often used in makefiles to refer to a specific C compiler, and the user may wish to provide an alternate compiler to use.

A very important thing to remember about makefiles are that every rule line begins with a tab, not spaces. Otherwise make might think it is some sort of dependency.

Comments

Any line that begins with a # is a comment and will be ignored.

Explicit rules

These are rules that tell make which files depend on the compilation of other files, as well as the commands required to compile a particular file. They have this form:

targetfile : sourcefiles
# NOTE: there is a TAB character before each command.
	commands           

This rule means that to create the targetfile, make must perform the listed commands on the sourcefiles.

Example

main: main.c test.h List.h
	pathcc -o main test.h List.h

The above example shows a rule that says that in order to create the target file main, the source files main.c, test.h and List.h have to exist, and make should use the command:

pathcc -o main test.h List.h

to create it.

Implicit rules

Implicit rules are in many ways like explicit rules, they are just listed without commands. This means make will use the suffixes on the files to determine which commands to perform.

Example

test.o: test.c io.h

This will cause the following command to be executed:

$(CC) $(CFLAGS) -c test.c io.h

Variables

It is possible to define variables in a makefile. To do this use the following command:

VAR_NAME=value

It is a convention to write variable names in uppercase. For instance:

OBJECTS=main.o hello.o test.o

In order to get a variables value, put the symbol $ before the varables name, like this:

$(VAR_NAME)

Automatic Variables

(Much of this section taken from http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables)

Suppose you are writing a pattern rule to compile a ‘.c’ file into a ‘.o’ file: how do you write the ‘cc’ command so that it operates on the right source file name? You cannot write the name in the recipe, because the name is different each time the implicit rule is applied.

What you do is use another feature of make namely automatic variables, which are computed afresh for each rule that is executed, based on the target and prerequisites of the rule. For example, you would use ‘$@’ for the object file name and ‘$<’ for the source file name.

Note that automatic variables:

  • only have values within the recipe - you cannot use them anywhere within the target list of a rule (they have no value there and will expand to the empty string).
  • they cannot be accessed directly within the prerequisite list of a rule. A common mistake is attempting to use $@ within the prerequisites list; this will not work.
Automatic variables
$@ The file name of the target of the rule. If the target is an archive member, then ‘$@’ is the name of the archive file. In a pattern rule that has multiple targets, ‘$@’ is the name of whichever target caused the rule’s recipe to be run.
$% The target member name, when the target is an archive member. For example, if the target is foo.a(bar.o) then ‘$%’ is bar.o and ‘$@’ is foo.a. ‘$%’ is empty when the target is not an archive member.
$< The name of the first prerequisite. If the target got its recipe from an implicit rule, this will be the first prerequisite added by the implicit rule.
$? The names of all the prerequisites that are newer than the target, with spaces between them. For prerequisites which are archive members, only the named member is used
$^ The names of all the prerequisites, with spaces between them. For prerequisites which are archive members, only the named member is used. A target has only one prerequisite on each other file it depends on, no matter how many times each file is listed as a prerequisite. So if you list a prerequisite more than once for a target, the value of $^ contains just one copy of the name. This list does not contain any of the order-only prerequisites; for those see the ‘$|’ variable, below.
$+ This is like ‘$^’, but prerequisites listed more than once are duplicated in the order they were listed in the makefile. This is primarily useful for use in linking commands where it is meaningful to repeat library file names in a particular order.
$| The names of all the order-only prerequisites, with spaces between them.
$*

The stem with which an implicit rule matches. If the target is dir/a.foo.b and the target pattern is a.%.b then the stem is dir/foo. The stem is useful for constructing names of related files.

In a static pattern rule, the stem is part of the file name that matched the ‘%’ in the target pattern.

In an explicit rule, there is no stem; so ‘$*’ cannot be determined in that way. Instead, if the target name ends with a recognized suffix, ‘$*’ is set to the target name minus the suffix. For example, if the target name is ‘foo.c’, then ‘$*’ is set to ‘foo’, since ‘.c’ is a suffix. GNU make does this bizarre thing only for compatibility with other implementations of make. You should generally avoid using ‘$*’ except in implicit rules or static pattern rules.

Example:

all: file.c main.c

Here:

  • $@ evaluates to all
  • $< evaluates to file.c
  • $^ evaluates to file.c main.c

Examples

Hello: Hello.o
	cc -o $@ $<
 
Hello.o: Hello.c
	cc -c -o $@ $<
 
.PHONY: clean
 
clean:
	rm -f Hello Hello.o
 

This small makefile is started with either make or make Hello. It does one more thing than just compile the program - there is also the target 'clean' which is used to remove all the files generated by the compilation with one command: make clean.

The first line in this makefile is the dependendencies (Hello: Hello.o), while the second line contains the rule to create the target Hello. The third line the dependencies for making Hello.o are listed, while line 4 contains the rules to make the target Hello.o in line 3.

Note that the PHONY tag is a technicality that tells make that a particular target name does not produce an actual file. It is not strictly necessary to include the line with the PHONY tag, unless a file named clean exists in the current directory. The $@and $<are two of the so-called internal macros and stand for the target name and "implicit" source, respectively. There are a number of other internal macros, which can be seen here.

main.exe : main.o foo.o
	pathcc main.o foo.o -o main.exe

main.o : main.c
	pathcc -c main.c

foo.o : foo.c
	pathcc -c foo.c

This simple makefile is run by giving the command make. The program always starts with the first rule it sees, in this case for main.exe. It then tries to build that first and finds dependencies. It will then check to see if any of those needs to be updated. This means, that the first time you call make for this makefile, it will run the following three commands:

pathcc -c main.c
pathcc -c foo.c
pathcc main.o foo.o -o main.exe

Let us assume you then edit the file foo.c and run make again. It will then only run these commands:

pathcc -c foo.c
pathcc main.o foo.o -o main.exe

Because there was no reason to recompile main.c, it will not do that.

makefile with variables

CC = pathcc
CFLAGS = -g -O2
OBJECTS = main.o foo.o

main.exe : $(OBJECTS)
	$(CC) $(CFLAGS) $(OBJECTS) -o main.exe

main.o : main.c
	$(CC) $(CFLAGS) -c main.c

foo.o : foo.c
	$(CC) $(CFLAGS) -c foo.c

There are many similarities between this makefile and the last. The difference is that many of the commands have been replaced with variables. Then, when make runs, it replaces the variables in the target, dependency, and command sections of the rules. This is often advantage, since it lets you specify the things that changes often in one place. One example is that it is very easy to change the compiler, since we are using the variable CC everywhere - and it is defined at the very top.

makefile with patterns

CC = pathcc
CFLAGS = -g -O2
OBJECTS = main.o foo.o

main.exe : $(OBJECTS)
	$(CC) $(CFLAGS) $(OBJECTS) -o main.exe

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

This is almost the same makefile as the one above, however, if you look in the last line you will see a pattern has been defined. This can be used every time make needs to compile any source. This is much simpler if we have many sources - otherwise we would have to write a rule for including the source for each of them. In the pattern, the % character is used to denote the part of the target and dependency that matches whatever the pattern is used for. The $< is a special variable meaning "whatever the dependencies are". There is another useful variable which we will often have use for. It is $@, which means "the target".

After this addition we can now add more source files in a simple manner, by just updating the line that defines the OBJECTS variable. The variable names CC and CFLAGS are part of make's built-in rules. To see a list of all of these, look at man make or run make -p. There is also a reference manual for make, which can be accessed by running info make. It will contain many examples and suggestions for building makefiles.

Links

Here is a list of links to more information about makefiles.

Updated: 2024-03-08, 14:54