Compiling
Let's say you want to write a simple 'hello world' application. You have 3 files, hello.cpp
hello-writer.cpp
and hello-writer.h
, the contents being
// hello-writer.h
void WriteHello(void);
// hello-writer.cpp
#include "hello-writer.h"
#include <stdio>
void WriteHello(void){
std::cout<<"Hello World"<<std::endl;
}
// hello.cpp
#include "hello-writer.h"
int main(int argc, char ** argv){
WriteHello();
}
The *.cpp files are converted to object files by g++
, using the commands
g++ -c hello.cpp -o hello.o
g++ -c hello-writer.cpp -o hello-writer.o
The -c
flag skips the linking for the moment. To link all the modules together requires running
g++ hello.o hello-writer.o -o hello
creating the program hello
. If you need to link in any external libraries you add them to this line, eg -lm
for the math library. The actual library files would look something like libm.a
or libm.so
, you ignore the suffix and the 'lib' part of the filename when adding the linker flag.
Makefile
To automate the build process you use a makefile, which consists of a series of rules, listing a thing to create and the files needed to create it. For instance, hello.o
depends on hello.cpp
and hello-writer.h
, its rule is
hello.o:hello.cpp hello-writer.h
g++ -c hello.cpp -o hello.o # This line must begin with a tab.
If you want to read the make manual, it tells you how to use variables and automatic rules to simplify things. You should be able to just write
hello.o:hello.cpp hello-writer.h
and the rule will be created automagically. The full makefile for the hello example is
all:hello
hello:hello.o hello-writer.o
g++ hello.o hello-writer.o -o hello
hello.o:hello.cpp hello-writer.h
g++ -c hello.cpp -o hello.o
hello-writer.o:hello-writer.cpp hello-writer.h
g++ -c hello-writer.cpp -o hello-writer.o
Remember that indented lines must start with tabs. Not that not all rules need an actual file, the all
target just says create hello
. It is common for this to be the first rule in the makefile, the first being automatically created when you run make
.
With all this set up you should then be able to go to a command line and run
$ make
$ ./hello
Hello World
More advanced Makefile stuff
There are also some useful variables that you can define in your makefile, which include
- CXX: c++ compiler
- CXXFLAGS:
Additional flags to pass to the
compiler (E.g include directories
with -I)
- LDFLAGS: Additional flags to
pass to the linker
- LDLIBS: Libraries
to link
- CC: c compiler (also used to
link)
- CPPFLAGS: preprocessor flags
Define variables using =
, add to variables using +=
.
The default rule to convert a .cpp file to a .o file is
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
where $<
is the first dependancy and $@
is the output file. Variables are expanded by enclosing them in $()
, this rule will be run with the pattern hello.o:hello.cpp
Similarly the default linker rule is
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
where $^
is all of the prerequisites. This rule will be run with the pattern hello:hello.o hello-writer.o
. Note that this uses the c compiler, if you don't want to override this rule and are using c++ add the library -lstdc++
to LDLIBS
with the line
LDLIBS+=-lstdc++
in the makefile.
Finally, if you don't list the dependancies of a .o
file make can find them itself, so a minimal makefile might be
LDFLAGS=-lstdc++
all:hello
hello:hello.o hello-writer.o
Note that this ignores the dependancy of the two files on hello-writer.h
, so if the header is modified the program won't be rebuilt. If you're interested, check the -MD
flag in the gcc docs for how you can automatically generate this dependancy.
Final makefile
A reasonable final makefile would be
// Makefile
CC=gcc
CXX=g++
CXXFLAGS+=-Wall -Wextra -Werror
CXXFLAGS+=-Ipath/to/headers
LDLIBS+=-lstdc++ # You could instead use CC = $(CXX) for the same effect
# (watch out for c code though!)
all:hello # default target
hello:hello.o hello-world.o # linker
hello.o:hello.cpp hello-world.h # compile a module
hello-world.o:hello-world.cpp hello-world.h # compile another module
$(CXX) $(CXXFLAGS) -c $< -o $@ # command to run (same as the default rule)
# expands to g++ -Wall ... -c hello-world.cpp -o hello-world.o