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.