Creating and using Makefiles


Makefiles are scripts used to automate the process of building pieces of code.  Makefiles include a set rules that are used by the make program to figure out how to build a program.  These scripts are specially useful when building systems that include several programs or when building programs that include multiple source codes.  This brief document describes the basics about Makefiles.  We use the same example discussed in the Using C and C++ document as an example.

In Using C and C++ we described the process of manually compiling a program named myprog.  Myprog is created by compiling and linking three C programs:  main.c, stack.c, and routines.c.

At the core of a makefile are the dependency rules.  Each rule is composed of three parts: the target, the dependency list, and the action.  The target is the program (or file) that will result when the rule is fired.  The dependency list mentions all other files that are needed to build the target.  The action is a list of commands that can be used to convert the files in the dependency list into the target (e.g., how to compile a .c file into a .o file).  The form of a rule is the following:

target: dependency_list
    action

The action part can include several actions.  A peculiar syntactical requirement of all rules is that the actions must be preceded by a TAB character.  That indentation in the action part MUST BE A TAB.

A simple makefile

Here is a simple makefile that can be used to build out myprog program.  We now discuss each part of that makefile in details.

     1  # (Comments in makefiles start with a '#').
     2  # A simple makefile.  Rename the programs as needed and add as many
     3  # new rules as necessary.
     4  #
     5  # Written September 1998 by Mauricio A. Hernandez
     6  #
     7
     8  #
     9  # Main Rule.  Builds the executable 'myprog' from the .o files.
    10  myprog: main.o stack.o routines.o
    11          gcc main.o stack.o routines.o -o myprog
    12
    13  #
    14  # Rules for each object file.
    15  #
    16  main.o: main.c
    17          gcc -c main.c
    18
    19  stack.o: stack.c
    20          gcc -c stack.c
    21
    22  routines.o: routines.c
    23          gcc -c routines.c
    24

Lines 1-6, 8-9 are comments.  The first rule is in lines 10-11.  The first rule in the makefile will be, by default, the main target of the makefile.  (i.e., the make program will try to build that first target only).
The first rule can be read as follows:  To build myprog, I need to build first main.o, stack.o, and routines.o.  Once I have built all those three, I can "gcc main.o stack.o routines.o -o myprog" to build myprog.

The beauty of the make program is that it will only execute the minimum number of rules that are needed to build the main target.  Lines 16-23 detail how to build each .o from its .c.  Make will only use these rules when needed.  That is, it will not re-attempt to recompile, for example, main.c, if you have not made changes to it since the last compilation.  Make can easily know this by comparing the last modification date of the .c with the corresponding .o.  If the modification date of the .c is greater than the .o, make will know that it needs to rebuild that .o from the .c.  Otherwise, make considers the .o to be "up to date" and does not execute the rule.  In general, a rule action is only executed if at least one of the files in the dependency list is not "up to date" with respect to the target.

To run the above makefile, you must do the following.  First, rename the makefile.sample file to just makefile.  Make sure the makefile is in the same directory where the source code resides.  Then, at the shell prompt, type make.

    $ make
    gcc -c main.c
    gcc -c stack.c
    gcc -c routines.c
    gcc main.o stack.o routines.o -o myprog
    $

The first time you run the make program, you should get a complete compilation of your program.   Now, using your favorite editor, make a small change to, say, stack.c (e.g., add an extra space somewhere).  Run make again and see the results.

    $ make
    gcc -c stack.c
    gcc main.o stack.o routines.o -o myprog
    $

Notice that only two rules fired: the one that build stack.o and the one that build myprog.  Since there was no change in main.c and routines.c, make knew that it did not need to rebuild them.

A more general-purpose makefile

The makefile discussed in the previous section will probably work for most of your programming assignments at UIS.  However, you will notice that you need to change a lot of names and add/delete rules  in order to adapt the makefile to every new program.  In this section we discuss a more general-purpose makefile that will adapt from homework to homework with only a few changes (mostly the names of the source code files and the target name).

     1  #
     2  # A more general purpose makefile.  Change the values the variables to
     3  # whatever is needed for your project and let the rules do the rest!
     4  #
     5  # Written September 1998 by Mauricio A. Hernandez
     6
     7  # The name of the compiler
     8  CC=gcc
     9  # Flags used by the compiler when creating object code
    10  CFLAGS=-g
    11  # Flags used by the compiler when linking
    12  FLAGS=
    13
    14  # The name of your executable
    15  TARGET=myprog
    16
    17  # The names of the .o files you need to build.
    18  OBJS=   main.o  \
    19          stack.o \
    20          routines.o
    21
    22  #
    23  # The Rules
    24  #
    25  all: $(TARGET)
    26
    27  $(TARGET): $(OBJS)
    28          $(CC) $(FLAGS) $(OBJS) -o $@
    29
    30  .c.o:
    31          $(CC) -c $(CFLAGS) $<
    32
    33  clean:
    34          rm $(OBJS) $(TARGET)
    35
    36  # Done.

Lines 7-20 define some variables that will be used by the rules.  Start by selecting the name of the compiler on line 8.  For instance, if you want to use the C++ compiler, that line should read CC=g++.  Put all the flags the compiler will need when compiling the .c into .o's.  In this case, the "-g" flag will be used to allow debugging of the program.  Line 12 are the flags used by the linker.  In this case, no flags are needed.  Line 15 defines the name of the executable and lines 18-20 define the names of all the .o's needed to build the target.  This is probably all you need to change to adapt this makefile to your homework.

Lines 22-36 define the rules used by the make program when executing this makefile.  The first rule (line 25) says that to build "all", you must build TARGET.  I use this "all" rule as my default main rule since it allows me to put multiple TARGET programs if needed.  However, if you are only building one program with this makefile, you do not need that rule.  The second rule (lines 27-28) teaches make how to compile the TARGET given the OBJS files.  The "$@" in the action part is replaced by the target of the rule.

Lines 30-31 is a rule that is slightly different from the dependency rules we have seen so far.  This rule can be read as saying "To convert a .c file into a .o file we do the following".  This rule applies to all .o files that you need to build.  Make will notice that to build a .o, you will need a .c and use this rule to compile the .c.

Finally, lines 33-34 is a useful rule that deletes the target program and all the .o files generated by the compilation.  This is a useful rule when you want to make a complete recompilation of your program.  To use the rule simply type at the shell prompt:

    $ make clean
    rm main.o stack.o routines.o myprog
    $

A quick reminder: All the indentations you see in the example makefiles are TABs.  Do not use spaces to indent your actions!

More Information

Make and Makefiles are complex beasts with lots of features we cannot cover in this basic introduction.  Perhaps the best way of learning more about make and makefiles in reading the online manual pages.  Just type "man make" at the shell prompt.  You may also take a look to an online manual for GNU make.


Home | Information | Academics | People | Resources | Contact

The Department of Computer Science at the University of Illinois at Springfield
Copyright© 2002 University of Illinois at Springfield