1.7. Command-line compiling

Why in the ‘modern’ era, is there still such focus on command line and text based tools for programming? Several actually.

1.7.1. The C++ compiler

There are many programs that will make executables from C++ source. One popular choice is gcc, the Gnu project C and C++ compiler. gcc is free and open-source, meaning that it can be made to run on many different operating systems. It also has the advantage of being able to compile more than just C and C++, although it was developed initially for that. One of the advantages of using a multi language tool like gcc is that it potentially minimizes the number of new tools you have to learn to begin working in a new language.

When you invoke gcc, it normally does preprocessing, compilation, assembly and linking.

Standard executable build steps

gcc is a complex program with many options. You can execute only one of these steps, some of them, all of them, or change the behavior of one or more steps.

Generally, we are interested in building our programs as simply as possible. Given a single source file main.cpp, that contains all the code needed to describe a working program, it is enough to type:

g++ main.cpp

This command will perform all of the steps to make an executable and write the executable to a file named a.out. Normally, however, we want to give our programs meaningful names:

g++ main.cpp -o main

The -o switch tells g++ to write the executable to a file named main. We could have added a file extension, like .exe, but that is ordinarily only done on the Windows © operating system. On most other systems, the extension is used for people to sort out different types of files, but the operating system doesn’t need them to figure things out.

If our main.cpp contained syntax from the C++11 standard, or included any headers that did, then in order for it to compile, the following switch must also be added:

g++ main.cpp -std=c++11 -o main

The default C++ version for most compilers, gcc included is c++98.

A summary of useful command line arguments:

o

Specify the output executable filename.

g

Include debugging symbols in the executable output. Debug level #2 is the default.

You must include this if you want to debug your program using a debugger.

Wall

Print all warnings

Werror

Treat warnings as errors and do not write an executable output file.

Wextra

Print extra warnings above and beyond ‘all’.

pedantic

Warn about code not compliant with the ISO standard.

std=c++11

Instruct the compiler the program contains C++11 syntax.

O3

Compile with optimization level #3 enabled.

The order of command line arguments (generally) does not matter.

1.7.2. The make utility

The make program automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them. Note how this is different from the previous sections. The make program doesn’t actually compile anything. It doesn’t know how. What make does know is

  • What source files are normally associated with C++ programs

  • What commands are used to compile and link C++ source files

make actually ‘knows’ some other things as well, and can be given more information to make it even more useful, but these first two things are the most important.

To prepare to use ‘make’, you must write a file called the “makefile” that describes the relationships among files in your program and provides commands for updating each file. By default, make searches for a file named Makefile.

Once a file named Makefile exists, each time you change some source files, this simple shell command:

make

is enough to perform all necessary recompilations. The make program uses the makefile data and the last-modification times of the files to decide which of the files need to be updated. For each of those files, it issues the recipes recorded in the makefile.

A simple makefile consists of “rules” with the following shape:

TARGET ... : PREREQUISITES ...
   RECIPE
   ...
   ...

A “target” is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out, such as ‘clean’. Since these don’t result in object files or executables, they are called phony targets.

A “prerequisite” is a file that is used as input to create the target. A target often depends on several files.

A “recipe” is an action that ‘make’ carries out. A recipe may have more than one command, either on the same line or each on its own line.

Note

You need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary.

A very simple Unix C++ makefile:

CXX = c++
CXXFLAGS=-Wall -Wextra -pedantic -std=c++11 -O3

my_prog : clean
   ${CXX} ${CXXFLAGS} -o my_prog *.cpp

clean:
   rm -f my_prog

This makefile always deletes the executable file ‘my_prog’ whether any source files have changed or not and recompiles every cpp file in the current directory to regenerate the executable. Not particularly efficient, but for small programs it gets the job done and requires no maintenance if new files are added.

Assuming you keep your work separated into directories so that all the source code in a directory applies only to a single executable, then this simple makefile is sufficient for everything you’ll need to worry about over course duration.

Suffice it to say that make provides many tools and techniques to define ‘smarter’ recipes that do not suffer the constraints listed above.

The make program defines a large number of built-in variables to control how programs get built. While there are many, the two to focus on for now are those used in the previous makefile.

CXX

Specify which C++ compiler to use

CXXFLAGS

Specify command line arguments to pass to the C++ compiler

A slight variation on this theme creates several executables from a list. The primary assumption in this makefile is that each program in the list PROGS contains a main and is a single file. The makefile makes use of the fact that make can make a simple program without a makefile, if the program is contained within a single file.

CXX = c++
CXXFLAGS=-Wall -Wextra -pedantic -std=c++11 -O3

PROGS = asciitable \
        hello \
        phrase-o-matic \
        printodds \
        std11-test \

all: $(PROGS)

clean:
    rm -f $(PROGS)

Even without a Makefile, make is smart enough to produce sensible defaults for simple programs. For example, given our main.cpp example from the previous section, we can make it using make:

make main

The make program will actually invoke:

g++ main.cpp -o main

If you have exported an environment variable that make uses, such as CXXFLAGS, then those will be used by make:

export CXXFLAGS=-Wall -Wextra -pedantic -std=c++11

Now the make program will invoke:

make main
g++ main.cpp -Wall -Wextra -pedantic -std=c++11 -o main

Which is exactly what the previous makefile is doing, but on all of the programs listed.

1.7.3. Running your programs

If you compile your own program, you will need to prefix it with ./ on the command line to tell the shell that you want to run a program in the current directory (called ‘.’) instead of one of the standard system directories. So for example, if I’ve just built a program called hello, I can run it by typing:

./hello

More to Explore

  • Textbook FAQ: Why not use an IDE?

  • GNU Make Manual

  • Prefer compile-time and link-time errors to runtime errors

    • Effective C++ item #46

  • Pay attention to compiler warnings

    • Effective C++ item #48

You have attempted of activities on this page