当前位置: 首页 > news >正文

cmake之旅(2)

cmake之旅(2)

  • 1 从一个最小的 CMakeLists.txt 开始
  • 2 cmake_minimum_required —— 版本约束
  • 3 project —— 项目定义
  • 4 message —— 打印信息
  • 5 set —— 变量定义
    • 5.1 普通变量
    • 5.2 CMake 内置变量
    • 5.3 缓存变量
  • 6 add_executable —— 生成可执行文件
  • 7 include_directories —— 指定头文件搜索路径
  • 8 完整示例
  • 9 补充:CMake 中容易踩的坑
  • 10 本篇命令速查表
  • 11 总结与下一篇预告

同系列文章:
cmake之旅(1):构建的过程
cmake之旅(2):CMakeLists.txt 核心语法
cmake之旅(3):多目录项目管理
cmake之旅(4):静态库与动态库
cmake之旅(5):函数、宏与 .cmake 模块
cmake之旅(6):查找和使用第三方库
cmake之旅(7):编译选项与条件编译
cmake之旅(8):Modern CMake 与 target 思维
cmake之旅(9):安装与导出
cmake之旅(10):自动化测试与 CTest

在上一篇中,我们从手动编译一步步走到了 CMake,体验了 CMake 带来的便利。但上一篇中的 CMakeLists.txt 只是一个"能跑就行"的版本,我们对里面的每一行到底在做什么,可能还是一知半解。

这一篇我们就来系统地学习 CMakeLists.txt 的核心语法,搞清楚每条命令的含义和用法,为后续写出更复杂的 CMakeLists.txt 打下基础。

1 从一个最小的 CMakeLists.txt 开始

还是使用上一篇中的简单程序:

#include<iostream>intmain(){std::cout<<"Hello Cmake"<<std::endl;return0;}

对应的 CMakeLists.txt 最少只需要三行:

# 设定 CMake 的最低版本要求 cmake_minimum_required(VERSION 3.10) # 定义项目名称 project(HelloCMake) # 生成可执行文件,第一个参数是可执行文件名,后面是源文件 add_executable(main main.cpp)

没错,就这三行就够了。这三行也是任何一个 CMakeLists.txt 的"骨架"。接下来我们逐一拆解。

2 cmake_minimum_required —— 版本约束

cmake_minimum_required(VERSION 3.10)

这条命令指定了构建这个项目所需要的 CMake 最低版本。为什么需要它?因为不同版本的 CMake 支持的特性不同,比如某些命令在旧版本中根本不存在。加上这个约束后,如果用户的 CMake 版本太低,CMake 会直接报错提示,而不是跑出一堆莫名其妙的错误。

建议:一般设置为 3.10 或更高就可以了,3.10 之后的版本支持了大部分常用的现代 CMake 特性。如果你不确定选什么版本,可以在终端中运行cmake --version看看自己当前的版本,选一个不高于当前版本的值即可。

3 project —— 项目定义

project(HelloCMake)

这条命令做了两件事情:

  1. 定义了项目名称为HelloCMake
  2. CMake 自动帮你设置了一系列相关的变量

我们来验证一下它到底生成了哪些变量。修改 CMakeLists.txt:

cmake_minimum_required(VERSION 3.10) project(HelloCMake) # 打印 project 自动生成的变量 message("项目名称: ${PROJECT_NAME}") message("项目源码目录: ${PROJECT_SOURCE_DIR}") message("项目构建目录: ${PROJECT_BINARY_DIR}") add_executable(main main.cpp)

执行构建命令后,你会在终端中看到类似这样的输出:

项目名称: HelloCMake 项目源码目录: /home/user/my_project 项目构建目录: /home/user/my_project/build

project()不仅仅是起个名字,它还自动帮我们设置好了几个有用的路径变量。

project 还可以指定更多信息:

project(HelloCMake VERSION 1.0.0 DESCRIPTION "一个学习cmake的项目" LANGUAGES CXX )
  • VERSION:项目版本号,设置后可以通过${PROJECT_VERSION}获取
  • DESCRIPTION:项目描述
  • LANGUAGES:指定项目使用的语言,CXX表示 C++,C表示 C。如果不写,默认支持 C 和 C++

4 message —— 打印信息

刚才我们已经用到了message,它是 CMake 中的"打印函数",在调试 CMakeLists.txt 时非常有用。

# 普通信息 message("这是一条普通信息") # 状态信息(推荐使用,输出时会带 -- 前缀) message(STATUS "这是一条状态信息") # 警告信息(黄色提醒,不会中断构建) message(WARNING "这是一条警告") # 错误信息(会中断构建) message(FATAL_ERROR "出错了,构建终止!")

执行后输出大概是这样的:

这是一条普通信息 -- 这是一条状态信息 CMake Warning at CMakeLists.txt:x (message): 这是一条警告 CMake Error at CMakeLists.txt:x (message): 出错了,构建终止!

建议:日常调试推荐使用message(STATUS ...),输出整齐,容易和 CMake 本身的输出区分。

5 set —— 变量定义

set是 CMake 中最常用的命令之一,用于定义变量。

5.1 普通变量

# 定义一个变量 set(MY_VAR "hello") # 使用变量(通过 ${} 取值) message(STATUS "MY_VAR 的值是: ${MY_VAR}")

输出:

-- MY_VAR 的值是: hello

一个常见的用法:用 set 管理源文件列表

# 将源文件列表存入变量 set(SOURCES main.cpp add/add.cpp de/de.cpp ) # 使用变量来生成可执行文件 add_executable(main ${SOURCES})

这样做的好处是:当源文件增多时,只需要在set里面加一行,不用去改其他地方。

5.2 CMake 内置变量

CMake 预定义了很多有用的内置变量,最常用的几个如下:

变量名含义示例值
CMAKE_CXX_STANDARDC++ 标准版本11、14、17、20
CMAKE_CXX_STANDARD_REQUIRED是否强制要求该标准True / False
CMAKE_BUILD_TYPE构建类型Debug / Release
CMAKE_SOURCE_DIR顶层源码目录/home/user/project
CMAKE_BINARY_DIR顶层构建目录/home/user/project/build
CMAKE_CURRENT_SOURCE_DIR当前 CMakeLists.txt 所在目录随文件位置变化

最常用的就是设置 C++ 标准:

# 使用 C++17 标准 set(CMAKE_CXX_STANDARD 17) # 如果编译器不支持 C++17,则报错(而不是降级到更低标准) set(CMAKE_CXX_STANDARD_REQUIRED True)

思考一下:如果不设置CMAKE_CXX_STANDARD_REQUIRED为 True 会怎样?假设你代码中用了 C++17 的特性,但编译器只支持 C++14,CMake 不会报错,而是静默降级到 C++14,然后你的代码就编译失败了,报出一堆看不懂的编译错误。所以强烈建议始终将CMAKE_CXX_STANDARD_REQUIRED设为 True

5.3 缓存变量

除了普通变量,CMake 还有一种"缓存变量",它的值会被保存到构建目录下的CMakeCache.txt文件中,在多次运行 CMake 时不会丢失。

# 定义一个缓存变量(类型为 STRING,默认值为 "default",带描述信息) set(MY_OPTION "default" CACHE STRING "这是一个可配置的选项")

缓存变量通常用于提供可配置的选项,让使用者在不修改 CMakeLists.txt 的情况下调整构建行为。比如你可以在命令行中覆盖它:

cmake-DMY_OPTION="custom_value"..

不过目前阶段,你只需要知道有这个概念就行,后续我们会在讲条件编译的时候深入使用它。

6 add_executable —— 生成可执行文件

add_executable(main main.cpp)

这条命令告诉 CMake:用 main.cpp 编译生成一个叫 main 的可执行文件。

如果有多个源文件,直接往后面加就行:

add_executable(main main.cpp add/add.cpp de/de.cpp)

或者配合前面讲的set变量:

set(SOURCES main.cpp add/add.cpp de/de.cpp ) add_executable(main ${SOURCES})

两种写法效果完全相同,但后者更整洁。

7 include_directories —— 指定头文件搜索路径

回忆一下上一篇的例子,我们的头文件分布在 add 和 de 目录中。如果不告诉 CMake 去哪里找头文件,编译就会报错:“找不到 add.h”。

# 告诉编译器去 add 和 de 目录下寻找头文件 include_directories(add de)

加上这行之后,代码中的#include "add.h"就能被正确找到了。

注意:include_directories是一个"全局"命令,它会影响当前 CMakeLists.txt 中所有的目标。在后续的文章中,我们会学到更"精准"的替代方案target_include_directories,它可以只针对某个特定目标生效。这里先了解即可。

8 完整示例

我们把上面的知识串起来,用上一篇的多文件项目来写一个完整的 CMakeLists.txt:

文件结构:

├──add│ ├── add.cpp │ └── add.h ├── de │ ├── de.cpp │ └── de.h ├── main.cpp └── CMakeLists.txt

CMakeLists.txt:

# ============================================================ # 项目:HelloCMake # 描述:cmake之旅(2)的完整示例 # ============================================================ # 设定 CMake 的最低版本要求 cmake_minimum_required(VERSION 3.10) # 定义项目信息 project(HelloCMake VERSION 1.0.0 DESCRIPTION "cmake之旅学习项目" LANGUAGES CXX ) # 设定 C++ 标准为 C++17,并强制要求 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) # 打印一些调试信息 message(STATUS "项目名称: ${PROJECT_NAME}") message(STATUS "项目版本: ${PROJECT_VERSION}") message(STATUS "C++ 标准: ${CMAKE_CXX_STANDARD}") # 定义源文件列表 set(SOURCES main.cpp add/add.cpp de/de.cpp ) # 指定头文件搜索路径 include_directories(add de) # 生成可执行文件 add_executable(${PROJECT_NAME} ${SOURCES})

构建和运行:

mkdirbuildcdbuild cmake..make./HelloCMake

注意这里我们用了${PROJECT_NAME}作为可执行文件名,这样可执行文件的名字就和项目名称自动保持一致。

执行cmake ..时你会看到我们用message打印的信息:

-- 项目名称: HelloCMake -- 项目版本: 1.0.0 -- C++ 标准: 17

9 补充:CMake 中容易踩的坑

在刚开始写 CMakeLists.txt 的时候,有一些常见问题值得提前了解。

变量未定义不会报错

在 CMake 中,如果你引用了一个不存在的变量,它不会报错,而是返回空字符串:

message(STATUS "值: ${NOT_EXIST}")

输出只会是-- 值:,不会有任何警告。这在调试时容易让人困惑——明明写了变量,怎么值是空的?所以拼写的时候要格外注意。

命令名不区分大小写,变量名区分大小写

# 以下两行等价 message(STATUS "hello") MESSAGE(STATUS "hello") # 但是变量名是区分大小写的 set(myVar "aaa") set(MYVAR "bbb") message(STATUS ${myVar}) # 输出 aaa message(STATUS ${MYVAR}) # 输出 bbb

建议:命令统一使用小写(这是现代 CMake 的约定),变量名使用大写或大写加下划线。

CMakeLists.txt 文件名大小写敏感

文件名必须是CMakeLists.txt,首字母 C、M、L 大写,其余小写。写成cmakelists.txtCMAKELISTS.TXT都是不行的。

10 本篇命令速查表

命令作用示例
cmake_minimum_required设定 CMake 最低版本cmake_minimum_required(VERSION 3.10)
project定义项目名称和信息project(MyApp VERSION 1.0 LANGUAGES CXX)
message打印信息(调试用)message(STATUS "hello")
set定义变量set(MY_VAR "value")
add_executable生成可执行文件add_executable(app main.cpp)
include_directories添加头文件搜索路径include_directories(include/)

11 总结与下一篇预告

这一篇我们系统学习了 CMakeLists.txt 的核心语法:版本约束、项目定义、变量、打印信息、生成可执行文件、指定头文件路径。掌握这些之后,对于简单的项目已经可以自如地编写 CMakeLists.txt 了。

但是你可能已经发现了一个问题:目前所有的源文件都平铺在同一个 CMakeLists.txt 中。如果项目变大了,比如有十几个模块、几十个源文件,全都塞在一个 CMakeLists.txt 里,管理起来就非常痛苦了。

有没有办法让每个模块都有自己的 CMakeLists.txt,然后由一个顶层的 CMakeLists.txt 来统一管理呢?

http://www.jsqmd.com/news/619648/

相关文章:

  • STM32嵌入AI模型实战指南
  • 打造沉浸式智能AI问答助手:Vue + UniApp 全端实战(支持 Markdown/公式/多模态交互)夹
  • 避坑指南:QTableWidget中使用QCheckBox时容易忽略的5个细节问题
  • 避坑指南|2026靠谱GEO服务商推荐 - 品牌测评鉴赏家
  • Spring Boot + Redis 缓存优化方案,解决 Redis 缓存的三大经典问题
  • 学习dp入门
  • 3步打造轻量Windows 11:tiny11builder精简系统实战指南
  • SGLang实战:如何用Python DSL编写带分支的LLM生成任务(附完整代码)
  • 喔去,litellm 竟然被投毒了,赶紧检查你的机器中招了没有侥
  • 物联网云平台工业设备对接远程控制数据采集视频接入开源可二次开发 该物联网云平台使用 Java ...
  • 如何用OnmyojiAutoScript实现阴阳师全自动托管:每天节省2小时游戏时间的完整指南
  • 互联网企业项目管理的核心挑战
  • 基于MPC的模型预测轨迹跟踪控制联合仿真simulink模型+carsim参数设置 效果如图
  • 短剧付费转化系统设计:试看 + 阶梯定价 + 会员锁客全链路
  • 智慧农业无人机数字孪生系统源码:基于WebGL的3D农场可视化平台
  • 我想在豆包做广告,联系谁?第三方豆包优化方案助您精准获客 - 品牌2026
  • 扔给 AI 自动部署!Wazuh 安全监控平台 - 一键部署提示词
  • 【可信计算】TPM2-tools实战:从文件度量到完整性验证
  • SpringSecurity(3)学习内容
  • fre:ac音频转换器:3大核心功能让你的音乐管理焕然一新
  • 从Vivado工程到上电自启:ZYNQ7020双核ARM+FPGA的完整启动流程详解
  • EC-QA-04-质量问题跟踪表
  • 3分钟掌握G-Helper:终极华硕笔记本性能优化指南
  • 单相全桥逆变器Simulink仿真分析与MATLAB实现探索
  • 智能销售辅助在机械设备行业转化率突破:从经验依赖到AI赋能的革命性转型
  • 基于单片机控制的汽车电动车窗
  • 现在不重构组织,Q3将面临AI人才断层潮:SITS2026圆桌披露的21天敏捷转型启动清单
  • 解密WarcraftHelper:现代系统兼容方案让经典魔兽争霸3重获新生
  • 西门子PLC 1200与V20变频器USS通讯项目:包含实际程序、CAD图纸及详细注释
  • 避开这些坑!Applied Intelligence投稿6中5后,我总结的格式与语言避雷指南(附Decision Letter模板)