Makefiles

It now takes three lines to compile our program, which is a pain to have to type every time we make a change. We can simplify compilation by placing all of the compilation instructions in a single file called a Makefile (with NO extension).

Here is the format of a Makefile:

compiler declaration
compiler flags declaration
executable name declaration
header list declaration

object list declaration

compiling/linking instruction
cleaning instruction (removing output files)

(There are many other options for creating Makefiles, but we will use the template above in this class.) Here is the Makefile for our statistics program

//saved in a file name Makefile, with no extension
CC = gcc
CFLAGS = -g -Wall
EXE = nums
HEADERS = stats.h
CODE = stats.c prog.c

OBJECTS = $(CODE:.c=.o)

nums: $(OBJECTS) $(HEADERS)
    $(CC) $(CFLAGS) $(OBJECTS) –o $(EXE)

clean:
    rm -f *.o *.exe *.out

Now, let’s go through the Makefile one line at a time:

CC = gcc

This line declares the variable CC, and says that we’re using the gcc compiler.

CFLAGS = -g -Wall

This line declares the variable CFLAGS and states that we will use two compiler flags – -g and -Wall. You don’t have to include this line, but it can be useful. The -g option means that our executable is always built with debugging information, so that we can debug it in gdb. The -Wall option tells the compiler to display warnings. Often times these compiler warnings can give insight into why a program isn’t working, as C will let you do many things that you probably don’t intend to do (especially with pointers).

EXE = nums

This line declares the variable EXE and states that we wish to name our executable “nums”, instead of the default “a.out” or a.exe". We can replace “nums” with whatever we want to use as our executable name. Again, this line is optional.

HEADERS = stats.h

This line declared the variable HEADERS and lists all the (user-created) header files used in the program.

CODE = stats.c prog.c

This line declares the variable CODE and lists all the code files (.c files) used in the program.

OBJECTS = $(CODE:.c=.o)

This declares the variable OBJECTS and instructions to create an object file (.o) for every .c file listed in the CODE variable. (An object file is a single file converted to machine code.) So, since we have the files prog.c and stats.c, will we get the object files prog.o and stats.o.

nums: $(OBJECTS) $(HEADERS)
    $(CC) $(CFLAGS) $(OBJECTS) –o $(EXE)

This line describes how to compile our program. Thenums at the beginning is a name we’re giving this compilation instruction – we could have called it anything. When we put $(OBJECTS) on the top line (and similarly for other variables), this gets the value out of the OBJECTS variable. The top line, $(OBJECTS) $(HEADERS), states that our program is dependent on all the header files and all the object files. This line says that to compile the program, all the object files must first be made. By listing the header files as a dependency as well, it ensures that if a change was made to a header file then the entire program will be rebuilt.

When we substitute the values of the variables, the second line becomes:

gcc -g -Wall prog.o stats.o –o nums

This line says to link together the two object files, and rename the executable to nums. (That’s the –o nums part). It also says to build the executable with debugging information (g) and to display compiler warnings (-Wall).

Next, we have:

clean:
    rm -f *.o *.exe *.out

This line says that to clean our program, we want to delete the object files (*.o), all executable files (*.exe and *.out). The -f option ensures that we will not see a warning if there are no files to remove that match one of the wildcards.

Using a Makefile

Now that we have a Makefile, we want to use it to compile our program. To compile a program with a Makefile, type:

make

This will generate all the object files, link them together, and create the executable called nums.

To run our program, we do:

./nums

This is the same as what we’ve done before to run our program, but now the executable is named nums instead of a.exe.

To remove all the output files, we type:

make clean

This will remove all .o files and executable files.

Makefile Rules

We could spend the rest of the semester talking about exactly how Makefiles work and different intricacies and still not cover everything. Makefiles are a very powerful tool – we’ve only scratched the surface to be able to compile our program in a specific way.

Here are some rules to follow as you write your own Makefiles:

  • You should always define the variables CC, HEADERS, CODE, and OBJECTS. The value of HEADERS should be a list of all .h files in your project, and the value of CODE should be a list of all .co files in your program. Your OBJECTS value should always be the same – $(CODE:.c=.o) (create an object file for every .c file).
  • The CFLAGS declaration is optional, but recommended
  • The EXE declaration is optional – if you leave it off (along with the -o $EXE in the compilation instruction), then your executable will be named a.exe (or a.out).
  • There should be a tab character at the beginning of the second line for the nums: instruction and the clean: instruction. This MUST be a tab – spaces won’t work.
  • For the most part, your Makefiles will remain the same from program to program. You’ll just change the HEADERS and CODE lists and change the name of the executable.

Getting make

The make tool may have already be installed when you installed the gcc compiler. To test, open a terminal and type:

make

If you see an error that says something about “No targets specified and no makefile found”, then make is correctly installed. If you see an error that says something like: “The term make is not recognized…”, then make is either not installed or the PATH variable does not include its location.

Windows users

For Windows users who don’t already have make, open an MSYS2 terminal and type:

pacman -S make

Then, you will need to add the location of make.exe to your PATH environment variable. Look in both C:\msys64\mingw64\bin and C:\msys64\usr\bin – copy the address of whichever location contains make.exe.

Click Start, type Environment variables, and select “Edit the system environment variables”). Click “Environment Variables..”, then find Path under System variables and click “Edit…”. Click “New”, paste in the address you copied in the previous step, and click OK three times to dismiss each frame.

Mac users

For Windows users who don’t already have make, open the terminal and enter:

xcode-select --install