Skip to content
April 24, 2012 / ecrafter

CMake Tutorial Part 1- Introduction

Introduction

The following posts form a tutorial that is intended to help you get your feet wet with the CMake cross-platform build system in Linux/C++. We’ll start with the simplest possible CMake script in Part 1, and explore one additional feature in each further post. You’ll see how I like to structure my C++ projects with the help of CMake. The hope is that, by the end of the tutorial, you’ll be comfortable enough with the basics of CMake to go off and set up your projects in your preferred manner without too much frustration.

The first thing to note about CMake is that is not a replacement for make and Makefiles. Instead, CMake is a Makefile generation system (when run in Linux), providing the user with a simpler language than Makefile scripting for specifying the build, at the expense of an added step in the build process. The user 1) specifies the build properties using the CMake scripting language 2) tells CMake to generate the Makefile and 3) runs the generated Makefile. Note that for systems other than Linux/gcc, CMake does not generate Makefiles, but system-appropriate files. For example, Visual Studio project files can be generated for Windows, and Xcode project files on Mac. The examples in this tutorial have been found to work on Linux and OSX. Note that OSX provides both the gcc and clang compilers, and CMake seems to choose gcc by default. If you would prefer to build with clang (perhaps because of the better support for C++11 in the provided version), one solution is to open a terminal and define the CXX environment variable as follows:

export CXX=/usr/bin/clang++

Roughly speaking, the inputs to a build are the source files (.cpp) and any prebuilt libraries (.so or .a) that are needed. The output is an executable or another library. Unfortunately, builds are complicated by the presence of the scripts that control the build and any intermediate object (.o) or build files. When the intermediate files are placed in the same directory as the source files, it is known as an “in-source” build. In-source builds are messy, as the files clutter up the source directory in which the programmer is working. The solution to this mess is to put the object files in another “build” directory, and this is known as an “out-of-source” build. We will begin with a simple in-source build structure before moving to out-of-source builds.

Executable with in-source build

/part1
    Main.cpp
    CMakeLists.txt

Here there is only one directory, /part1. The source code is found in Main.cpp and the CMake build script is found in CMakeLists.txt.
Main.cpp

#include<iostream>
int main(int argc, char** argv)
{
   std::cout << "Success!" << std::endl;
   return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(Part1Project)
add_executable(part1exe Main.cpp)

“CMakeLists.txt” is the required name for CMake build scripts. Our CMake script is a minimal one. It starts with the cmake_minimum_required command, indicating that we require at least CMake version 2.8. We name our build project “Part1Project” using the project command. Finally, we create an executable “make target” using the add_executable command, passing in first the name of the target, part1exe (which will become the name of the executable), then the name of the source file. A “make target” is a particular desired product of the project, usually an executable, specified using the add_executable command, or a library, specified using the add_library command (discussed in Part4). A given project may have more than one target.

To perform the build and run the executable, we do the following:

cd part1  # Get into the source directory
cmake .   # Tell cmake to create a Makefile using the CMakeLists.txt found in current dir
make      # Tell make to run the CMake-generated Makefile (and build our executable target)
./part1exe   # Run our freshly minted executable

[Note that we could have typed make part1exe, specifying our particular target (part1exe), instead of just make. In general, make will perform the same thing as make all, building all of the targets in the project.]

Afterword, our /part1 directory looks like this:

/part1
    CMakeCache.txt
    cmake_install.cmake
    Main.cpp
    /CMakeFiles
    CMakeLists.txt
    Makefile
    part1exe

As feared, our in-source build has cluttered the source directory with intermediate build files (and, if we dig into /CMakeFiles, we will find our object files lurking). Note that since CMakeLists.txt is our build control script, Makefiles are now intermediate build files. We’ll get to cleaning this up with an out-of-source build in a later post. For now, let’s take a minute to figure out which compiler flags CMake is using for our build.

One way to determine the compiler flags is to view them directly using a verbose make. CMake defines a verbose make target that can be run with VERBOSE=1:

make clean
make VERBOSE=1

When I run this in the /part1 directory and sort through the output, I find that no flags are being set:

/usr/bin/c++ -o CMakeFiles/part1exe.dir/Main.cpp.o -c /home/ecrafter/workspace/CMakeTutorials/part1/Main.cpp
/usr/bin/c++ CMakeFiles/part1exe.dir/Main.cpp.o -o part1exe -rdynamic

The first line is the compiling, the second line is the linking. The only option we see being applied is the linker flag, -rdynamic. Next, we’ll see how to go about customizing our compilation.

Leave a comment