Creating Makefiles
2003-07-02 23:09:20
Category: tutorial:general:
Description: As you begin to work on larger projects you will find it necessary to have a means to organize the compilation of your source code. Having to compile multiple files from the command line can become a tedious and time consuming task. The GNU Make program will make this process much easier to deal with. This tutorial will cover the specifics you need to create your own Makefiles.
Author: detour
Viewed: 1697
Rating: (4 votes)


__MHEAD__Managing Projects Using Makefiles__MHEAD_END__

__HEAD__Introduction__EHEAD__
     As you begin to work on larger projects you will find it necessary to have a means to organize the compilation of your source code.  Having to compile multiple files from the command line can become a tedious and time consuming task.  The GNU Make program will make this process much easier to deal with.  If you have ever compiled or installed software on your linux system the chances are you have run ‘make’ to compile it for you.
     When you run ‘make’ it looks for a file named ‘Makefile’, this file holds a set of rules for compiling the project.  The files you are compiling are automatically examined to detect changes, if a change has been made or a certain file is not present it will be compiled, otherwise it’ll be left unchanged.  This is very useful when you start making very small changes to a large program, you only need to recompile the files you made changes too and then link them together.
     This tutorial will go over the basic steps for creating your own Makefiles and getting a handle on compiling your multifile projects.

__HEAD__Basic Makefile__EHEAD__
__START_BLOCK__
__FGREEN__# Basic Makefile 

# all is the default target and is called when just ‘make’ with
# no options is run.__EFONT__
all: myprog

__FGREEN__# define myprog and the files it requires to compile.  If main.o or
# file.o are not up to date they will be called upon to be updated before
# myprog is compiled together.  Note that tabs are required__EFONT__
myprog: main.o file.o
	g++ -o myprog main.o file.o

__FGREEN__# to compile main.o the file main.cpp is checked for updates.  If main.cpp
# is not present an error will be presented.  If main.cpp has not changed
# since the last build it will not be recompiled.__EFONT__
main.o: main.cpp
	g++ -Wall -c -o main.o main.cpp

file.o: file.cpp
	g++ -Wall -c -o file.o file.cpp
__END_BLOCK__
     Using this Makefile you can compile your program in one simple step.  The files ‘main.cpp’, ‘file.cpp’, and ‘file.h’ must exist or an error will occur.  Files are checked up to date by comparing their last time modified with its dependencies.  So when main.o is checked if it is older then main.cpp it will be compiled, otherwise nothing will happen.  This rule is applied to all of the compile directives.  You should also note the gaps before the compile line are tab's and are required so it doesn't get confused with other dependencies.

When ‘make’ is run for the first time you should see the following happen.

__START_BLOCK__
$ make
g++ -Wall -c -o main.o main.cpp
g++ -Wall -c -o file.o file.cpp
g++ -o myprog main.o file.o
__END_BLOCK__
Now run make again.

__START_BLOCK__
$ make
make: Nothing to be done for `all'.
__END_BLOCK__
     If you make a change to file.cpp and run ‘make’ again, file.o will be older then file.cpp so it will be compiled, and myprog will be older then file.o so it will be compiled.

__START_BLOCK__
$ touch file.cpp
$ make
g++ -Wall -c -o file.o file.cpp
g++ -o myprog main.o file.o
__END_BLOCK__ 
__HEAD__Variables__EHEAD__
     Sometimes you need to make changes to the options you pass to the compiler, like adding or removing -DDEBUG to define debugging for your program.  It would be silly to hand edit every single compile directive to make changes.  This is where variables to define your compiler and compiler options will make your life simplier.

     The following will assign the value "gcc" to the variable CC, append " -o" to it and then display the value.  All variables must first be defined before you can access them.  $ or $(VARIABLE) is the notation used to access a variable.

__START_BLOCK__
CC = gcc
CC += -o

all:
        @echo $
__END_BLOCK__
     A typical Makefile will define your compiler name, compiler flags, and compile line in variables.  Below is a modified version of the first Makefile.

__START_BLOCK__
__FGREEN__# Basic Makefile with variables__EFONT__

CC = g++
CFLAGS = -Wall -O2
COMPILE = $(CC) $(CFLAGS) -c

all: myprog

myprog: main.o file.o
	$(CC) -o myprog main.o file.o

main.o: main.cpp
	$(COMPILE) -o main.o main.cpp

file.o: file.cpp
	$(COMPILE) -o file.o file.cpp
__END_BLOCK__

     This will allow you to change your options without having to modify every single line.  We can also take this a step further, you will notice that main.o and file.o are compiled in the same manner with just different filenames.  Makefiles enable you to use wildcards to match file names and compile multiple files with one definition.  Consider the following.

__START_BLOCK__
__FGREEN__# Automated Makefile__EFONT__

CC = g++
CFLAGS = -Wall -O2
COMPILE = $(CC) $(CFLAGS) -c
OBJFILES := $(patsubst %.cpp,%.o,$(wildcard *.cpp))

all: myprog

myprog: $(OBJFILES)
	$(CC) -o myprog $(OBJFILES)

%.o: %.cpp
	$(COMPILE) -o $@ $<
__END_BLOCK__
     This Makefile will compile any .cpp file in the current directory.  The first change you see from the previous example is the OBJFILES variable.  This variable is filled with .o targets using the wildcard command and patsubst.  The $(wildcard *.cpp) will retrieve any .cpp file in the current directory and store it in a variable.  The patsubstr functions is used to convert a file from one format to another.  In this case each .cpp file is converted into a .o extension and then stored into OBJFILES.  This variable is then used to compile each .cpp file.
     Instead of having main.o: main.cpp we now have %.o: %.cpp.  Any target that has a .o extension will goto here.  In the compile line you will see two new variables $@ and $<.  $@ will match the target and the $< will match the dependency, so basically $@ will be replace with main.o and $< will be replaced with main.cpp.