A Concise Guide to Managing C++ Projects with CMake

1. Background

CMake is a product developed by Kitware and some open-source developers during the development of several toolkits (VTK), which ultimately formed a system and became an independent open-source project. Its official website is cmake.org, where more information about CMake can be found.

It is a cross-platform build tool that can describe the build process for all platforms using simple statements. CMake can output makefile files, which the system will use to compile the entire project based on the contents of this makefile.

A Concise Guide to Managing C++ Projects with CMake

Figure 1 Kitware and the CMake Suite

Software development involves the following three steps:

1. Compile the source code;

2. Link to other libraries;

3. Package the application into libraries or binaries.

If we have a large C++ project composed of many inter-calling projects, some for generating library files and some for implementing logical functions, the calling relationships between them are complex and strict. If one wants to carry out secondary development within such a complex framework, merely having the source code is far from sufficient; one also needs to clearly understand the complex relationships between these dozens of projects. Even if the original author provides relevant structural documentation, the process of setting up the project remains long and arduous for beginners. The core business of developers is software development, not software building. The purpose of CMake is to automate the software building process and it is cross-platform.

The original author only needs to generate a CMakeLists.txt document, and the users of the framework only need to download the CMakeLists.txt provided by the author along with the source code to set up the project using CMake with the author’s help. The process of writing CMake is essentially a programming process, but what you need to write is the CMakeLists.txt (usually one per directory), using the “CMake” language and syntax.

Beginners looking at CMakeLists often feel confused, as the numerous syntax instructions are unclear and they may not even be able to modify it themselves. This article will introduce various important variables, instructions, statements, and their functions in CMakeLists.

A Concise Guide to Managing C++ Projects with CMake

Figure 2 CMake Build Process

2. Installation

Most Linux operating systems will include CMake directly in the system, without the need for manual installation. Other operating systems, such as Windows or certain Linux systems, can download and install the package from the CMake official website:cmake.org/HTML/Download. Installation steps are omitted and can refer to other materials online.

3. Usage

Since CMake generates some intermediate files (CMakeFiles, CMakeCache.txt, cmake_install.cmake, Makefile), it is best to create a new folder in the project source code directory to store the generated intermediate files, i.e., external compilation. In the intermediate folder, execute the compilation commands cmake .. and make, then the compiler will compile the C++ project according to the instructions in the generated Makefile. To clean the compilation results, use the make clean command. On Windows, you can also use CMakeSetup (GUI) for project building.

The make install command will install the compiled target files directly to the /usr/local directory by default. If you want to install to another directory, you can use make install DESTDIR=< install_path >.

This article will next introduce the method of writing CMakeLists, which is what developers are most concerned about. The instructions for writing CMakeLists are case-insensitive, and to maintain a consistent style, this article will use uppercase letters.

4. Basic Instructions

To compile a very simple C++ project that does not depend on third-party libraries, the CMakeLists must contain at least the following:

#Declare the minimum required CMake version

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

#CMake project name. This instruction implicitly defines two CMake variables: PROJECT_BINARY_DIR and PROJECT_SOURCE_DIR, which point to the project’s compile path and project path, respectively.

PROJECT (HELLO)

#Generate executable file, using ${} to reference variables.

ADD_EXECUTABLE(Executable program name ${ Source files })

At this point, an executable program has been generated. If you want to manage a project that depends on third-party libraries, you will need to add some additional instructions. In addition, there are some important variables and statements in CMake that need to be mastered to use them skillfully. Below are listed:

5. Important Variables

#CMake version

CMAKE_SYSTEM_VERSION

#Output path for executable files

EXECUTABLE_OUTPUT_PATH

#Library file output path

LIBRARY_OUTPUT_PATH

#Install path

CMAKE_INSTALL_PREFIX

Note: This variable can be explicitly defined in CMakeLists as: SET(CMAKE_INSTALL_PREFIX < install_path >); it can also be passed as a command-line parameter: cmake -DCMAKE_INSTALL_PREFIX=< install_path >; or specified using make install DESTDIR=< install_path > after CMake is completed.

#Directory for storing binary files during project compilation.

CMAKE_BINARY_DIR

#Place the added header file search path before existing paths.

CMAKE_INCLUDE_DIRECTORIES_BEFORE

#Place the added header file search path after existing paths.

CMAKE_INCLUDE_DIRECTORIES_AFTER

#Top-level directory of the project

CMAKE_SOURCE_DIR

#Define the path where your own CMake modules are located

CMAKE_MODULE_PATH

#Control the default compilation method for libraries

BUILD_SHARED_LIBS

#Set C++/C compilation options, which can also be added using the ADD_DEFINITIONS() instruction

CMAKE_CXX_FLAGS/ CMAKE_C_FLAGS

Note: This instruction is very important because it can determine the compiler behavior for C++/C projects. Sometimes, if the compilation method of the third-party libraries we depend on is inconsistent with our project’s compilation method, the compilation may succeed, but run-time bugs may occur. In this case, it is necessary to check whether the compilation behaviors of various libraries in the project are consistent.

# Set build type, with options Debug/Release. The Debug option should be used when debugging the project with GDB. This variable can be set via command line: cmake -DCMAKE_BUILD_TYPE=Release or using the instruction SET(CMAKE_BUILD_TYPE [type]).

CMAKE_BUILD_TYPE

6. Important Instructions and Statements

#View detailed compile information generated by CMake, equivalent to executing make VERBOSE=1. This instruction is very helpful when the build fails and you need to find out the reason.

SET(CMAKE_VERBOSE_MAKEFILE ON)

#Explicitly assign values to variables, very important

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

#Used to find third-party libraries with the Find.cmake module, which provides the header file path and library file path for the library. If not found, set the CMAKE_MODULE_PATH variable to tell CMake where to find it.

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [components...]])</name>

Note: When compiling software that links to third-party libraries, we need to know the header file path, library file path, and the filename of the libraries to be linked. When using third-party libraries, this information must be provided in CMakeLists.

#Output user-defined information to the terminal, displaying status information, warnings, or errors. It includes three types: SEND_ERROR, STATUS, FATAL_ERROR.

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)

Example: MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})

#Add executable file

ADD_EXECUTABLE(<name> IMPORTED [GLOBAL])</name>

#Add a subdirectory containing source files to the current project and specify where to store intermediate binaries and target binaries.

ADD_SUBDIRECTORY(source_dir [binary_dir])

#Generate libraries. There are three types of generated libraries: SHARED, dynamic library; STATIC, static library; MODULE, effective on systems using dyld.

ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)

#Set properties for output targets

SET_TARGET_PROPERTIES(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)

Example: Set the target library name: SET_TARGET_PROPERTIES(target_name PROPERTIES OUTPUT_NAME "hello")

#Set the version number of the dynamic library, VERSION refers to the dynamic library version, SOVERSION refers to the API version.

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

#Add multiple specific header file search paths to the project. The default header file paths are /usr/include and /usr/local/include; if not, they need to be explicitly defined in CMakeLists.

INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)

#Link target files to third-party libraries, a very important statement

TARGET_LINK_LIBRARIES(target library1 <debug optimized="" |=""> library2...)</debug>

#Search for filenames in specified paths, where the specified paths are all the paths included in the CMAKE_INCLUDE_PATH environment variable.

FIND_PATH(filename)

#Add -D definitions to the C/C++ compiler. Modify preprocessor settings

ADD_DEFINITIONS()

#Define dependencies of the target on other targets, ensuring that other targets are built before compiling this target

ADD_DEPENDENCIES()

#Search all source code files in a directory and store the list in a variable, can be used with ADD_EXECUTABLE.

AUX_SOURCE_DIRECTORY(. SRC_LIST)

ADD_EXECUTABLE(main ${SRC_LIST})

#if statement, if the expression is not: empty, 0, N, NO, OFF, FALSE, NOTFOUND or _NOTFOUND, the expression is true.

IF(expression)

…..

ELSE(expression)

…..

ENDIF(expression)

#List variable traversal statement

FOREACH(loop_var)

ENDFOREACH(loop_var)

#while statement

WHILE(condition)

ENDWHILE(condition)

The variables, statements, and instructions mentioned in this article include the most common and important parts of writing CMakeLists. The process of writing CMake is essentially a programming process. If you want to further understand how to use CMake, the author has provided learning materials for CMake. Readers can scan the code and note [compile] to receive them themselves.

A Concise Guide to Managing C++ Projects with CMake
Previous Reviews
01Introduction to Image Segmentation Cityscape Dataset
02
Implementation of 3D Object Detection with Multi-task Multi-sensor Data Fusion
03
About Point Cloud Distortion and LiDAR Imagery Representation
04
Warning! 4000 Words to Teach You How to Write a Textbook-Level Resume That HR Will Love!

Leave a Comment