CMake的一些用法(教程)
CMake
本笔记参照于Youtube博主LearnQtGuide的教程CMake-Episode,源码。
Cmake官方网站
cmake_minimum_required
设置项目最低cmake版本,必须有,官方文档
语法
1 | cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR]) |
project
用于定义项目的基本信息和属性。官方文档
语法
1 | project(<PROJECT-NAME> [<language-name>...]) |
示例
基本用法
1 | project(MyProject C CXX) |
这表示你的项目名称是"MyProject",并且将使用C和C++作为编程语言。
详细用法
1 | project(HelloApp |
项目名称是"HelloApp",版本号是0.0.1,项目描述是"The leading Hello World APP",并且项目将使用C++作为编程语言。
add_executable
告诉 CMake 如何将一组源代码文件编译成一个可执行文件。官方文档
语法
1 | add_executable(<name> [WIN32] [MACOSX_BUNDLE] |
示例
1 | add_executable(HelloAppBinary main.cpp) |
"MyProgram" 是你想要生成的可执行文件的名称,而 main.cpp
是源代码文件的列表。
target_compile_features
用于指定目标(可执行文件、库等)所需编译特性(C++标准、语言特性等)。官方文档
语法
1 | target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...]) |
示例
1 | target_compile_features(HelloAppBinary PRIVATE cxx_std_20) |
"HelloAppBinary" 是你的库的名称,PRIVATE
表示这些编译特性仅在当前目标中可用,而 `cxx_std_20 表示使用 C++20 标准的特性。
示例Cmake项目一
main.cpp
1 |
|
CMakeLists.txt
1 | cmake_minimum_required(VERSION 3.5) |
Windows编译
cmake --help
可以看到命令使用文档,在Windows下默认使用VS平台构建项目,Linux默认使用Unix Makefiles。可以通过选项-G
指定平台。
MSVC
1 | cmake . |
这将默认生成MSVC构建系统文件(如Makefile、Visual Studio项目等)。
下面的命令应该使用VS Developer Powershell运行:
1 | msbuild .\HelloApp.sln |
这将编译成可执行文件。
MinGW
1 | cmake -G "MINGW Makefiles" ..\source\ |
这将构建MinGW项目。
1 | mingw-32make |
编译成可执行文件。
Ninja
Ninja 是一个高速且轻量级的构建系统,旨在提供更快的构建速度和更低的内存占用。
1 | cmake -G "Ninja" ..\source\ |
这将构建Ninja项目。
1 | ninja |
编译成可执行文件。
不关心构建系统
cmake生成项目之后直接执行以下命令进行编译。
1 | cmake --bulid . |
编译成可执行文件。
询问cmake可以在项目上执行哪些操作
1 | cmake --build . --target help |
清除生成的二进制文件
1 | cmake --build . --target clean |
这将只保留项目文件。
编译可执行文件
1 | cmake --build . --target HelloAppBinary |
编译成可执行文件。
target_include_directories
指定目标的头文件搜索路径,通常用于为特定的目标(比如可执行文件、静态库、共享库等)设置编译时的头文件搜索路径。官方文档
语法
1 | target_include_directories(<target> [SYSTEM] [AFTER|BEFORE] |
示例
1 | target_include_directories(HelloBinary PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) |
file
file
命令是 CMake 中用于操作文件和目录的命令之一。它允许你执行各种文件和目录操作,如拷贝、移动、创建目录、查找文件等。官方文档
以下是一些常见的 file
命令的用法:
拷贝文件:
1 | file(COPY source_file DESTINATION destination_directory) |
这个命令可以用来将源文件拷贝到目标目录。
移动文件:
1 | file(RENAME old_name new_name) |
这个命令可以用来将文件从一个名称重命名为另一个名称。
创建目录:
1 | file(MAKE_DIRECTORY directory_name) |
这个命令可以用来在指定的路径下创建一个新的目录。
删除文件或目录:
1 | file(REMOVE [file1 ...]) |
这些命令可以用来删除指定的文件或目录。
查找文件:
1 | file(GLOB variable [LIST_DIRECTORIES true/false] [globbing expressions]) |
这个命令可以用来查找满足指定模式的文件,并将结果存储在变量中。你可以使用通配符或正则表达式来指定匹配的模式。
检查是否为目录:
1 | file(IS_DIRECTORY directory_name) |
这个命令可以用来检查指定的路径是否为一个目录。
获取文件的大小:
1 | file(SIZE filename variable) |
这个命令可以用来获取指定文件的大小,并将结果存储在变量中。
但是不建议使用file搜寻所有文件放到global变量中,一些读物
示例Cmake项目二
include目录:dog.h
operations.h
dog.h
1 |
|
operations.h
1 |
|
src目录:dog.cpp
operations.cpp
dog.cpp
1 |
|
operations.cpp
1 |
|
main.cpp
1 |
|
CMakeLists.txt
1 | cmake_minimum_required(VERSION 3.5) |
命令cmake --system-information
--system-information
选项用于打印有关系统和构建环境的信息。当你在命令行中运行以下命令时,CMake 将显示与系统和构建环境相关的各种信息:
包括但不限于:
- 系统名称和版本
- 编译器信息(名称、版本等)
- 架构信息(32位还是64位)
- 构建工具链信息
- 环境变量
- CMake 配置选项
- CMake 模块路径
- 系统库路径
- CMake 编译类型
- CMake 构建目录
- 项目源代码目录
但是这将打印到终端,很影响阅读,可以传递一个文件名写入到到文件中。
Cmake一些选项
-D
标志用于设置 CMake 变量的值。这可以通过命令行来覆盖在 CMakeLists.txt 文件中定义的默认值。通过 -D
标志,你可以在生成构建系统时直接指定变量的值,而无需手动编辑 CMakeLists.txt 文件。
-G
(或 --generator
)选项用于指定要使用的生成器,这是一个构建系统生成工具。生成器根据指定的构建配置生成适合特定平台或开发环境的构建文件,例如 Makefile、Visual Studio 项目文件、Ninja 构建文件等。
-T
这个选项,它用于指定工具集(Toolset)的名称。工具集是一组特定的编译器和工具链,可以在不同的构建环境中使用。
示例
1 | cmake -G "Visual Studio 17 2022" -T "ClangCL" ../source |
1 | cmake -G "Ninja" -D CMAKE_CXX_COMPILER=cl ../source |
add_library
用于定义和构建一个库(静态库或共享库)目标。库是一组编译后的对象文件的集合,它可以供其他目标(如可执行文件或其他库)链接使用。
语法
1 | add_library(<name> [STATIC | SHARED | MODULE] |
STATIC
:指定创建静态库。静态库在链接时会被完全合并到可执行文件中。SHARED
:指定创建共享库(也称为动态库或共享对象)。共享库在运行时加载,多个可执行文件可以共享同一个库实例。MODULE
:用于一些平台,用于创建用于插件加载的库。
示例
1 | add_library(operations STATIC src/operations.cpp) |
创建一个名为 operations的静态库,源文件为operations.cpp 。
1 | add_library(logger STATIC logger/src/log.cpp) |
创建一个名为 logger的静态库,源文件为log.cpp 。
target_link_libraries
用于指定目标与其他库之间的链接关系。它告诉 CMake 在链接可执行文件或库时需要使用哪些其他库。这通常用于链接目标与依赖的库,包括系统库和自定义库。
语法
1 | target_link_libraries(target_name |
target_name
:要链接的目标的名称。PRIVATE
:这些库将会链接到目标自身。这对于库的实现细节和内部使用的依赖非常有用。PUBLIC
:这些库将会链接到目标自身以及使用该目标的其他目标。这对于公共接口库的依赖非常有用。INTERFACE
:这些库将会链接到使用该目标的其他目标,但不会链接到目标自身。
示例
1 | target_link_libraries(HelloAppBinary PUBLIC operations logger) |
链接 HelloAppBinary可执行文件与 operations,logger静态库。
set设置C++标准
1 | set(CXX_STANDARD_REQUIRED ON) |
示例Cmake项目三——Librariy Target
编译产生静态库
仍然使用项目二的代码基础上新增log文件来做演示。在项目根目录新增looger文件夹。其中新建include文件夹和src文件夹。
include文件夹放入log.h
1 |
|
src文件夹放入log.cpp
1 |
|
CMakeLists.txt
1 | cmake_minimum_required(VERSION 3.5) |
main.cpp
1 | #include <iostream> |
PUBLIC、PRIVATE和INTERFACE
PUBLIC
、PRIVATE
和 INTERFACE
是用于指定目标(如库或可执行文件)属性的关键字。这些关键字用于定义目标与其链接的依赖库之间的可见性和传递性。
PUBLIC:
使用 PUBLIC
关键字时,目标的属性将同时应用于目标自身以及依赖于该目标的其他目标。换句话说,使用了该目标的其他目标也会继承这个属性。
例如,当你使用 target_link_libraries
命令指定链接库的时候:
1 | target_link_libraries(MyApp PUBLIC MyLibrary) |
这会将 MyLibrary
的链接属性添加到 MyApp
目标,并且任何依赖于 MyApp
的目标也会继承这个属性。
PRIVATE:
使用 PRIVATE
关键字时,目标的属性仅适用于目标自身。依赖于该目标的其他目标不会继承这个属性。
例如,当你使用 target_include_directories
命令指定头文件路径时:
1 | target_include_directories(MyApp PRIVATE ${CMAKE_SOURCE_DIR}/include) |
这会将头文件路径添加到 MyApp
目标,但是依赖于 MyApp
的其他目标不会继承这个头文件路径。
INTERFACE:
使用 INTERFACE
关键字时,目标的属性仅适用于依赖于该目标的其他目标。目标自身不会获得这个属性。
例如,当你使用 target_link_libraries
命令指定链接库时,同时使用 INTERFACE
:
1 | target_link_libraries(MyApp INTERFACE MyInterfaceLibrary) |
这会将链接属性添加到 MyApp
的依赖目标,但是 MyApp
本身不会链接到 MyInterfaceLibrary
。
使用这些关键字,你可以更精细地控制属性的传递和可见性,以确保你的构建正确地配置和链接依赖项。这在创建库的接口和实现细节时特别有用,因为它允许你在不同的情况下区分目标属性。
include
include
命令在 CMake 中用于包含其他 CMake 脚本文件,以便在当前脚本中使用其中定义的命令、变量和函数。这是一种组织 CMake 配置的常用方式,可以使配置更加模块化和可维护。
语法
1 | include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>] |
基本用法
1 | include(filename [OPTIONAL] [RESULT_VARIABLE var] [NO_POLICY_SCOPE]) |
filename
:要包含的文件的名称或路径。OPTIONAL
:如果指定了该标志,如果文件不存在,include
命令不会产生错误,而是跳过。RESULT_VARIABLE var
:如果指定了该选项,CMake 将把包含文件的结果存储在变量var
中。NO_POLICY_SCOPE
:如果指定了该选项,包含的文件将在没有策略影响的情况下执行。
示例用法
1 | # 包含另一个 CMake 脚本文件 |
通常,include
命令用于将一组相关的 CMake 命令和配置放在一个单独的脚本文件中,然后在主要的 CMakeLists.txt
文件中使用 include
命令来导入这些配置。这有助于保持项目配置的结构和清晰度。
add_subdirectory
add_subdirectory
是 CMake 中的一个命令,用于向当前项目添加一个子目录。它允许你在主项目中包含其他的子项目,每个子项目都可以拥有自己的 CMakeLists.txt
文件和构建配置。
语法
1 | add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM]) |
基本用法
1 | add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL]) |
source_dir
:要添加的子目录的源代码目录。binary_dir
:可选,要求在指定的二进制目录中进行构建。如果省略,将在主项目的构建目录中进行构建。EXCLUDE_FROM_ALL
:可选,如果指定了该选项,子目录的构建将不会作为主项目的一部分进行,默认情况下会构建。
示例用法
假设你的项目结构如下:
1 | luaCopy codeMyProject/ |
在 MyProject/CMakeLists.txt
中,你可以使用 add_subdirectory
命令来添加 subproject
子目录:
1 | add_subdirectory(subproject) |
在 subproject/CMakeLists.txt
中,你可以定义子项目的构建规则和配置。
通过使用 add_subdirectory
,你可以将项目拆分为更小的模块,每个模块都有自己的构建和配置。这种方法有助于提高项目的可维护性和清晰度,同时允许在不同的子项目中定义不同的构建和设置。
message
message
命令用于在执行CMake配置过程中输出信息。它通常用于调试、显示变量的值、显示错误或状态消息等。
语法
1 | General messages |
<mode>
: 指定消息类型。可以是以下几种:- STATUS: 输出前缀为
--
的消息,通常用于显示状态信息。 - WARNING: 生成警告信息,但不会停止CMake的配置或生成过程。
- AUTHOR_WARNING: 类似于WARNING,但是可以被开发者关闭。
- SEND_ERROR: 产生一个错误,错误信息会被输出,并且会停止CMake的进程。
- FATAL_ERROR: 立即停止所有CMake的操作,并退出。错误信息会被输出。
- DEPRECATION: 如果使用的是CMake的过时特性,可以用这个模式来显示废弃警告。
- STATUS: 输出前缀为
- "message to display": 要显示的文本信息。
示例用法
1 | # 基本状态消息 |
传参方式
1 | # Scripts can be run with the command below: |
变量
在CMake中,变量是用来存储信息,如路径、配置选项或是决定构建过程中行为的参数。以下是CMake中关于变量的一些基本概念和常用变量类型:
语法
设置变量: 使用
set
命令来定义或修改变量的值。1
2cmakeCopy code
set(MY_VARIABLE "SomeValue")使用变量: 在引用变量时使用
${}
语法。1
2cmakeCopy code
message(STATUS "The value of MY_VARIABLE is: ${MY_VARIABLE}")变量作用域: 变量在其被定义的目录和子目录中可见。可以设置父目录的变量或者使用
CACHE
选项来跨目录访问变量。
常见变量类型
- 内置变量: CMake预定义的变量,如
CMAKE_PROJECT_NAME
,CMAKE_C_COMPILER
等,用于提供关于项目和构建环境的信息。 - 环境变量: 使用
$ENV{VAR_NAME}
语法来访问,如$ENV{PATH}
。 - 缓存变量: 使用
set(VAR VALUE CACHE TYPE DOCSTRING)
设置,这些变量值将被存储在CMakeCache.txt中,并在项目重新配置时保持。
常用的CMake变量
- CMAKE_PROJECT_NAME: 当前项目的名称。
- CMAKE_C_COMPILER: C编译器的完整路径。
- CMAKE_CXX_COMPILER: C++编译器的完整路径。
- CMAKE_SOURCE_DIR: 项目的顶级源目录。
- CMAKE_BINARY_DIR: 项目的顶级构建目录。
- CMAKE_CURRENT_SOURCE_DIR: 当前处理的CMakeLists.txt所在的源目录。
- CMAKE_CURRENT_BINARY_DIR: 当前处理的CMakeLists.txt所在的构建目录。
使用变量的几点注意
- 字符串和列表: 在CMake中,变量可以存储字符串或列表(字符串序列)。列表使用分号
;
分隔。 - 变量引用: 总是使用
${}
来引用变量。 - 修改和作用域: 注意变量的修改可能会影响到其他目录或父目录,尤其是在使用全局变量或缓存变量时。
1 | #Variables may only contain alphanumeric characters and underscores: https://cmake.org/cmake/help/book/mastering-cmake/chapter/Writing%20CMakeLists%20Files.html#variables |
list
在CMake中,list
命令用于操作列表,其中列表是由分号分隔的字符串。列表在CMake中非常普遍,用于处理路径集合、源文件、定义标志等。list
命令提供了一组功能来操作这些列表,包括添加元素、删除元素、查询列表信息等。
语法
1 | list(LENGTH <list> <out-var>) |
示例
1 | # 定义一个列表变量 |
option
在CMake中,option()
命令用于定义和管理用户在配置项目时可以设置的选项。这些选项通常用于控制构建过程中是否启用某些功能或代码块。
语法
1 | option(<option_variable> "description of the option" [initial value]) |
示例
1 | #The option command |
if
在CMake中,if
语句是控制逻辑的基本构件,它允许根据条件执行不同的代码路径。它的基本形式很像其他编程语言中的if
语句,可以执行基于条件的编译指令、设置变量、调整项目设置等操作。
语法
1 | if(<condition>) |
示例
1 | #The if command |
循环
foreach
语法
1 | foreach(loop_var IN LISTS list_name) |
while
语法
1 | while(condition) |
示例
1 | #Foreach Loop |
function
在CMake中,function
命令用于定义一个新的函数。函数在CMake中是一种将一组命令封装在一起以便重用的方式。它类似于其他编程语言中的函数或过程,可以接受参数,执行操作,并在结束时返回。
语法
1 | function(<name> [arg1 [arg2 [arg3 ...]]]) |
示例
1 | # A function that mimicks the return mechanism in languages like C++ |
macro
CMake宏是CMake构建系统的一个功能,允许用户定义一组命令,然后在CMake脚本中多次调用这组命令。
语法
1 | macro(<name> [arg1 [arg2 [arg3 ...]]]) |
示例
1 | # Macro that mimicks the return mechanism in languages like C++ |
示例CMake项目四
文件目录
1 | E:\CMAKE\CMAKESERIES\EP018 |
代码
CMakeLists.txt
1 | #[=[ |
src/information.h
1 |
|
src/information.cpp
1 |
|
src/main.cpp
1 |
|
src/math/CMakeLists.txt
1 | # The math library |
supermath.h
1 | int add(int a, int b); |
supermath.cpp
1 |
|
src/stats/CMakeLists.txt
1 | # The stats library |
stats.h
1 | double mean(int* values, int count); |
stats.cpp
1 | #include "supermath.h" |
FetchContent
FetchContent
是 CMake 3.11 及以上版本中的一个模块,它使得在构建期间下载外部依赖成为可能。FetchContent
可以在配置时间从给定的URL下载文件或者克隆git仓库,然后在构建期间使用这些下载的内容。使用 FetchContent
的好处是它允许项目自动处理外部依赖,无需手动下载或者将外部库的源代码包含在项目中。
示例
1 | include(FetchContent) |
代码测试
Google Test官方文档
Catch2官方文档
代码文档
Doxygen
Doxygen Awesome
## 代码管理