SoC Logo HowTo
Use the Make Command Prof. Bartenstein
 

Contents:

Overview

Why use the make Command?

When writing and running a computer program, we often need to perform several operations, such as compiling, testing, and starting a debugger on a specific command. Each of these operations requires the invocation of special commands, such as the compile command, with several options. And, when debugging a ccomputer program, we often need to go through several of these steps in succession, getting all the steps right, and getting all the parameters specified correctly. If we do all this work manually, it's easy to forget something or make a mistake along the way. The make command allows us to put all of the information required for all the steps in a file, called a "make file", along with information about the dependencies between the commands. Once a make file is in place, we can run the make command to perform all the necessary steps to perform all the required operations.

If the make file is coded correctly, we can be guaranteed that every time we run the make command, all dependencies will be resolved, and all the correct parameters will be passed to each operation. That means we don't have to worry about forgetting an operation or mistyping a parameter. The result is an enormous savings in development time and frustration levels.

arrow_circle_up

Introduction to Make

The make command uses an ASCII text file called a make file to control how it works. By default, the make command looks for a make file with the name Makefile with no file extension in the current directory. We will always use this convention, but for more information on make-file file names, see Make-File File Names below.

The make file contains make rules. Each rule defines a target - the name of a file to be built or goal to be achieved, dependencies - a list of other targets or files which must be up to date in order for this target to be achieved, and a recipe - one or more UNIX commands required to create the target from the dependencies. When we invoke the make command, a target is chosen, the dependencies for that target are created if necessary, and if any of the dependencies are newer than the target, then the recipe is invoked to build the target - to create or recreate the target. (If all the dependencies are older than the target, make does nothing.)

For example, suppose I have C code in a file called myPgm.c, I use gcc to compile that program, I want to test my program with a command line parameter, and I want to be able to debug my program with gdb. Then, I could create a Makefile with the following contents:


test : myPgm
	./myPgm 72
	
gdb : myPgm
	gdb -ex ./myPgm 72
	
myPgm : myPgm.c
	gcc -ggdb -Wall -std=c18 -o myPgm myPgm.c

If I then run make, the make program will choose the "test" target, which has a depedency on the "myPgm" executable file. If no such file exists, make will invoke the gcc command to build the executable file. Then, make will run the myPgm executable with the command line argument 72.

If I invoke make gdb, the make command will check to see if the "myCmd" executable file is "up to date" (newer than it's dependency, myPgm.c). If so, the make command will run the gdb command in the gdb rule recipe. Suppose I find a bug in my program, exit gdb, and edit myPgm.c. I can then run make gdb again. The make command will see that the myPgm executable is a dependency, and that myPgm is dependent on myPgm.c, which is newer than the executable file. The make command will then rerun the gcc command to rebuild the myPgm executable file before running the gdb command!

arrow_circle_up

Invoking the make Command

If make is invoked with no parameters and if there is a target named "all" in the make file, then make chooses that target. If there is no target named "all", then the make commands uses the target in the first rule in the make file. Since we often put the top level rule first in the make file, this is almost always the right choice. However, if we want to do something different, we have to specify at least one parameter on the make command.

If we invoke make and specify the name of a target as a single parameter, such as make clean or make gdb, the make command uses the first parameter as the name of the target to build. Clearly, there should be rules with targets of clean or gdb in the make file.

It is also possible to specify flags and values before the target when invoking the make command. The flags can be used to override specific behavior of the make command. For example, if we invoke make -f MyMake.txt clean, that will use the file MyMake.txt as the make file instead of the default Makefile, and run the rule that specifies a clean target in the MyMake.txt make file.

For more details on how to run make, see the GNU make manual, How to Run make section.

arrow_circle_up

Make Files

A make file often has two kinds of specifications. First, make files specify variable definitions. For more information on defining and using variables, see Using Variables in Make Files below.

Make files also contain make rules. The make rules tell the make command what to do and when to do it. The make rules often use the variables defined in the variable definitions. The first part of each rule in a make file defines the target of that rule. The target of a rule is either a specific file, such as an executable file built by the compiler, or a "pseudo-file", a name we use in place of a real file name. The rule defines what that target depends on, and how to create the target from those dependencies. When we run the make command, the make command figures out which target to build; whether any of the dependencies for that target need to be rebuilt, and whether any dependencies have changed so that target itself needs to be rebuilt. If so, the make command invokes the command or commands required to build the target. For more information about make rules, see Make File Rules below.

For details on overall make file syntax, see the GNU make manual Writing Makefiles section.

Make-File File Names

We will use a make file called Makefile in the current directory for everything we do. The make command itself looks for files called GNUmakefile, then makefile, then Makefile, and uses whatever it finds first in the current directory, unless you specify a -f or --file flag on the make command. Note that none of the default file names have a file type.

Warning - if you download a file such as Makefile from a web browser, the browser often thinks it knows better and adds a file type of .txt to the file, so you end up with a file called Makefile.txt in the current directory. Then, when you run the make command, the command reports that no make file could be found. To fix this, run the UNIX rename command: mv Makefile.txt Makefile to remove the .txt file type.

For more details, see the GNU make manual What Name to Give Your Makefile section.

arrow_circle_up

Using Variables in Make Files

Variables in make-files are just variable names that have a specific value. These variables can then be used in the make rules themselves. Variables are most often used in cases where the same information is used in several make rules. By putting them in variables, we can specify them at the top of the file. Then, if they ever need to be changed, we only have to change them in one place, rather than in all the rules that use that information.

Variables are also very useful when using built in rules. See the Built-In Make Rules section below for more details.

A variable is often defined at the top of a make file. The syntax is simply:

name = value

Where name is the name of the variable, and value is the value that should be associated with that variable.

Then, in a rule, we can reference the variable by using either $(name) or ${name}.

The make command pre-defines many variables to use in its own built-in rules. We often include variable definitions in our make files to override the default definition used by the make command. For example, if we want to use the gcc compiler to compile our C code, we can do this by adding:

CC=gcc

at the top of our make file. Then, when make file rules want to invoke a C compiler (for example, built-in makefile rules), it will use $(CC) instead of hard-coding a compiler. Then we can change the compiler just by changing the CC variable; for example CC=clang.

For all the details, see the GNU make manaul How to Use Variables section.

arrow_circle_up

Typical make targets

There are several de-facto standards for target names in make files, as follows:

all
A pseudo-file target that usually has dependencies on all executables to be built in the current "project".
clean
A pseudo-file target that removes everything created by the make file itself, without removing the basic input files such as the code files used to build executables in the current project. If you ever run into problems with the Makefile, such as incorrect dependencies, one solution is to run make clean and then rerun the make command that caused the problems.
test
A pseudo-file target that builds all the executables in the current project, and then runs those executables using pre-defined test cases.
install
We won't use this target, and if you find it, usually it doesn't make sense to run this target. This target builds all the executables in the project, and then makes those executables available to all users on the current system. In order to do so, the install target typically needs to write to disk space owned by the system rather than a specific user, and we typically don't have the authority to write into that space.

arrow_circle_up

Make File Rules

Make File Rule Components

Make file rules consist of three components, a target, a list of dependencies and a recipe. These are laid out in a make file as follows:

  
target : dependencies
      recipe line 1
      recipe line 2
      ...

  

The target is either a file name, or a "pseudo-target", an abstract name that does not correspond to a file. The dependencies are a list of blank separated targets and/or files. The recipe line or lines must be preceded by a tab. (Note that some editors automatically convert a tab character to multiple blanks. You must turn off this automatic conversion in order to work on make files!). You may have as many recipe lines as you need. The end of the rule is specified by a blank line.

The recipe consists of one or more UNIX commands that are used to create the target from the dependencies. For example to create an executable file called "hello" from an input file called "hello.c", you might code a rule as follows:

  
hello : hello.c
      gcc -g -Wall -o hello hello.c
  

This tells the make command that the hello executable file depends on the C code in hello.c, and to make the hello executable, make needs to run the command gcc -g -Wall -o hello hello.c.

For details on writing make rules, see the GNU make manual Writing Rules section. For all the details on writing recipes in rules, see the GNU make manual Writing Recipes in Rules section.

arrow_circle_up

How the make Command Uses Rules

If put the example "hello" rule above in a Makefile and run the command make hello, the make command will first compare the time/date stamp of the hello.c file against the time/date stamp of the hello executable file. If the hello executable file is newer than hello.c, then make considers the current version of hello "up to date", and does not execute the recipe. However, if hello.c is newer than hello, then you must have changed the C code after you last compiled, and the make command will run the recipe, gcc -g -Wall -o hello hello.c to recompile your code and produce an updated executable. If you get a non-zero return code from the gcc compiler, then make will stop right there. If there is no hello file in the current directory, then the make command considers that file as "ancient", and runs the recipe to update it. (Since pseudo-targets are never there, they are always considered "ancient", and the recipe is always run. For example, make test will always run the test recipe because there is no file called test in the current directory)

The beauty of the make command is that if you put a target in the dependency list, then the make command builds that sub-target before building the target you specified. For example, if there were another rule in your make file that specified:

  
test : hello
      ./hello
  

and you ran make test, then the make command would first run make hello to see if the hello.c file has changed since you last compiled it. If it has, then the make file will run the gcc command in the hello recipe. Then, make will continue running the test target, find that there is no test file, so determine that it needs to run the test recipe, ./hello.

Often times, make files contain several levels of cascading rules. This ensures that all dependencies are up to date before checking to make sure that the top level target is up to date. We no longer have to remember that every time we change our C code, we need to recompile. The make command with correct dependencies remembers that for us, and only recompiles when it needs to.

There is a section in the GNU make manual called How make Processes a Makefile that may be useful for more details.

arrow_circle_up

Built-In Make Rules

The make command has an extensive list of built-in rules. These rules allow you to do things like create an executable file from C code in a .c file without having to specify a recipe. There is a built-in rule that specifies:

  
x : x.c
      $(CC) $(LDFLAGS) $(CFLAGS) -o x $(LOADLIBES) x.c
  

This rule says that if you run the command make helloWorld, then the make command will look for a file called helloWorld.c. If such a file is found, and the helloWorld executable is out of date, then the make command will run the recipe, substituting for the CC, LDFLAGS, CFLAGS, and LOADLIBES variables. If we have defined the relevant variables correctly, we will get gcc -g -Wall -o helloWorld helloWorld.c, even though we never specified that rule in our Makefile.

For more details, see the GNU make manual Using Implicit Rules section.

arrow_circle_up

Make Command References

This web page covers the basics, but there's a lot more about make and make files out there. For more detail, here are some recommended web sites to look up:

GNU Make Manual
This is the most complete and decisive reference for the make command. There's a lot in here, and sometimes it's hard to find what you're looking for, but it's all there. I use the "HTML - entirely on one page" version so I can do searches from my web browser.
Wikipedia Make (software)
The Wikipedia page about make. Note that this covers several different implementations of make, including GNU make (gmake), which is the one we will be using.
Learn Makefiles
A tutorial on using Makefiles "with the tastiest examples". Covers both elementary and sophisticated uses of make.
Tutorialpoint's Learn Makefile
Another makefile tutorial.
devhints.io Makefile cheatsheet
A Makefile cheat sheet - useful for quick references, but covers some pretty sophisticated make capabilities.

arrow_circle_up