主工程文件

首先要创建工程的主要入口,也就是一个 CMakeLists.txt 文件。

构建工程时就需要指定这个文件的所在目录。

CMake 版本和策略

主工程文件首先要描述的就是对 CMake 的版本选择和全局策略开关。

主要涉及 cmake_minimum_requiredcmake_policy 两个命令。

对整个工程使用的 CMake 特性进行约定。

cmake_minimum_required(VERSION 3.24)

全局变量

这部分定义工程需要的全局变量,如工程名、版本号等。

可以直接用 project 命令定义,也可以用 set 命令对变量进行指定。

project("base" VERSION 0.0.1)

将全局变量同步到 C++ 代码

如果有些全局变量希望可以共享给 C++ 代码,比如工程版本号,可以使用 configure_file 修改部分文件的代码,将全局变量赋给文件中的指定占位符。

创建一个 project_config.h.in 文件,写入以下内容。

constexpr int kBaseVersionMajor = @Base_VERSION_MAJOR@;
constexpr int kBaseVersionMinor = @Base_VERSION_MINOR@;
constexpr char kBaseVersion = @Base_VERSION@;

在 CMakeLists.txt 中增加生成文件的命令。

configure_file(global_config.h.in global_config.h)

构建后就会自动生成解析后的 global_config.h 文件供其他 C++ 源码使用。

全局编译选项

修改全局编译器配置,也可以使用默认配置不做修改。

这里指定默认的 C++ 语言标准版本。

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

添加自定义命令

如果有些构建的准备工作需要处理,比如将配置阶段生成的 project_config.h 拷贝到源文件目录,可以用 add_custom_command 命令添加自定命令。

add_custom_command(OUTPUT "project_config.h"
  COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/project_config.h" "${CMAKE_CURRENT_SOURCE_DIR}"
  DEPENDS "project_config.h.in"
  VERBATIM)

这里添加 VERBATIM 阻止 CMake 默认的命令转义行为,可以防止不同平台出现的兼容性问题

添加可执行程序目标

这里构建一个简单的可执行文件。

add_executable(base_test "base_test.cpp" "project_config.h")

对于指定目标,可以进行编译选项配置和依赖配置等操作,具体需要看编译目标的需求。

目标构建事件

如果有些构建的准备工作需要在构建前后处理,可以用 add_custom_command 命令添加构建前事件进行处理。

比如进行文件签名等。

注意,虽然构建事件有 PRE_BUILD,但是不同构建工具对这个事件的支持不太一样,对于大部分构建前的准备工作应该由自定义命令目标来实现,而不是 PRE_BUILD

命令行

基本配置完成后,就可以允许 cmake 命令进行构建。

配置生成

先生成构建需要的配置文件。

mkdir build
cd build
cmake -S .. -B . -G Ninja

执行构建

从当前文件夹执行编译,构建目标输出,就可以生成需要的编译产物了。

cmake --build . --config Debug

创建库

如果这个工程的编译目标是一个库,而非可执行程序,可以使用 add_library 命令。

默认情况下,创建的是静态链接库。

add_library(libbase, lib_test.cpp lib_test.h)

使用库

要指定目标依赖的库,可以用 target_link_libraries 命令。这个命令会自动计算依赖关系和链接库产物。

同时,依赖库的头文件需要用 target_include_directories 指定,这样才能在编译时搜索到依赖库的头文件。

target_link_libraries(base_test PUBLIC libbase)
target_include_directories(base_test PUBLIC "${LibBase_SOURCE_DIR}")

这里的 PUBLIC 指的是是否传递被依赖的库的头文件目录可见性,如果该目标的导出头文件有用到依赖库的头文件,就需要指定为 PUBLIC

如果使用的是其他 CMake 工程的库,需要先包含那个工程。

add_subdirectory("../lib_project" lib_project)

这里指定了 add_subdirectory 的 binary_dir 参数,是因为包含非当前工程子目录的工程时,需要指定一个相对路径给 CMake,才能正确设置生成文件的相对路径。

参考