【软件开发】CMake 学习笔记

CMake 是什么?

是构建系统(如 Visual Studio)的文件(如 .vcxproj .sln)的创建器,具体要生成的构建系统可以通过 CMakePresets 文件中的 generator 指定。

构建系统一般不是跨平台的,但 CMake 支持在不同的操作系统上生成不同的构建系统文件,通过这种包装的方式从而实现了 C++项目的跨平台。

CMake 指令

https://cmake.org/cmake/help/latest/manual/cmake.1.html

# 基于目标位置的“cmake项目”生成“构建系统项目”
cmake <path>
# 编译目标位置的“构建系统项目”,输出其二进制文件[编译cmake项目中的自定义目标]
cmake --build <path> [--target <target>]
# 运行.cmake脚本并为脚本设置可选参数
cmake [-D <var>=<value>]... -P <cmakeScript>

CMakeLists 指令

CMakeLists.txt是生成 CMake 项目的说明文件,用专门的 CMake 语言编写,由于 CMake 也是一种编程语言,因此也支持判断循环等,此外还提供了很多便利的功能性指令。

CMakeLists.txt可以有多份,但每个文件夹至多能放一个,此外顶层CMakeLists.txt文件中的首条指令必须用于指定 CMake 版本,版本指令如下:

# 描述CMake的最低支持版本(顶层CMake文件必带)
cmake_minimum_required(VERSION <version>)

环境变量

CMake 中没有常规编程语言中的变量概念,所有中间数据都用环境变量存储。环境变量即一对字符串键值组,恰好充当变量名和变量值。用户可以自己设置环境变量,也可以获取系统的环境变量。

基本读写

# 设置环境变量[环境变量作用域扩大到父范围(默认为当前函数或目录)]
set(<varName> <varValue> [PARENT_SCOPE])
# 设置缓存变量(全局作用域,持久存储在磁盘)(必须设置FORCE,否则有缓存时不会设置)
set(<varName> <varValue>... CACHE <type> <docstring> FORCE)
# 获取环境变量(下方代码将被直接替换成环境变量值)(支持嵌套使用)
${<varName>}

编辑内容

环境变量本质就是字符串,可以采用 stringlist、包括再次使用 set 等命令来编辑。另外针对非局部作用域变量的编辑还有几个注意点:

  1. 对缓存变量和父作用域变量编辑,只会修改以其为初值的局部变量,因此修改完必须再次显式设置为缓存变量。
  2. 缓存变量可以在构建 cmake 文件时从命令行设置 -D<var>=<value>,注意代码中也要 set 该缓存命令才行,但不要用 FORCE 关键字。

预设环境变量

CMake 系统预设的环境变量,用户可以从中读写一些重要的配置数据。

  • 编译环境

    1. CMAKE_CXX_STANDARD:项目中所用的 C++标准(数字,如 17、20)
    2. CMAKE_CXX_STANDARD_REQUIRED:指定的 C++标准是否是必须的,从而决定编译器不支持时是否停止生成(布尔值)
  • 项目信息

    1. <projectName>_VERSION_MAJOR:声明项目时额外提供的主版本号。
    2. <projectName>_VERSION_MINOR:声明项目时额外提供的次版本号。
  • 目录

    1. CMAKE_SOURCE_DIR:顶级 CMakeLists 的源目录。
    2. CMAKE_BINARY_DIR:顶级 CMakeLists 的构建目录。
    3. CMAKE_CURRENT_SOURCE_DIR:当前 CMakeLists 的源目录。
    4. CMAKE_CURRENT_BINARY_DIR:当前 CMakeLists 的构建目录。
    5. PROJECT_BINARY_DIR:上次调用project()的 CMakeLists 对应的构建目录。
    6. PROJECT_SOURCE_DIR:上次调用project()的 CMakeLists 对应的源目录。
    7. CMAKE_RUNTIME_OUTPUT_DIRECTORY:构建系统最终输出的二进制文件的目录。

    备注:指令 5、6 不受add_subdirectory()影响。

环境变量列表

CMake 中没有常规编程语言中数组的概念,相应的是改用“环境变量列表”代替。环境变量列表本质也是一个环境变量,其值也是一个字符串,但特别的是该值通常是由多个用“;”分隔的条目组成。

许多指令都支持环境变量列表作为输入来支持多参数,同时也配有专门的工具指令来编辑环境变量列表。

# 从变量列表中去除指定项
list(REMOVE_ITEM <list> <value> [<value> ...])
# 根据正则表达式过滤变量列表
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regex>)
# 追加条目
list(APPEND <list> <value>)

流程控制

既然是编程语言,自然也支持逻辑控制。

条件执行

# 基本用法:[不]满足条件时执行
if([NOT] <bool>) \ endif()
# 比较变量的值(由于没有变量的概念,都是通过显式指定比较方式来比较)
if(<variableName> EQUAL <value>)
# 确认字符串是否满足指定的正则表达式
if(<var> MATCHES <regex>)
# 查看环境变量是否定义
if(DEFINED <variableName>)
# 确认路径是否存在
if(EXISTS <inputPath>)

遍历执行

# 遍历环境变量列表
foreach(<var> <list>) \ endforeach()

函数定义

既然是编程语言,自然也支持自定义函数。

# 定义一个宏函数
macro(<macroName> [inputVar]...) \ endmacro()

依赖处理

由于CMakeLists.txt可以有多份,甚至打包成模块,因此需要相关的指令处理依赖。

# 添加一个子目录(子目录需要带有CMakeLists)
add_subdirectory(<source_dir>)
# 寻找一个模块(带有特定配置文件的目录将会被识别为模块,可当成项目使用)
find_package(<packageName> CONFIG REQUIRED)

项目配置

CMakeLists.txt主要用于创建 C++项目,必然需要很多声明项目属性的指令。每个CMakeLists.txt文件中只能声明一个项目,当有多个项目时需要创建多份CMakeLists.txt

基础配置

以下指令是创建一个项目必须要提供的。

# 创建一个项目
project(<projectName>)
# 声明项目的输出为可执行文件
add_executable(<projectName> <sourceFile>...)
# 声明项目的输出为静态/动态库文件
add_library(<projectName> [STATIC|SHARED] <sourceFile>...)
# 声明项目是特殊的无输出项目
add_custom_target(<projectName> SOURCES <sourceFile>...)

可选配置

可选的影响项目属性的配置

# 为项目添加附加包含目录
target_include_directories(<projectName> {PUBLIC|PRIVATE} <headerDir>...)
# 为项目添加附加库
target_link_libraries(<projectName> {PUBLIC|PRIVATE} {<projectName>|<libFile>...})
# 为项目添加预处理定义
target_compile_definitions(<projectName> {PUBLIC|PRIVATE} <definition>...)
# 为项目添加编译选项
target_compile_options(<option>...)
# 将项目分类到vs中的解决方案文件夹(3.26之前需先打开 USE_FOLDERS 功能)
set_target_properties(<projectName> PROPERTIES FOLDER <folderName>)
# 为项目设置编译事件(如下方为编译后执行某cmake脚本)
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND cmake -D target=${target} -P script.cmake
)
  • 依赖项传递说明:

    • PUBLIC:使用该选项添加引用,引用的头文件将传染给使用该项目的其他项目。
    • PRIVATE:引用不具备传染性,其他使用该项目的项目可能要再次添加头文件引用。

    除非项目已经将引用的库完全封装,希望后续的使用者不要再直接调用该引用的库时才该用PRIVATE,否则应设置为PUBLIC

  • 全局项目设置说明:

    上述部分指令如果去除target_前缀和部分参数,可转为对所有项目的设置的指令。

全局配置

以下指令对所有项目都生效

# 设置全局项目的编译选项
add_compile_options(<option>...)

读取配置

反向获取关于项目的一些配置信息

# 获取项目类型(EXECUTABLE|STATIC_LIBRARY)
get_target_property(<outVar> <projectName> TYPE)

文件系统

既然是基于文件的生成系统,自然经常需要和文件系统打交道。

文件处理

# 获取{当前目录|包括子目录}的满足glob通配符的文件路径并将其路径打包在一个变量列表中
file({GLOB | GLOB_RECURSE} <list> <glob>...)
# 获取相对路径
file(RELATIVE_PATH <output> <root-path> <path>) # 创建或覆盖一个文件并写入指定内容
file(WRITE <file> <content>)
# 向指定文件末尾追加内容
file(APPEND <file> <content>)
# 复制文件或文件夹到目标目录
file(COPY <sourceDir> DESTINATION <destinationDir>)
# 复制并修改文件到指定位置。自动替换文件中的环境变量值(@<var>@)并自动移入当前CMakeLists的构建目录。
configure_file(<inputFile> <outputFile>)

路径计算

# 获取路径对应的父目录地址
cmake_path(GET <path-var> PARENT_PATH <out-var>)
# 获取路径对应的文件名
cmake_path(GET <path-var> FILENAME <out-var>)

其他特殊指令

其他一些特殊但常用的指令

# 在vs中将指定文件分类到与文件系统一致的筛选器结构,而不是默认筛选器。
source_group(TREE <fileRootDir> FILES <inputFile>...)
# 在vs中禁用对指定文件的编译
set_property(SOURCE <files> PROPERTY VS_SETTINGS "ExcludedFromBuild=true")

CMakePresets

https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html

“CMakePresets.json”是 CMake 的配置文件,存放在项目的根目录中,用于配置一些 CMake 构建选项。

示例内容如下:

{
//CMakePresets所用的版本
"version": 6,
//定义生成配置。可定义多个,然后在构建指令中指明。
"configurePresets": [
{
//配置名称
"name": "default",
//所用的生成器,用于将CMake项目转为原生项目
"generator": "Visual Studio 17 2022",
//生成输出目录(构建目录)
"binaryDir": "${sourceDir}/build",
//配置CMake缓存变量(一种长期存储的环境变量,存在CMakeCache.txt文件中)
"cacheVariables": {
//构建中使用的一些构建工具,由cmake语言写成
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
}
]
}

配置完毕后生成时通过添加--preset选项,如cmake --preset=<presetName>指令使用。

使用技巧

参考资料

【软件开发】CMake学习笔记的更多相关文章

  1. cmake 学习笔记(三)

    转自:http://blog.csdn.net/dbzhang800/article/details/6329314 接前面的 Cmake学习笔记(一) 与 Cmake学习笔记(二) 继续学习 cma ...

  2. cmake 学习笔记(三) (转)

    接前面的 Cmake学习笔记(一) 与 Cmake学习笔记(二) 继续学习 cmake 的使用. 学习一下cmake的 finder. finder是神马东西? 当编译一个需要使用第三方库的软件时,我 ...

  3. cmake学习笔记(五)

    在cmake 学习笔记(三) 中简单学习了 find_package 的 model 模式,在cmake 学习笔记(四)中了解一个CMakeCache相关的东西.但靠这些知识还是不能看懂PySide使 ...

  4. cmake 学习笔记(二)

    在 Cmake学习笔记一 中通过一串小例子简单学习了cmake 的使用方式. 这次应该简单看看语法和常用的命令了. 简单的语法 注释 # 我是注释 命令语法 COMMAND(参数1 参数2 ...) ...

  5. 微信小程序开发:学习笔记[7]——理解小程序的宿主环境

    微信小程序开发:学习笔记[7]——理解小程序的宿主环境 渲染层与逻辑层 小程序的运行环境分成渲染层和逻辑层. 程序构造器

  6. 微信小程序开发:学习笔记[5]——JavaScript脚本

    微信小程序开发:学习笔记[5]——JavaScript脚本 快速开始 介绍 小程序的主要开发语言是 JavaScript ,开发者使用 JavaScript 来开发业务逻辑以及调用小程序的 API 来 ...

  7. 微信小程序开发:学习笔记[4]——样式布局

    微信小程序开发:学习笔记[4]——样式布局 Flex布局 新的布局方式 在小程序开发中,我们需要考虑各种尺寸终端设备上的适配.在传统网页开发,我们用的是盒模型,通过display:inline | b ...

  8. 微信小程序开发:学习笔记[3]——WXSS样式

    微信小程序开发:学习笔记[3]——WXSS样式 快速开始 介绍 WXSS(WeiXin Style Sheets)是一套用于小程序的样式语言,用于描述WXML的组件样式,也就是视觉上的效果. WXSS ...

  9. 微信小程序开发:学习笔记[2]——WXML模板

    微信小程序开发:学习笔记[2]——WXML模板 快速开始 介绍 WXML 全称是 WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件.事件系统,可以构建 ...

  10. 微信小程序开发:学习笔记[1]——Hello World

    微信小程序开发:学习笔记[1]——Hello World 快速开始 1.前往微信公众平台下载微信开发者工具. 地址:https://mp.weixin.qq.com/debug/wxadoc/dev/ ...

随机推荐

  1. 使用Docker快速部署一个Net项目

    前言 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化. 优点 Web 应用的自动化打包和发布. 自动化测试和 ...

  2. git gitignore文件不生效

    配置了 .gitigore 文件不生效,是刚开始将那些过滤的文件加到了版本控制.后续增加的,没有进入到版本控制 解决办法就是从版本控制移除,重新更新下gitignore文件 执行以下命令: 根据情况自 ...

  3. shell 获取 目录名 当前目录名

    Four ways to extract the current directory name By  Sergio Gonzalez Duran on November 06, 2007 (9:00 ...

  4. 关于Qt几百个版本无法兼容的深度思考

    关于Qt众多版本(至少几百个)都不兼容的问题,在经过和Qt中国的林斌大神和其他大神(Qt非官方技术交流群)头脑风暴以后,最终得出以下的结论. Qt在二进制兼容这块,已经做了最大的努力,通过将各种代码细 ...

  5. 从零开始构建一个基于大模型和 RAG 的知识库问答系统

    SimpleAbdQA 本项目所使用的大模型为:qwen1.8b 演示中所使用Embedding为:Word2vec 一.介绍 通过从本项目中,你可以得到: 了解基于大模型的本地知识库的运作原理 了解 ...

  6. [转]Automatic Image Stitching with Accord.NET

    原文链接:Automatic Image Stitching with Accord.NET

  7. 即时通讯技术文集(第31期):IM开发综合技术合集(Part4) [共13篇]

    为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第31 期. ​[- 1 -] IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践 ...

  8. .NET Core + Kafka 开发指南

    什么是Kafka Apache Kafka是一个分布式流处理平台,由LinkedIn开发并开源,后来成为Apache软件基金会的顶级项目.Kafka主要用于构建实时数据管道和流式应用程序. Kafka ...

  9. [.NET] API网关选择:YARP还是Ocelot?

    API网关选择:YARP还是Ocelot? 摘要 随着微服务架构的流行,API网关在系统架构中扮演着越来越重要的角色.在.NET生态中,YARP(Yet Another Reverse Proxy)和 ...

  10. Zookeeper、Eureka、Consul、Nacos、Etcd全方位对比

    前三篇博文分别记录了Eureka.Zookeeper.Consul三个服务中心,那么他们之前有何区别呢? 一: 因为不会同时存在或者同时满足C.A.P三个方面,所以只能存在cp,ap,ca三种体系, ...