This is a short, quick introduction to using GNU Make to automatically build your C/C++ project’s executable.
Jump directly to the makefile if you are not interested in the explanation.
I am assuming that you are using Linux or Mac OS X. On Windows things are a little bit more complicated. For one thing, GNU Make is not installed by default, so you will have to install it yourself. It can be found on the gnuwin32 website. All commands described below assume that you are using the
bash shell, which is the default shell on Linux/OS X. On Windows, again, one can install it through Cygwin… Cygwin will also supply the GCC toolchain.
Directory structure of the project
I prefer separating source files and binary files into their own directories. This is usually also the way how IDEs like Eclipse organize the project files.
One standard structure is:
/project/home/dir/: This is the home directory of your project. This directory should contain:
makefile: The makefile that will be described below
src/: The subdirectory that will contain all you source files (
bin/: This is the subdirectory where the compiler will create the binary files and the final executable.
Simple C++ project
Make uses makefiles (a file at the root directory of your project called
makefile) to build your project. The makefile specifies rules that Make will follow when generating intermediate files.
So let’s assume that you have the directory structure as described above. To create this for testing in a directory
~/testproj, run the following commands in the terminal (lines starting with
$ specify which command you should run in your
bash shell, lines without
$ give you the output of the previous command):
Let us add some empty source files:
Let’s also add some default main file, so we can actually build the project. Open
src/main.cpp and enter the following commands:
A quick way to do this from the command line is running the following code:
Now we can build the project. Suppose that the name of the binary file will be
myprogram. And as I mentioned above, we want to store the binary in the subdirectory
g++ we can run:
Now it’s time to test the program:
Simple makefile for a simple project
g++ this way is quite fragile and tedious, you have to remember to put in all the source files, and all the various options that you might have. This is where Make comes in handy.
makefile that we have just created in the first step of creating the simple project, and enter the following text (make sure that the whitespace at the beginning of line 2 is a single TAB character):
You can use the following shell command to insert the text (character
\ at the end of line 2 signifies continuation of the command, so also enter line 3 as a command after entering line 2):
Now remove the compiled program:
And run make by invoking the
make command (make sure that you are in the root of your project directory, that is,
It’s that simple. Now if you run
make again, you get the following:
This is one of the big advantages of make: it only rebuilds the program if there has been a change in one of your source files since it was built last time. This simple makefile will not, however, rebuild the code if there is change in the header files
make does not know about them. You can force
make to rebuild everything by running:
The simple makefile contains two lines. The first line tells make that the building program
bin/myprogram depends on three files:
src/other.cpp. That means that whenever one of these files changes, make will try to rebuild
bin/myprogram. When writing makefiles,
bin/myprogram is called the target, and the source files are the prerequisites of building the target.
The second line then explains how to build
bin/myprogram. Here it is as simple as running
g++ with the names of the source files (make will replace
$^ with their names automatically), and the name of the output file (make will replace
bin/myprogram). They are called automatic variables. The second line must start with a single TAB character to work properly.
Lets try to write a more general makefile for our project. Copy the following source (the makefile is available at this link) into your
makefile (again, make sure that lines 23 and 29 start with a single TAB character):
# we can define variables in a makefile # variable CC will specify the compiler; feel free to use clang++ CC:=g++ # this variable contains any extra compiler options that we might # want to add, like -O3, -march=native, etc. # -O3 tells g++ to optimize the code for speed CC_OPTS:=-O3 # this variable will contain the names of all cpp source files SRCS:=$(wildcard src/*.cpp) # variable with all header files HEADERS:=$(wildcard src/*.h) # this will contain the names of all intermediate object files OBJECTS:=$(patsubst src/%.cpp,bin/%.o,$(SRCS)) # this rule is fancier now # $< are the names of all prerequisites (the object files) # $@ is the name of the target (bin/myprogram in this case) bin/myprogram: $(OBJECTS) $(CC) $^ $(CC_OPTS) -o $@ # must start with TAB character # but now we have to tell make how to build the object files # -c option tells g++ to only compile one source file at a tile # $< is the name of the first prerequisite (the cpp file in this case) bin/%.o: src/%.cpp $(HEADERS) $(CC) $< $(CC_OPTS) -c -o $@ # must start with TAB character
Try building your project by running
Seems like everything is working fine. Now running
make again will just print that
bin/myprogram is up to date.
Explanation of the improved makefile
The improved makefile is much more convenient. It automatically searches the subdirectory
src for all
*.cpp source files and all
*.h header files. It will instruct
g++ to build the program from these files.
This time, however, it uses an intermediate step during which it compiles each
cpp source file into an object file
bin/*.o. This speeds up the compilation when you have many source files in your project, and change only a few of them at a given time. Make will automatically rebuilt only the updated files.
# starts a comment. Everything after it all the way to the end of the line is ignored by Make.
You can define variables in a makefile using the code
NAME:=value. The value can be then used anywhere by writing
$(NAME). This is often quite useful.
$(wildcard src/*.cpp) produces a space separated list of all
cpp files in subdirectory
$(patsubst src/%.cpp,bin/%.o,$(SRCS)) converts the names of the
cpp source files into the names of object files. For example,
src/main.cpp will become
GCC and optimization
g++ with no extra options,
g++ will not optimize the code for speed. This is useful for debugging because the structure of the program is exactly preserved. However, when doing numerical computations, you usually want the code to run as fast as possible. To tell
g++ to optimize the code for speed, add
-O3 command line option. The behavior of the program will not change (well, it should not), but the speed might improve significantly.