Setup a simple CMake project for a C/C++ application

CMake is a cross-platform free and open-source software for build automation, testing, packaging and installation of software by using a compiler-independent method. CMake is not a build system itself; it generates another system’s build files. It supports directory hierarchies and applications that depend on multiple libraries. It can invoke native build environments such as Make, Qt Creator, Ninja, Android Studio, Apple’s Xcode, and Microsoft Visual Studio. It has minimal dependencies, requiring only a C++ compiler on its own build system. Source Wikipedia.

In this article I will show how to use CMake in a simple C++ project to generate the required makefiles and build the project on Linux and Windows host machines. Let’s get started.

CMake Installation

By default, the newer versions of Ubuntu comes with the CMake package installed. To verify whether CMake is installed or not, open a terminal window and run:

$ cmake --version

If CMake is not installed download and install from https://cmake.org/download/ or run the following command:

$ sudo apt install cmake

Please read the “Windows” section under “Other tools to install” to install cmake along with other utilities.

Other tools to install

To build a C/C++ project you must install a compiler and a make utility. Here I will show how to install and use the gcc, g++ and make utilities.

By default, gcc, g++ and make utilities are installed. To verify whether they are installed or not, open a terminal and type:

$ gcc --version
$ g++ --version
$ make --version

If these utilities are not installed, then run the following command:

$ sudo apt install make gcc gcc-multilib g++-multilib

Download and install MSYS2 from: https://www.msys2.org/

MSYS2 is a collection of tools and libraries providing you with an easy-to-use environment for building, installing and running native Windows software.

After installation start the MSYS2 terminal app from the start menu.

Install the gcc, g++ compilers, cmake and make using the following commands:

$ pacman -S mingw-w64-ucrt-x86_64-gcc
$ pacman -S cmake
$ pacman -S make

Verify each installation by running the below commands:

$ gcc --version
$ g++ --version
$ cmake --version
$ make --version

If all is good you can generate and build your program using CMake

Configure a C/C++ project with CMake

Consider the below C++ project having multiple sub-directories. Each of the directories contains a “CMakeLists.txt” file. These CMakeLists.txt files contains the necessary cmake configurations to generate the relevant makefiles and compile the program:

cmake_cpp
    |- source
            |- CMakeLists.txt
            |- src
                 |- CMakeLists.txt
                 |- main.cpp
                 |
                 |- module1
                 |      |- CMakeLists.txt
                 |      |- module1.cpp
                 |      |- module1_priv.h
                 |
                 |- module2
                 |      |- CMakeLists.txt
                 |      |- module2.cpp
                 |      |- module2_priv.h
                 |
                 |- include
                         |- main.h
                         |- module1.h
                         |- module2.h

Below are the content and explanation of each CMakeLists.txt file.

cmake_cpp/source/CMakeLists.txt`
# set the minimum cmake version requirement
cmake_minimum_required(VERSION 3.20)

# set the project name and version number
project (cmake_cpp
	VERSION 0.1
	DESCRIPTION "CMake demo")

# This project will output an executable file called app
add_executable(app "")

# add the next lower level directory
add_subdirectory(src)
cmake_cpp/source/src/CMakeLists.txt
# add the next lower level directories
add_subdirectory(module1)
add_subdirectory(module2)

# make a list of source files to be compiled
list(APPEND app_sources 
			main.cpp
# 			add more source files here
	)

# this function adds the list of source files to the executable app
target_sources(app PUBLIC ${app_sources})

# add paths to include directories
target_include_directories(app PUBLIC . include)
cmake_cpp/source/src/module1/ CMakeLists.txt (same for cmake_cpp/source/src/module2/ CMakeLists.txt)
# make a list of source files to be compiled
list(APPEND app_sources 
			module1.cpp
# 			add more source files here
	)

# this function adds the list of source files to the executable app
target_sources(app PUBLIC ${app_sources})

# if there is a need to add module specific include directories, uncomment the following line
#target_include_directories(app PUBLIC .)

Generate and Build

The next step is to generate the relevant makefiles and then call the make utility to compile and link and build the executable. Below are the set of commands to do so. Please note these commands will run on Ubuntu and Windows (tested in MSYS2)

Change to the `cmake_cpp` directory (the below command is considering the directory is at /home/user)

$ cd /home/user/cmake_cpp

The next command is to tell the cmake program to use the files under the source directory and generate the necessary makefiles inside a build directory. Note that the build directory may or may not be present. If not present the cmake program will create the build directory.

$ cmake -Ssource -Bbuild

Next is to compile the code. This command tells the cmake program to compile the files present inside `build` directory.

$ cmake --build build

Use the below commands if the above build fails. If cmake cannot find the compiler, the path can be set from the command line. A known issue with MSYS2 is, it may use the gcc compiler in place of g++ compiler. If you get linker errors of undefined references to C++ standard library objects, then set the g++ compiler as shown below.

$ cmake -DCMAKE_CXX_COMPILER=/ucrt64/bin/g++.exe -Ssource -Bbuild
$ cmake --build build

Use the command whereis g++ to get the path to the g++ executable

CMake can also generate configuration files for IDEs. Below is a list of Generators available. Use cmake –help to see the list.

Generators

The following generators are available on this platform (* marks default):
  Green Hills MULTI            = Generates Green Hills MULTI files
                                 (experimental, work-in-progress).
* Unix Makefiles               = Generates standard UNIX makefiles.
  Ninja                        = Generates build.ninja files.
  Ninja Multi-Config           = Generates build-<Config>.ninja files.
  Watcom WMake                 = Generates Watcom WMake makefiles.
  CodeBlocks - Ninja           = Generates CodeBlocks project files.
  CodeBlocks - Unix Makefiles  = Generates CodeBlocks project files.
  CodeLite - Ninja             = Generates CodeLite project files.
  CodeLite - Unix Makefiles    = Generates CodeLite project files.
  Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files.
  Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
  Kate - Ninja                 = Generates Kate project files.
  Kate - Unix Makefiles        = Generates Kate project files.
  Sublime Text 2 - Ninja       = Generates Sublime Text 2 project files.
  Sublime Text 2 - Unix Makefiles
                               = Generates Sublime Text 2 project files.

For instance, if you want to generate the cmake_cpp program for Eclipse IDE use the below command.

$ cmake -Ssource -Bbuild -G"Eclipse CDT4 - Unix Makefiles"
$ cmake --build build

To enable debugging in Eclipse IDE use.

$ cmake -Ssource -Bbuild -DCMAKE_BUILD_TYPE=Debug -G"Eclipse CDT4 - Unix Makefiles"
$ cmake --build build

The Eclipse project is generated inside the build directory. To open the project in eclipse go to File -> Import -> Existing Projects into Workspace and browse for the cmake_cpp/build directory

Source files

Complete source can be found in the following github link:

https://github.com/deyro/cmake_cpp

I hope the information provided here is useful and will help others to get started with using CMake in C/C++ projects.

For any questions please comment.

Leave a Reply

Your email address will not be published. Required fields are marked *