CMake是一个跨平台的自动化构建系统,它使用配置文件(CMakeLists.txt)来生成标准的构建文件,如Unix的Makefile或Windows的Visual Studio工程文件。CMake旨在支持多平台源代码编辑和管理,并且可以用于管理复杂项目和大型代码库的构建过程。
CMake的主要特点包括:
-
跨平台: 支持在多种操作系统上构建项目,包括Windows、Linux、macOS等。
-
生成构建系统: 根据CMakeLists.txt文件生成适用于不同平台的构建系统或IDE项目文件。
-
可扩展: 允许用户通过编写自己的CMake模块和脚本来扩展其功能。
-
查找依赖: 能够自动查找并配置项目所需的外部库和依赖项。
-
配置选项: 提供丰富的配置选项,允许用户自定义构建类型、编译选项等。
-
安装规则: 支持定义安装规则,方便软件的打包和分发。
-
集成测试: 支持集成测试,确保代码质量。
-
社区支持: 拥有一个活跃的社区和丰富的在线资源,包括文档、教程和论坛。
-
适用于大型项目: 特别适合于大型项目和多语言支持的项目。
CMake通过提供一套统一的构建和配置接口,简化了在不同平台上编译和构建项目的复杂性,是许多开源项目和商业软件所采用的构建工具之一。
安装 CMake
Linux 系统中,可以使用以下方式来安装 CMake:
-
使用包管理器:
大多数Linux发行版的包管理器都有CMake包。例如,在Ubuntu或Debian上,你可以使用以下命令:
1
sudo apt-get install cmake
-
从源代码编译:
你也可以从源代码编译CMake。从官网下载源代码,然后使用以下命令编译和安装:
1
2
3./bootstrap
make
sudo make install
macOS 系统中可以使用 Homebrew 来安装CMake:
1 | brew install cmake |
Windows 系统中,我们可以访问CMake官网下载页面:https://cmake.org/download/,下载适用于Windows的安装程序。
安装完成后,打开终端输入 cmake --version
来验证CMake是否正确安装。
CMake 语法
CMake的命令和配置写在CMakeLists.txt文件中,它是一个文本文件,它包含了使用 CMake 构建系统的项目的配置和构建指令。每个使用CMake的项目的根目录和每个子目录通常都会有一个CMakeLists.txt文件。
最低版本要求
1 | cmake_minimum_required(VERSION 3.1) |
CMake 的版本与它的特性(policies)相互关联,高版本的 Cmake 总会移除掉最低版本不支持的新的特性,所以我们一般使用 CMake 的最新版本,它几乎 完全是向后兼容的。
设置一个项目
1 | project(MyProject VERSION 1.0 |
默认语言是 C CXX
。
制作一个可执行文件
1 | add_executable(one two.cpp three.h) |
one 是要制作(或者说是构建)的可执行文件的名称,也是创建的 CMake 目标(target)的名称。
制作一个库
1 | add_library(one STATIC two.cpp three.h) # 语法 :库名,库类型,源文件 |
库类型可选项有 STATIC
, SHARED
, MODULE
, OBJECT
,分别对应静态库,动态库,模块,object。我们可以使用 INTERFACE
来制作一个接口库,它不能被链接,但可以被其他库依赖。
与add_library相关的一个CMake变量是BUILD_SHARED_LIBS,它表示是否应该生成动态库,用于控制默认情况下是否构建共享库(动态链接库)。 add_library可以用来覆盖该变量行为。
1 | set(BUILD_SHARED_LIBS ON) # 默认构建共享库 |
添加依赖项
1 | add_library(another STATIC another.cpp another.h) |
依赖项类型有 PUBLIC INTERFACE PRIVATE
,分别对应公开,接口,私有。
添加头文件搜索路径
1 | include_directories(${PROJECT_SOURCE_DIR}) # 为整个项目添加头文件搜索路径 |
设置变量
1 | set(MY_VARIABLE "value") # 设置自定义变量 |
访问变量时,使用 ${VAR_NAME}
语法。我们可以使用message命令来打印变量的值,这有助于调试
1 | message("The value of MY_VARIABLE is ${MY_VARIABLE}") |
设置缓存变量
CMake 提供了一个缓存变量来允许你从命令行中设置变量,缓存变量信息会保存到 CMakeCache.txt 文件中。
1 | set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description") # 只能通过命令行来设置这个变量,它不会覆盖已经定义的值。 |
CMake 中已经有一些预置的变量,像 CMAKE_BUILD_TYPE 就是一个内置缓存变量。下面表格,列出了一些常见的CMake内置变量:
变量名称 | 描述 | 分类 |
---|---|---|
CMAKE_VERSION | CMake的版本信息,如"3.16.3"。 | 版本信息 |
CMAKE_SOURCE_DIR | 包含顶层CMakeLists.txt文件的目录。 | 目录 |
CMAKE_BINARY_DIR | 由cmake命令创建的输出构建目录 | 目录 |
CMAKE_CURRENT_SOURCE_DIR | 当前被处理的CMakeLists.txt所在的目录。 | 目录 |
CMAKE_CURRENT_BINARY_DIR | 当前目标文件的构建目录。 | 目录 |
PROJECT_SOURCE_DIR | 包含项目主CMakeLists.txt的目录。如果项目是作为子项目包含的,则可能与CMAKE_SOURCE_DIR不同 | 目录 |
PROJECT_BINARY_DIR | 项目构建目录。如果项目是作为子项目构建的,则可能与CMAKE_BINARY_DIR不同 | 目录 |
CMAKE_HOME_DIRECTORY | CMake安装目录。 | 目录 |
CMAKE_CACHEFILE_DIR | 存储CMake缓存文件的目录。 | 目录 |
CMAKE_COMMAND | 运行当前CMake脚本的CMake可执行文件的路径。 | 路径 |
CMAKE_GENERATOR | 指定的CMake生成器,如"Unix Makefiles"或"Ninja"。 | 构建 |
CMAKE_BUILD_TYPE | 指定的构建类型,如"Debug"或"Release"。 | 构建 |
CMAKE_SYSTEM | 目标平台,如"Linux-x86_64"。 | 平台/系统 |
CMAKE_SYSTEM_NAME | 目标平台的名称,如"Linux"。 | 平台/系统 |
CMAKE_SYSTEM_PROCESSOR | 目标平台的处理器,如"x86_64"。 | 平台/系统 |
CMAKE_SYSTEM_VERSION | 目标平台的操作系统版本。 | 平台/系统 |
CMAKE_C_COMPILER | 指定的C编译器的路径。 | 编译器 |
CMAKE_CXX_COMPILER | 指定的C++编译器的路径。 | 编译器 |
CMAKE_Fortran_COMPILER | 指定的Fortran编译器的路径。 | 编译器 |
CMAKE_LINKER | 指定的链接器的路径。 | 编译器 |
CMAKE_AR | 指定的归档器(静态库创建器)的路径。 | 工具 |
CMAKE_RANLIB | 指定的库索引生成器的路径。 | 工具 |
CMAKE_INCLUDE_PATH | 用于查找头文件的附加搜索路径。 | 搜索路径 |
CMAKE_LIBRARY_PATH | 用于查找库文件的附加搜索路径。 | 搜索路径 |
CMAKE_PREFIX_PATH | 用于查找软件包的路径前缀列表。 | 搜索路径 |
CMAKE_FRAMEWORK_PATH | 用于查找MacOS框架的附加搜索路径。 | 搜索路径 |
CMAKE_PROGRAM_PATH | 用于查找可执行程序的附加搜索路径。 | 搜索路径 |
CMAKE_SYSTEM_INCLUDE_PATH | 系统级别的头文件搜索路径。 | 系统搜索路径 |
CMAKE_SYSTEM_LIBRARY_PATH | 系统级别的库文件搜索路径。 | 系统搜索路径 |
CMAKE_SYSTEM_PROGRAM_PATH | 系统级别的可执行程序搜索路径。 | 系统搜索路径 |
CMAKE_MODULE_PATH | 用于查找CMake模块文件的路径。 | CMake |
CMAKE_ADDITIONAL_MAKE_CLEAN_FILES | 需要在"make clean"时清理的额外文件列表。 | 清理 |
CMAKE_EXPORT_COMPILE_COMMANDS | 是否生成compile_commands.json文件。 | 编译命令 |
CMAKE_AUTOMOC | 是否自动运行moc(元对象编译器)来处理Qt的信号和槽。 | Qt |
CMAKE_CXX_STANDARD | 设置C++标准版本 | 编译控制 |
CMAKE_CXX_STANDARD_REQUIRED | 是否要求指定C++标准版本 | 编译控制 |
CMAKE_CXX_EXTENSIONS | 是否允许C++的扩展特性 | 编译控制 |
动态创建配置文件
有时候我们需要动态获取配置信息,比如版本号,编译时间等。这时候我们可以使用configure_file
函数。configure_file
函数可以生成一个配置文件,这个配置文件可以包含一些变量,这些变量可以通过CMake变量来设置。例如我们可以生成一个版本信息文件,其中包含版本号和编译时间:
1 | configure_file(config.h.in config.h @ONLY) # 配置生成 config.h 文件 |
- config.h.in 是源文件的路径,.in 扩展名表示这是一个模板文件。模板文件中使用@符号包裹要被替换掉的变量。
- config.h 是目标文件的路径,CMake会将 config.h.in 文件复制到目标文件config.h 中,并替换其中的变量。
@ONLY
标志表示只替换@包裹的变量,对于${VAR}
格式的变量不进行替换。
下面是 config.h.in 文件内容:
1 |
下面是 CMakeLists.txt 文件内容:
1 | cmake_minimum_required(VERSION 3.5) |
执行 cmake -S . -B build
命令后我们可以在 build 目录下看到生成 的config.h 文件,其中包含我们设置的版本号、编译时间和编译目录等信息。
设置支持的 C++标准
1 | set(CMAKE_CXX_STANDARD 17) # 设置C++标准版本为 17 |
我们也可以通过设置编译目标属性来设置支持的 C++标准:
1 | set_target_properties(myTarget PROPERTIES |
设置地址无关代码
用标志 -fPIC
来设置地址无关代码(Position independent code)是常见操作的。大部分情况下,你不需要去显式的声明它的值。CMake 将会在 SHARED 以及 MODULE 类型的库中自动的包含此标志。如果你需要显式的声明,可以这么写:
1 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) |
这样会对全局的目标进行此设置,你可以只对某个目标进行设置是否开启此标志。:
1 | set_target_properties(lib1 PROPERTIES POSITION_INDEPENDENT_CODE ON) |
增加构建子目录
add_subdirectory命令可以用来增加构建子目录。例如,我们可以在项目根目录下创建一个子目录,然后使用add_subdirectory命令来构建子目录:
1 | add_subdirectory(subdir) |
下载外部项目依赖
FetchContent是CMake的一个模块,它提供了一种机制来自动下载和包含外部项目作为当前项目的依赖,而无需用户手动下载或管理这些依赖。这个模块在CMake 3.11及以上版本中可用。
1 | include(FetchContent) |
查找外部包
CMake 提供了 find_package 命令来查找外部包。find_package
的基本格式如下:
1 | find_package(<PackageName> [version] [EXACT] [QUIET] [REQUIRED] |
- REQUIRED:如果未找到包,则会生成错误。
- QUIET:如果未找到包,则不会显示警告信息。
- COMPONENTS:用于指定库的特定组件
例如我们可以使用 find_package 命令来查找 OpenCV 库:
1 | find_package(OpenCV REQUIRED) |
下面是查找数学库的示例:
1 | find_library(MATH_LIBRARY m) |
find_package
其工作原理主要依赖于两种模式:Module模式和Config模式。
-
Module模式:
在这种模式下,find_package 会查找名为
Find<LibraryName>.cmake
的文件。CMake首先在CMAKE_MODULE_PATH指定的路径中搜索该文件,如果未找到,则会在CMake自带的模块目录中查找。该文件负责查找库的头文件和链接库,并将相关信息存储在特定变量中,例如
<NAME>_FOUND
、<NAME>_INCLUDE_DIRS
和<NAME>_LIBRARIES
等。这些变量随后可以在CMakeLists.txt中使用,以便进行编译和链接 -
Config模式:
如果Module模式未能找到所需的库,CMake会转入Config模式。在此模式下,CMake查找名为
<LibraryName>Config.cmake
或<lower-case-package-name>-config.cmake
的文件。这些配置文件通常由库的开发者提供,包含了库的必要信息,如头文件和库文件的路径。使用Config模式时,用户可以通过指定CONFIG关键字来明确请求此模式
安装
在CMake中,install命令用于指定在安装目标(可执行文件、库、文件等)时应该如何复制文件和目录。这使得软件包的安装过程可以自动化,并且确保所有必要的文件被放置在正确的位置。install 命令相关语法如下:
安装目标的到指定目录:
1 | install(TARGETS targets ... [EXPORT <export-name>] |
- TARGET targets: 指定要安装的target.
- ARCHIVE|LIBRARY|RUNTIME等: 指定target的类型。
- DESTINATION dir: 指定要安装到的路径。当使用相对路径时,是相对于变量CMAKE_INSTALL_PREFIX的, 该变量默认为/usr/local/, 也可以在CMakeLists.txt中指定,也可以在运行CMAKE命令时指定,例如:cmake … -DCMAKE_INSTALL_PREFIX=/install/location
安装指定文件到指定目录:
1 | install(<FILES|PROGRAMS> files... |
- FILES files: 指定要安装的文件。
- DESTINATION dir: 指定要安装的路径。
安装目录到指定目录中:
1 | install(DIRECTORY dirs... |
- DIRECTORY dirs: 指定要安装的头文件的目录路径,默认为相对于当前的路径。 如果路径以/结尾时,会把路径里面的内容复制到给定路径,如果没有以/结尾,则会会复制该目录。
- DESTINATION: 要安装到的目录路径。
下面是一些使用示例:
1 | install(TARGETS my_executable RUNTIME DESTINATION bin) # 安装可执行文件 |
运行 CMake
构建项目
经典 CMake 构建流程:
1 | mkdir build |
新版 CMake 构建流程:
1 | cmake -S . -B build # 生成构建目录,-B指定待生成的构建目录, -S 指定源码目录 |
我们可以通过环境变量执行编译器:
1 | CC=clang CXX=clang++ cmake .. |
设置生成器
CMake Generators是CMake构建系统用来指定如何生成构建文件的一套工具。当你使用CMake来配置项目时,Generator定义了输出构建系统的具体类型,这通常依赖于你想要使用的构建工具和开发环境。
通过cmake --help
命令可以查看所有CMake支持的生成器。以下是一些常见的CMake Generators:
-
Unix Makefiles:
这个Generator生成标准的Makefiles,适用于Unix-like系统(如Linux和macOS)以及使用Gnu Make的Windows环境。
-
Ninja:
Ninja是一个小型的专注于速度的构建系统,它可以生成比传统Makefiles更快的构建过程。
-
Visual Studio:
这个Generator为不同版本的Visual Studio IDE生成解决方案(.sln)和项目文件(.vcxproj),适用于Windows开发。
-
Xcode:
为macOS的Xcode IDE生成.xcodeproj项目文件。
我们可以通过-G
参数指定生成器,也可以使用环境变量CMAKE_GENERATOR
来指定生成器:
1 | cmake -G Ninja .. // 指定Ninja生成器 |
执行安装
通过上面命令构建完成之后,我们可以使用下面命令进行安装:
1 | # 如果当前在build 目录下面 |
设置选项
在 CMake 中,你可以使用 -D 设置选项。你能使用 -L 列出所有选项,或者用 -LH 列出人类更易读的选项列表。如果你没有列出源代码目录或构建目录,这条命令将不会重新运行 CMake(使用 cmake -L 而不是 cmake -L .)。
CMake 支持缓存选项。CMake 中的变量可以被标记为 “cached”,这意味着它会被写入缓存(构建目录中名为 CMakeCache.txt 的文件)。你可以在命令行中用 -D 预先设定(或更改)缓存选项的值。CMake 查找一个缓存的变量时,它就会使用已有的值并且不会覆盖这个值。
标准选项
大部分软件包中都会用到以下的 CMake 选项:
-
CMAKE_BUILD_TYPE:用于指定项目的构建类型,默认为 Release。常见可能值有:
- Debug: 启用调试信息的生成,优化级别较低,通常用于开发和调试。
- Release: 关闭调试信息,启用更高级别的优化,用于生成发布版本的软件。
- RelWithDebInfo: 同时包含发布版优化和调试信息,适用于需要优化性能但也需要调试信息的场景。
- MinSizeRel: 最小化生成的可执行文件或库的大小,通常用于需要最小化部署体积的场景。
-
CMAKE_INSTALL_PREFIX:用于指定安装位置。UNIX 系统默认的位置是 /usr/local,用户目录是 ~/.local
-
BUILD_SHARED_LIBS: 用于控制共享库的默认值,值为 ON 或者 OFF。值为 ON 时默认生成动态库,否则生成静态库。
-
BUILD_TESTING: 用于设置启用测试的通用名称
-
CMAKE_CXX_STANDARD:用于指定 C++ 标准版本,默认为 11。
下面命令会设置默认的构建类型为 Release,安装位置为 /usr/local,编译器为 clang++,C++ 标准为 17,启用测试,生成动态库:
1 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_CXX_COMPILER=clang++\ |
参考资料
另外借助了 AI 工具:perplexity AI 、 kimi,以及 VSCode 的插件:TONGYI Lingma