Create a simple C++ program

Purpose of this document

The goal of this document is to give a first way that explain how to compile and link a C++ program using g++ (GNU GCC) and make (GNU Make) commands.

Click on the following link to download the HelloWorld example: HelloWorld.tar.bz2.

program version
g++ 3.4.3
make 3.80

Write a program

Let us write a simple HelloWorld program that print a message to the standard output. First create a HelloWorld directory and inside it a structure of input/output directories.

[~/workspace/C++] > mkdir -p HelloWorld/bin HelloWorld/inc HelloWorld/obj HelloWorld/src

inc and src directories will contain respectively the include and source input files. bin and obj directories will contain respectively program executable and intermediate object output files.

Now create the header (or C++ interface) file hello.h.

~/workspace/C++/HelloWorld/inc/hello.h
#ifndef HELLO_H
#define HELLO_H

void Hello();

#endif

Then create the implementation file hello.cpp.

~/workspace/C++/HelloWorld/src/hello.cpp
#include <stdio.h>
#include "hello.h"

void Hello()
{
        printf("Hello World !\n");
}

Lastly create the main.cpp file that contains the main part.

~/workspace/C++/HelloWorld/src/main.cpp
#include "hello.h"

int main(int argc, const char* argv[])
{
        Hello();
        return 0;
}

Compile and link (command line version)

The first step consists of compiling source files.

[~/workspace/C++/HelloWorld] > g++ -I ./inc -c src/hello.cpp -o obj/hello.o
[~/workspace/C++/HelloWorld] > g++ -I ./inc -c src/main.cpp -o obj/main.o

The option -I ./inc says to GCC where find header (or include) files. Option -c src/hello.cpp defines the file to compile and the option -o obj/hello.o defines the output object file to generate.

The second step consists of linking the program.

[~/workspace/C++/HelloWorld] > g++ -o bin/hello obj/hello.o obj/main.o

The first parameter after the option -o defines the name of the output executable file. Other parameters are files to link. Now the program is created and can be executed from the bin directory.

[~/workspace/C++/HelloWorld] > bin/hello
Hello World !

Compile and link (with GNU Make)

The GNU Make program uses the (default) file Makefile (or makefile) to determinate what actions to take into account. This file consists of a list of rules. Each rule is defined by a target, an optional list of dependencies and a list of commands.

a rule of a Makefile
<target>: <dependency> ... <dependency>
	<command>
	...
	<command>

A target may be either a file to be generated or an identifier for an action. GNU Make executes commands of the target if one or more dependencies have changed since the target was last built. A dependency is a file or an identifier defined in the file rules (it refers to another target). A command is an action (a shell command) that GNU Make executes. A rule may have one or more commands, only one per line. Note that each command line must begin with a tab character.

For our program HelloWorld let us write a simple Makefile.simple file that reproduces commands described in the previous paragraph.

~/workspace/C++/HelloWorld/Makefile.simple
default: bin/hello

bin/hello: obj/hello.o obj/main.o
	g++ -o bin/hello obj/hello.o obj/main.o

obj/hello.o: src/hello.cpp inc/hello.h
	g++ -I ./inc -c src/hello.cpp -o obj/hello.o

obj/main.o: src/main.cpp inc/hello.h
	g++ -I ./inc -c src/main.cpp -o obj/main.o

.PHONY: clean cleanall

clean:
	rm -f obj/*.o

cleanall: clean
	rm -f bin/hello

A rule named default is defined. It is the first rule of the file so it is called by default when the command make -f Makefile.simple is executed (the -f option defines the name of the file that contains rules). The target default depends on the bin/hello rule that may generate the executable file of the program HelloWorld. The target bin/hello depends on both object files obj/hello.o and obj/main.o that are also defined as targets that depends respectively on src/hello.cpp and inc/hello.h files and src/main.cpp and inc/hello.h files. So rules forms a chain of dependencies.

The program HelloWorld is created invoking the file Makefile.simple with the GNU Make program.

[~/workspace/C++/HelloWorld] > make -f Makefile.simple
g++ -I ./inc -c src/hello.cpp -o obj/hello.o
g++ -I ./inc -c src/main.cpp -o obj/main.o
g++ -o bin/hello obj/hello.o obj/main.o

Both targets clean and cleanall defines actions to clean up all files generated by the GNU Make program. The .PHONY target is used to avoid conflict with a file of the same name. Dependencies of this target are rules that may be explicitly invoked.

[~/workspace/C++/HelloWorld] > make -f Makefile.simple cleanall
rm -f obj/*.o
rm -f bin/hello

Now let us write a more advanced Makefile that used some interesting features provided by GNU Make. See the GNU Make section that describes some features. Refer to the GNU Make manual for more explanations.

~/workspace/C++/HelloWorld/Makefile
CC=g++

ifeq ($(DEBUG),yes)
	CXXFLAGS=-Wall -g
	LDFLAGS=-Wall -g
else
	CXXFLAGS=-Wall
	LDFLAGS=-Wall
endif

INCPATH=inc
SRCPATH=src
OBJPATH=obj
LIBPATH=lib
BINPATH=bin

SRC=$(SRCPATH)/hello.cpp \
    $(SRCPATH)/main.cpp
OBJ=$(OBJPATH)/hello.o \
    $(OBJPATH)/main.o
EXEC=$(BINPATH)/hello

INCLUDES=-I ./$(INCPATH)

default: $(EXEC)

$(EXEC): $(OBJ)
	$(CC) $(LDFLAGS) -o $@ $^

$(OBJPATH)/%.o: $(SRCPATH)/%.cpp $(INCPATH)/hello.h
	$(CC) $(CXXFLAGS) $(INCLUDES) -c $< -o $@

.PHONY: clean cleanall

clean:
	rm -f $(OBJPATH)/*.o

cleanall: clean
	rm -f $(EXEC)

GNU Make defines some internal variables.

variable definition
$@ target name
$< name of the first dependency
$^ list of dependencies
$? list of dependencies most recent than the target

The program HelloWorld is compiled invoking the make command.

[~/workspace/C++/HelloWorld] > make
g++ -Wall -I ./inc -c src/hello.cpp -o obj/hello.o
g++ -Wall -I ./inc -c src/main.cpp -o obj/main.o
g++ -Wall -o bin/hello obj/hello.o obj/main.o

Compilation is done in the debug mode assigning the value of DEBUG to yes. Do not forget to clean the project before compile it again.

[~/workspace/C++/HelloWorld] > make cleanall
rm -f obj/*.o
rm -f bin/hello
[~/workspace/C++/HelloWorld] > make DEBUG=yes
g++ -Wall -g -I ./inc -c src/hello.cpp -o obj/hello.o
g++ -Wall -g -I ./inc -c src/main.cpp -o obj/main.o
g++ -Wall -g -o bin/hello obj/hello.o obj/main.o

Useful options of g++

 g++ [option] ...

The GNU project C and C++ compiler. g++ normally does preprocessing, compilation, assembly and linking. The overall options allow you to stop this process at an intermediate stage. For example, the -c option says not to run the linker. Then the output consists of object files outputed by the assembler. Other options are passed on to one stage of processing. Some options control the preprocessor and others the compiler itself. Yet other options control the assembler and linker.

option description
-I <dir> Add the directory dir to the list of directories to be searched for header files. Directories named by -I are searched before the standard system include directories. If the directory dir is a standard system include directory, the option is ignored to ensure that the default search order for system directories and the special treatment of system headers are not defeated.
-L <dir> Add directory <dir> to the list of directories to be searched for -l.
-l <library> Search the library named library when linking.
-c <file> ... Compile or assemble the source files, but do not link. The linking stage simply is not done. The ultimate output is in the form of an object file for each source file.
-o <file> Write output to file. This is the same as specifying file as the second non-option argument to cpp. g++ has a different interpretation of a second non-option argument, so you must use -o to specify the output file.
-O<level> Optimizing compilation takes somewhat more time, and a lot more memory for a large function.
See man page for details about level.
-Wall Turns on all optional warnings which are desirable for normal code.
-g Produce debugging information in the operating system's native format (stabs, COFF, XCOFF, or DWARF). GDB can work with this debugging information.
-fpic Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target machine. Such code accesses all constant addresses through a global offset table (GOT).
So you may get an error message from the linker indicating that -fpic does not work; in that case, recompile with -fPIC instead.
-fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offset table.
-shared Produce a shared object which can then be linked with other objects to form an executable. Not all systems support this option. For predictable results, you must also specify the same set of options that were used to generate code (-fpic, -fPIC, or model suboptions) when you specify this option.
-static On systems that support dynamic linking, this prevents linking with the shared libraries. On other systems, this option has no effect.

Useful options of make

make [-f <file>] [option] ...  <target> ...

The GNU make utility to maintain groups of programs. The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.

option description
-f <file> Use file as a makefile.
-k Continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.
-n Print the commands that would be executed, but do not execute them.

Useful links

link comment
GNU GCC manual g++ manual
GNU Make manual make manual