Makefile Tutorial
Programmers are lazy, and when you start to create multiple source files, you don't want to have to type gcc filename1.c
, gcc filename2.c
, gcc filename3.c
, etc. That would be inefficient, boring, and tedious, and wouldn't give you the chance to get up, stretch, eat, and scroll through Reddit. In order to automate this process, we use a utility called make
, which will do all the necessary compiling and linking for us with a simple command. You might think of this as some sort of evil Linux or C black magic; however, make
is a useful utility which has been ported over to most operating systems and can be used by programming languages including Java, C, C++, and many others. In order to use the make
utility, you need to create a file called makefile
.
Dependency Specification
Dependencies are the heart and soul of a makefile. Without dependencies, your makefile would do a whole lot of nothing. The dependencies tell make
what needs to be done to get an executable. Think of them as the rules that must be followed in order to create the ultimate executable. They have the following syntax.
result: dependencyA dependencyB dependencyC ... commands to process dependencyA, dependencyB, dependencyC, ... to generate result
The following is an example of a simple makefile. It says that the target, program
, depends on helloworld.c
. The command needed to process helloworld.c
is to run the gcc
compiler on helloworld.c
, using the -o
flag to name the output program
. There is also a rule which defines how to clean up the process, in this case to remove program
.
It's very important to note that the space before the line gcc helloworld.c -o program
is actually a tab.
The use of tabs to define what command is executed by a rule is not optional. You must use a tab on the line after each dependency list to make a makefile
work.
program: helloworld.c gcc helloworld.c -o program clean: rm -f program
Variable Definition
In addition to dependency specification, a makefile
can optionally contain variable definitions. To define a variable you use the following syntax.
VARIABLE = value
One useful variable can specify the compiler and the flags that you would like to use. Let's say you need to send different flags to different commands, or use one compiler such as g++
on some files and gcc
on other files.
CC = gcc
This assigns the variable CC
to have the value gcc
. In order to obtain the value from a given variable, you expand the variable with dollar sign syntax.
${VARIABLE}
Thus, you might use ${CC}
to refer to its value, gcc
.
The following is an example with variables.
CC = gcc EXEC = helloworld ${EXEC}: helloworld.c ${CC} helloworld.c -o ${EXEC} clean: rm -f ${EXEC}
Miscellaneous
Makefiles should be named makefile
or Makefile
. If you type the make
command in a terminal, it will look for a file with one of these names. It will try to create the target at the top of the file. If you want to create a specific target, you can specify that target (by typing make program
or make clean
, for example).
The make
utility has some basic intelligence built into it. If it's told to make a target, it will look at the dependencies and only rebuild items whose sources are newer than the existing output. This means that the make
command will generally not recompile files that have not been updated.
Don't forget the tab! There has to be a tab before the command is given in the dependency. You can specify multiple commands on separate lines under a single dependency, if they each are preceded by a tab.