Use Makefile to manage your project

Now project 2 is already there and has nearly identical file structure as project 1, I decide that a common project skeleton should be built.

A Glance At All Files

sample input file:
Primes.in

config file:
Doxyfile
Makefile — I’ll talk about this later.

source file:
main.c++
TestPrimes.h

autogenerated file:
html/*
Primes.out
TestPrimes.out
main.app — one for testing and one for the real program.

doxygen will generate html/*, g++ will generate main.app, valgrind and main.app will generate Primes.out and TestPrimes.out.

I beleive no one likes to type or copy&paste really long commands, this is where “make” command comes very handy.

How “make” Works

In short words, make helps run long commands or a series of commands in a *smart* way.

However, make itself doesn’t know anything about your project. You need to tell it how to build your project. What options to use, which compiler to use, which library to link to, and so on. You tell make all these infomation by writing a Makefile.

what a real world Makefile looks like

Makefile can become really long in a real world project, especially for cross-platform project. For some project, Makefile is generated by using other tools, for example autoconf&automake or cmake. For others, Makefile is completely written by hand.

A good example for the later case is the mplayer project. You can checkout their latest Makefile using svn.


svn export svn://svn.mplayerhq.hu/mplayer/trunk/Mak…

A Beginner’s Makefile

Now let’s forget about the complexity of real world project and get back to our simple two-source-file project.

Remember in project 1, we build the test program by using:

g++ -ansi -pedantic -lcppunit -ldl -Wall -DTEST main.c++ -o main.app

Now, how to write this build process in Makefile.


test: main.c++
“TAB here”g++ -ansi -pedantic -lcppunit -ldl -Wall -DTEST main.c++ -o main.app
“TAB here”./main.app

The second line must start with a TAB. I don’t know how to let wordpress blog prints it.

After that, run

make test

will build a main.app file using the long g++ command.

How to build the main program, add this to your Makefile:

main: main.c++
“TAB here”g++ -ansi -pedantic -Wall main.c++ -o main.app
“TAB here”./main.app”

now “make main” will build the main program.

You may already guess the syntax of Makefile.

target: related files
“TAB here”a command that build the target

That’s it for a simple build. And it already can help you a lot.
As you see, this file can be shared in all our projects, because main.c++ stays the same filename across our projects.

What’s the “Smart” Thing

make not only helps build your project, but also build in a smart way.

Now, try do another “make test” right after a “make test”. What happened. make doesn’t do anything at all.

Now edit the main.c++ and rerun “make test”. make detects the modification and a new main.app is generated.

So make will only rebuild a target if it’s related source files have changed. That’s usually a good thing to speed up build process.

More Targets

Now that main.app can be built by make, how about doxygen docs? Can it be built by make?

First, you need to know how to generate the docs yourself. Oh, easy command:

doxygen Doxyfile

Can we put something like this in our Makefile? Sure we can.

docs: Doxyfile
“TAB here”doxygen Doxyfile

Does doxygen depends on some other files? In other words, is there any other files that if they get changed, docs should be rebuilt? Because doxygen generates docs by using special comments in source files, if source files get changed, docs should also be updated. So, we should also add all the source files: main.c++ TestPrimes.h as related files. Now the docs target becomes:


docs: Doxyfile main.c++ TestPrimes.h
“TAB here”doxygen Doxyfile

More About Dependence

Up to now, what I put after the “target:” are all files.
Now think about that when you generate docs, you want to make sure all test cases is passing. Can you put a file name after “docs:” to tell make this infomation?

I can think of such a file without modifying source code. Fortunately make allows a target depends on other targets. What if we make “docs” depends on “test”.


docs: Doxyfile main.c++ TestPrimes.h test
“TAB here”doxygen Doxyfile

Now before make makes “docs” target, it will first make “test” target. Which will compile and run the tests. If there is error when building the “test” target, make will stop and won’t try to build the “docs” target. Otherwise, you see all tests pass and “docs” get updated.

Variables And Build Directory

We only use two source files in our project, using variables and a separate build directory doesn’t seem neccesary. However, a few built-in variables still help.


$< first item in dependence list
$^ all items in dependence list
$@ build target

Now we can write

test: main.c++
“TAB here”g++ -ansi -pedantic -lcppunit -ldl -Wall -DTEST $< -o $@
“TAB here”./$@

main: main.app
main.app: main.c++
“TAB here”g++ -ansi -pedantic -Wall $< -o $@
“TAB here”./$@

To know more about variables in Makefile, read the make manual.

What else make Can Do

Because make doesn’t limit the commands that you can use while “building” a target, in fact you can do anything you want.

For our class projects, I also use it to generate the valgrind output, a file without comments for UVa and TAGS used by emacs and more recently to init a project skeleton for this class. You can check my Makefile if you are interested.

Knowing some scripting will help you a lot when writing commands that are not compiling things. You can use any language you want, be it shell-script, python, perl. Some good old GNU tools also proved to be helpful, such as sed, cut, sort, etc.

I guess this is an end now.

Just want to get you some feelings on what make is. To learn how to use it, go read the offical make manual. The manual covers everything, both for beginners and for experts. Books on make are available too if you prefer paper reading.

Tags: , ,

Leave a Reply