Essential Guide to Managing C++ Projects with CMake

Essential Guide to Managing C++ Projects with CMake

1. Background

CMake is a product derived from the development of several toolkits (VTK) by Kitware and some open-source developers, ultimately forming a system and becoming an independent open-source project. Its official website is cmake.org, where you can find more information about CMake.

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 these makefiles.

Essential Guide to Managing C++ Projects with CMake

Figure 1 Kitware and CMake Suite

Software development involves the following three steps:

1. Compile the source code;

2. Link to other libraries;

3. Package the application into a library or binary file.

If we have a large C++ project composed of many interdependent projects, some for generating library files and others for implementing logical functions, the calling relationships between them are complex and strict. If you want to carry out secondary development within such a complex framework, merely having its source code is far from sufficient; you also need to clearly understand the complex relationships among these dozens of projects. Even if the original author provides relevant structural documentation, the process of establishing the project remains long and arduous for newcomers. Developers’ core business is software development, not software building. The purpose of CMake is to automate the software build process and it is cross-platform.

The original author only needs to generate a CMakeLists.txt document, and the framework users 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 assistance. The process of writing CMake actually involves programming, but what you need to write is the CMakeLists.txt (usually one for each directory), using the “CMake” language and syntax.

New developers may feel confused when they first see a CMakeLists file, as they may not understand the numerous syntax directives and may not even be able to modify it themselves. This article will introduce various important variables, directives, statements, and their functions in CMakeLists.

Essential 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, so manual installation is not necessary. Other operating systems, such as Windows or certain Linux distributions, can download the installation package from the CMake official website: cmake.org/HTML/Download. Installation steps are omitted, and you can refer to other online resources.

3. Usage

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

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 the most concerning part for developers. The directives in CMakeLists are case-insensitive, and for uniformity, this article will use uppercase letters.

4. Basic Directives

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

# Declare the minimum required CMake version

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

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

PROJECT (HELLO)

# Generate an 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 need to add some additional directives. In addition, there are some important variables and statements in CMake that need to be mastered to use them effectively. Below are some listed:

5. Important Variables

# CMake version

CMAKE_SYSTEM_VERSION

# Executable file output path

EXECUTABLE_OUTPUT_PATH

# Library file output path

LIBRARY_OUTPUT_PATH

# Installation 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 after CMake completion using make install DESTDIR=< install_path > to define the installation path.

# Directory for storing binary files during project compilation.

CMAKE_BINARY_DIR

# Place added header file search paths before existing paths.

CMAKE_INCLUDE_DIRECTORIES_BEFORE

# Place added header file search paths after existing paths.

CMAKE_INCLUDE_DIRECTORIES_AFTER

# Top-level directory of the project

CMAKE_SOURCE_DIR

# Define the path for your own CMake modules

CMAKE_MODULE_PATH

# Control the default compilation method of libraries

BUILD_SHARED_LIBS

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

CMAKE_CXX_FLAGS/ CMAKE_C_FLAGS

Note: This directive is very important because it determines the compiler behavior for C++/C projects. Sometimes, if the compilation methods of third-party libraries we depend on differ from those of our own project, compilation may succeed, but runtime may produce very subtle bugs. In such cases, we need to check whether the compilation behaviors of various libraries in the project are consistent.

# Set build type; type options are Debug/Release. Use the Debug option when debugging the project with GDB. This variable can be set via command line: cmake -DCMAKE_BUILD_TYPE=Release or directive SET(CMAKE_BUILD_TYPE [type]).

CMAKE_BUILD_TYPE

6. Important Directives and Statements

# View detailed compilation information generated by CMake, equivalent to executing make VERBOSE=1. This directive 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 Find.cmake modules, which provide 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( [major.minor] [QUIET] [NO_MODULE] [[REQUIRED|COMPONENTS] [components…]])

Note: When compiling software that links to third-party libraries, we need to know the header file path, library file path, and the filenames of the libraries to link. 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. 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 an executable file

ADD_EXECUTABLE( IMPORTED [GLOBAL])

# Add a subdirectory containing source files to the current project, specifying the location for intermediate binaries and target binaries.

ADD_SUBDIRECTORY(source_dir [binary_dir])

# Generate a library. 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 library2…)

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

FIND_PATH(filename)

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

ADD_DEFINITIONS()

# Define other targets that the target depends on, ensuring that other targets have been built before compiling this target

ADD_DEPENDENCIES()

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

AUX_SOURCE_DIRECTORY(. SRC_LIST)

ADD_EXECUTABLE(main ${SRC_LIST})

# if statement, if the expression content is not empty, 0, N, NO, OFF, FALSE, NOTFOUND, or _NOTFOUND, then 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 directives mentioned in this article encompass the most common and important aspects of writing CMakeLists. The process of writing CMake is essentially a programming process.If everyone wants to learn more aboutCMake usage, the author has provided learning materials for CMake, and readers can scan the code to obtain them by noting “Compilation”.
Essential Guide to Managing C++ Projects with CMake
   ABOUT 
    关于我们 


深蓝学院是专注于人工智能的在线教育平台,已有数万名伙伴在深蓝学院平台学习,很多都来自于国内外知名院校,比如清华、北大等。

Leave a Comment