Now that I know enough C++ to be able to build something useful, I started looking at tools that will make it possible to create maintainable projects. I’m exploring CMake because it is probably the most popular build tool for C++ projects. It allows you to create platform independent configuration files that can then be translated to the platform of your choice.

Installation

In Ubuntu, you can install CMake using apt-get:

1
sudo apt-get install cmake

For other systems, you can download the binary from CMake’s downloads page.

Example project

Before we start playing with CMake, we will need a project to build. The project won’t actually do anything, but it will contain a few files and directories to simulate a larger project. This is going to be the directory structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
project-directory/
|---src/
|   |---main.cpp
|   |---helpers/
|   |   |---AwesomeHelper.cpp
|   |   |---AwesomeHelper.h
|   |   |---OkHelper.cpp
|   |   |---OkHelper.h
|   |
|   |---models/
|   |   |---Person.cpp
|   |   |---Person.h
|
|---libraries/
    |---MyLibrary/
        |---src/
            |---MyLibrary.cpp
            |---MyLibrary.h

The result of building this project will be an executable program. Our dependency tree will be like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                   --------
                   | main |
                   --------
                _/    |     \_
              _/      |       \_
            _/        |         \_
           /          |           \
  ----------   -----------------   -------------
  | Person |   | AwesomeHelper |   | MyLibrary |
  ----------   -----------------   -------------
      |
      |
  ------------
  | OkHelper |
  ------------

Configuring a CMake project

A CMake project starts with a CMakeLists.txt file. Projects can be nested, so multiple CMAkeLists.txt files can exist within a project. For this example, we will have a project-wide CMakeLists.txt file, and another one for MyLibrary.

Let’s start by creating a CMakeLists.txt file in the project-directory/libraries/MyLibrary folder. This file will instruct CMake how we want the library to be built:

1
2
3
4
5
6
7
8
9
# Minimum version of CMake required to build this project
cmake_minimum_required(VERSION 3.0)

# Name of the project
project(MyLibrary)

# Add a library to this build. The name of the library is MyLibrary and it
# consists of only the MyLibrary.cpp file
add_library(MyLibrary src/MyLibrary.cpp)

Building the project

We will build the library in a folder named build, so generated files are separated from our source code:

1
2
3
4
5
cd project-directory/libraries/MyLibrary
mkdir build
cd build
cmake ..
make

A few files will be generated in the build folder. Among them, a file named libMyLibrary.a. That is the library we just generated.

1
2
$ ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake  libMyLibrary.a  Makefile

Putting it all together

Now that the library is ready, we can continue with the executable. We will create a CMakeLists.txt file in project-directory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Minimum version of CMake required to build this project
cmake_minimum_required(VERSION 3.0)

# Name of the project
project(Project)

# Add all the source files needed to build the executable
add_executable(Project src/main.cpp src/helpers/AwesomeHelper.cpp src/helpers/OkHelper.cpp src/models/Person.cpp)

# Include the directory where MyLibrary project is. Otherwise, we can't use the
# library
add_subdirectory(libraries/MyLibrary)

# Link the executable and the library together
target_link_libraries(Project MyLibrary)

Now we can build the whole project with just a few easy to remember commands:

1
2
3
cd build
cmake ..
make

Adding variables to CMakeLists.txt

I feel having to constantly type paths can be a problem if I ever want to change my folder structure, so I will add some variables for the folder names:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Minimum version of CMake required to build this project
cmake_minimum_required(VERSION 3.0)

# Name of the project
project(Project)

# Create a few variables for the folder names, so they are easier to rename in
# the future
set(HELPERS_DIR src/helpers)
set(MODELS_DIR src/models)

# Add all the source files needed to build the executable
add_executable(
  Project
  src/main.cpp
  ${HELPERS_DIR}/AwesomeHelper.cpp
  ${HELPERS_DIR}/OkHelper.cpp
  ${MODELS_DIR}/Person.cpp
)

# Include the directory where MyLibrary project is. Otherwise, we can't use the
# library
add_subdirectory(libraries/MyLibrary)

# Link the executable and the library together
target_link_libraries(Project MyLibrary)

Conclusion

C++ build system is not very easy to use, so having a tool like this can really help make sense of a project. Another advantage of using CMake is that by using it, the project can now be compiled in multiple platforms with no extra work.

[ c++  programming  automation  productivity  ]
Variadic templates / functions in C++
Passing functions as arguments in C++
C++ Generics / Templates
Using thread pools in C++
Building a simple server with C++