Skip to content

make

Makefile

Makeifles are a simple way to organize code compilation. This tutorial offers a very basic idea of what is possible using make. For this tutorial, please download three files, a main program (hello.c), a functional code (hellofunc.c), and an include file (hello.h) [hello.c, hellofunc.c, hello.h] under the 'hello' directory. To compile these codes, you would use the following command:

1
gcc -o hello hello.c hellofunc.c -I.

This command compiles the two c files, and names the executable hello. With the '-I.' flag, gcc will look in the current directory for the include file 'hello.h'. With only two .c files, it is easy to compile with the above approach, but with more files, it is more likely having typos. In addition, if you are only making changes to one .c file, the above approach recompiles all of .c files every time which is time-consuming and inefficient.

So it is time to learn how makefile will be helpful for such cases. First, create a file which has the following two lines. (The filename should be makefile or Makefile), and put it under the 'hello' directory.

1
2
hello: hello.c hellofunc.c
      gcc -o hello hello.c hellofunc.c -I.

Now, type make on the terminal and check if the executable is created. The make command will execute the compile command as you have written it in the makefile. Note that make with no arguments executes the first rule in the file. Furthermore, by putting the list of files on which the command depends on the first line after the ':', make knows that the rule hello needs to be executed if any of those files change. One very important thing to note is that there should be a tab before the gcc command in the makefile (multiple spaces do not work!). There must be a tab at the beginning of any command, otherwise, you will get a lot of errors.

Can we make it a little bit more efficient? Let's modify our makefile as following:

1
2
3
4
CC=gcc
CFLAGS=-I.
hello: hello.o hellofunc.o
    $(CC) -o hello hello.o hellofunc.o

In this makefile, we define CC and CFLAGS, which are special macros communicating to make how we want to compile the files hello.c and hellofunc.c. In particular, CC is for the C compiler, and CFLAGS is the list of flags to pass to C compiler. By putting the object files (hello.o and hellofunc.o) in the dependency list and in the rule, make knows it must first compile the .c files individually, and then build the executable hello. If your project is small, like consisting of a few separate codes, this form of makefile is enough to handle the set of codes. However, this makefile misses include files. For example, if you made a change to 'hello.h' make would not recompile the .c files, even though they needed to be. In order to fix this problem, we need to tell make that all .c files depend on certain .h files. It can be done by writing a simple rule and adding it to the makefile.

1
2
3
4
5
6
7
8
9
CC=gcc
CFLAGS=-I.
DEPS = hello.h

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hello: hello.o hellofunc.o
    $(CC) -o hello hello.o hellofunc.o

This addition first creates the macro DEPS (the macro name does not have to be DEPS. You can use any name.), which is the set of .h files on which the .c files depend. Then we define a rule for all .o files. The rule says that the .o file depends on the .c files, and the .h files which are included in the DEPS. Next, the rule says that to generate the .o file, make needs to compile the .c file using the compiler defined in the CC. The -c flag says to generate the object file, the -o \$@ says to put the output of the compilation in the file named on the left side of the :, the \$< is the first item in the dependencies list, and the CFLAGS macro is defined on the 2nd line.

For the simplification, you can use special macros \$@ and \$^, which are the left and right sides of the :, respectively, to make the overall compilation rule more general. In the example below, all of the include files should be listed as part of the macro DEPS, and all of the object files should be listed as part of the macro OBJ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
CC=gcc
CFLAGS=-I.
DEPS = hello.h
OBJ = hello.o hellofunc.o

%.o: %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hello: $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS)

Now you have a good sense of makefile. For more information on makefiles and the make function, check out the GNU Make Manual, which will tell you everything on makefile.

You can download some makefile examples using getexample in our HPC.