Home ->
Unix Programming Tools
make is a standard POSIX program to read in a Makefile and execute one or more rules to compile your program.
A Makefile consists of a series of variables and rules. Each rule has three different parts.
rule1: dependency1 dependency2
action
rule2: dependency1 dependency2 dependency3
action
- If the dependency exists, but is newer than the last time the target being built, then it will rebuild it, as one of its files has changed.
- If the dependency does not exist, then make will look for a rule with that dependency's name and execute that. When make finishes that action to build the dependency, then it will verify it exists and continue down the chain of dependencies.
Example: Let's look at a Makefile to build a simple C program called hello.c
hello: hello.c
gcc -o hello hello.c
We would then execute make hello
, or even just make
as hello is the first rule, in our code directory to build the program.
kandrea@zeus-1:~/tmp/doc$ ls
hello.c Makefile
kandrea@zeus-1:~/tmp/doc$ make hello
gcc -o hello hello.c
kandrea@zeus-1:~/tmp/doc$ ls
hello hello.c Makefile
kandrea@zeus-1:~/tmp/doc$
This is nice, but can be much more useful with variables.
In a Makefile, a variable can be created by making an assignment using the = symbol. These variables can then be used by referencing them within a $() enclosure.
These variables are commonly used to store frequently used options for compiling, as shown here:
CFLAGS=-g -Wall -Og
CC=gcc
hello: hello.c
$(CC) $(CFLAGS) -o hello hello.c
This now supports more common options. By running make, hello will still be built using hello.c, however, now we are also building it for GDB debugging with the -g flag, for debug-level optimization with -Og, and with all warnings turned on with -Wall. One nice thing here is that we can now add many more rules that all use the same common options.
The last little change is when we look at the action line, the hello.c depenency is still hardcoded in to the action. Makefiles support several standard built-in variables that can be used in its place:
hello.o:
, then $@ would become hello.o
hello.o:
, then $* would become hello
hello.c hi.c yo.c
, then $< would be hello.c
hello.c hi.c yo.c
, then $^ would be hello.c hi.c yo.c
Here's an example in action:
CFLAGS=-g -Wall -Og
CC=gcc
hello: hello.c
$(CC) $(CFLAGS) -o $@ $^
This example will build hello using the following action:
kandrea@zeus-1:~/tmp/doc$ make
gcc -g -Wall -Og -o hello hello.c
kandrea@zeus-1:~/tmp/doc$
While nice, the real power of make is when you have to build a program using multiple source files. Those are easy with a Makefile, since all you need to do is add them to the dependencies and then use the appropriate built-in variables and any custom action you need to write. Once all of that is written, even the most complicated program can be re-built using just a single call to make
from the command line.