How CMake simplifies the build process (Part 1: Basic build system)

From my previous blogs there were some posts that got popular due to their useful content, and one that got my special attention is the tutorials on how to set up a basic (and further a bit more complex) build system based on CMake. These posts were left to dark when my contract with Dreamhost expired last year and I was lame enough to not save a backup. Lucky enough, I found them while browsing web.archives.org, which is actually a very good tool whenever you need to obtain information from previous versions of your website.

Wikipedia (link) defines CMake as:

CMake is a cross-platform, open-source system for managing the build process of software using a compiler-independent method. It is designed to support directory hierarchies and applications that depend on multiple libraries, and for use in conjunction with native build environments such as Make, Apple’s Xcode and Microsoft Visual Studio. It also has minimal dependencies, requiring only a C++-compiler on its own build system.

From my experience with build systems, CMake is one of the most flexible and easy to use ones, specially when you deal with a cross compilation environment (actually its name stands for Cross Platform Make). So let’s get started with the guide on how to set up a simple build system for a small project containing a few separate source files.

CMake consists of a series of macros and functions which finds the required components for you in a simple and precise way – meaning no unnecessary dependencies :)

NOTE: I like the concept of shadow build directories to separate source from binary data, it gives your project a clean environment and also avoids unnecessary source modifications (specially when your source code management relies on stat to monitore source changes), among other advantages. For this, there is a very useful CMake macro called MacroOutOfSourceBuild.cmake (link), which requires the user to build the source code outside its base directory, ensuring the developer to use a shadow build directory.

So let us start our example project. Suppose you have a small project “Hello World” (link) which consists of a set of files arranged like this:

boab@oceanlab:~$ find helloworld/
helloworld/
helloworld/src
helloworld/src/main.cpp
helloworld/src/helloworld.cpp
helloworld/src/CMakeLists.txt
helloworld/src/helloworld.h
helloworld/cmake
helloworld/cmake/modules
helloworld/cmake/modules/MacroOutOfSourceBuild.cmake
helloworld/CMakeLists.txt

As you can see above, the source files helloworld.h, helloworld.cpp and main.cpp are located inside src/ directory, which is located on the project’s base directory. CMake states that each project directory (including child directories) which contains source code should have a file named CMakeLists.txt. This file contains the build steps for the source files contained on that directory. The “Hello World” project does have two directories (base directory and src/ directory), then two CMakeLists.txt files are created as follows:

helloworld/CMakeLists.txt (base directory)

# Project name is not mandatory, but you should use it
project(helloworld)
 
# States that CMake required version must be greater than 2.6
cmake_minimum_required(VERSION 2.6)
 
# Appends the cmake/modules path inside the MAKE_MODULE_PATH variable which stores the
# directories of additional CMake modules (ie. MacroOutOfSourceBuild.cmake):
set(CMAKE_MODULE_PATH ${helloworld_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
 
# The macro below forces the build directory to be different from source directory:
include(MacroOutOfSourceBuild)
 
macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build.")
 
add_subdirectory(src)

helloworld/src/CMakeLists.txt

# Include the directory itself as a path to include directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
# Create a variable called helloworld_SOURCES containing all .cpp files:
set(helloworld_SOURCES helloworld.cpp main.cpp)
 
# For a large number of source files you can create it in a simpler way
# using file() function:
# file(GLOB hellworld_SOURCES *.cpp)
 
# Create an executable file called helloworld from sources:
add_executable(helloworld ${helloworld_SOURCES})

Now you create a shadow build directory (ie. build/) and start building your project, as shown with the commands below:

$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/boab/helloworld/build
$ make
[ 50%] Building CXX object src/CMakeFiles/helloworld.dir/helloworld.cpp.o
[100%] Building CXX object src/CMakeFiles/helloworld.dir/main.cpp.o
Linking CXX executable helloworld
[100%] Built target helloworld

Voilà! Now you have finished setting up a simple project build system. The second part of this tutorial (Advanced build system) is cooking and will be served soon :)


7 Comments on “How CMake simplifies the build process (Part 1: Basic build system)”

  1. [...] blog about my open source activities How CMake simplifies the build process (Part 1: Basic build system) [...]

  2. Fariba says:

    Hello,
    I am using CMake, in command line I am trying to compile and run my program,
    I set the path to my project path
    cd path
    mkdir build
    cd build
    cmake ..
    and everything work up to here but then I want to run the program that it doesn’t work with the command make
    also ccmake .. doesn’t work
    I am using windows, and latest CMake
    Any help would be appreciated

    Kind Regards,
    Fariba

    • Bruno Abinader says:

      Hi Fariba, can you please paste the output given after you execute “make”?

      • Fariba says:

        It says pcl_common_debug.dll is missing,
        I copied all dlls to my project folder, it solved the problem but I have to copy all dlls every time to my different projects!

  3. Ju Hwang Kim says:

    Hello, Thanks for the great post.
    Could you please check the link of “helloworld.tgz” file?
    Because I couldn’t download it..
    I think file is missing ..

  4. steve88 says:

    Helpful, thank you!


Leave a Reply