3、CMake基础语法:注释、变量定义与引用、set()命令、message()命令、数学运算
各位同学,欢迎来到第三章。前两章我们聊了CMake能干什么,以及怎么搭建环境。从这章开始,咱们要真正动手写CMakeLists.txt了。
说实话,我刚开始学CMake的时候,觉得语法挺别扭的。跟C++、Python都不一样,大小写不敏感,变量引用还要加个${}。但用久了你会发现,它其实很简洁,设计思路就是「把事情说清楚,别绕弯子」。
这一章,我们就把CMake最基础的语法过一遍。包括怎么写注释、怎么定义和使用变量、两个最常用的命令set()和message(),以及简单的数学运算。这些都是你以后写CMake脚本的「砖瓦」。
3.1 注释:给代码加点「说明书」
写注释这件事,我个人觉得比写代码本身还重要。你想想看,三个月后你回头看自己写的CMakeLists.txt,如果没有注释,你可能得花半小时回忆当初为什么这么写。
CMake支持两种注释方式:
- 单行注释:用
#开头,从#到行尾都是注释。 - 多行注释:用
#[[开头,]]结束,中间可以跨多行。
# 这是一个单行注释
set(MY_VAR "hello") # 行尾也可以加注释
#[[
这是一个多行注释
可以写好几行
适合用来临时屏蔽一大段代码
]]
# 这里用STATIC是因为动态库在目标平台上有兼容性问题,而不是 # 设置变量为STATIC。后者看代码本身就能明白,前者才是真正的信息量。
3.2 变量定义与引用:CMake的「便签纸」
变量在CMake里就像便签纸——你把一个值写在上面,贴在那里,需要的时候撕下来用。定义变量用 set() 命令,引用变量用 ${变量名}。
举个例子:
set(PROJECT_NAME "MyApp")
set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)
message("项目名称:${PROJECT_NAME}")
message("版本号:${VERSION_MAJOR}.${VERSION_MINOR}")
这里有几个要点:
- 变量名习惯用大写,但不是强制要求。我个人喜欢全大写,一眼就能看出是CMake变量。
- 字符串可以不加引号,但如果字符串里有空格,必须加引号。
- 引用变量时,
${}会直接展开成变量的值。
message() 是你的好朋友,多打印多检查。
3.3 set() 命令详解:不只是赋值
set() 是CMake里最常用的命令之一。它的基本语法是:
set(<变量名> <值> [CACHE TYPE DOCSTRING FORCE])
我们先用最简单的形式:
set(SRC_FILES main.cpp utils.cpp network.cpp)
set(DEBUG_FLAGS -g -O0 -Wall)
注意,set() 可以一次设置多个值,这些值会组成一个列表(list)。比如上面的 SRC_FILES 实际上是一个包含三个元素的列表。
稍微进阶一点,set() 还可以配合 CACHE 关键字,创建缓存变量。缓存变量会出现在CMake的GUI界面中,用户可以在配置时修改:
set(MY_OPTION "ON" CACHE BOOL "是否启用某个特性")
这个功能在制作可配置的库时非常有用。我做过一个图像处理库,用户可以通过CMake GUI开关不同的算法模块,就是靠 CACHE 变量实现的。
| 参数 | 说明 | 示例 |
|---|---|---|
| 变量名 | 标识符,习惯大写 | MY_VAR |
| 值 | 可以是字符串、数字、列表 | "hello" 或 42 或 a b c |
| CACHE | 创建缓存变量(可选) | CACHE BOOL "描述" |
| FORCE | 强制覆盖已有值(慎用) | FORCE |
3.4 message() 命令:你的调试利器
写CMake脚本最痛苦的是什么?就是出了问题不知道变量到底等于什么。message() 就是你的救命稻草。
基本用法:
message("你好,CMake!")
message("SRC_FILES = ${SRC_FILES}")
message() 还支持不同的消息类型,用关键字控制:
message(STATUS "正在检查依赖...") # 状态信息,白色
message(WARNING "编译器版本可能过低") # 警告,黄色
message(AUTHOR_WARNING "这个用法已弃用") # 作者警告
message(SEND_ERROR "配置错误") # 错误,继续执行但会失败
message(FATAL_ERROR "致命错误,停止") # 致命错误,立即停止
message(STATUS "...") 打印当前进度。比如「检查到OpenCV版本为4.5.0」、「源文件列表为xxx」。这样别人看你的构建日志,一目了然。
3.5 数学运算:让CMake帮你算数
你可能会问:「CMake还需要做数学运算?」嗯,确实需要。比如计算版本号、计算数组索引、或者根据条件计算编译参数。
CMake用 math() 命令做数学运算,语法是:
math(EXPR <输出变量> "<表达式>")
看例子:
set(A 10)
set(B 3)
math(EXPR SUM "${A} + ${B}")
math(EXPR DIFF "${A} - ${B}")
math(EXPR PROD "${A} * ${B}")
math(EXPR QUOT "${A} / ${B}") # 整数除法,结果是3
math(EXPR MOD "${A} % ${B}") # 取模,结果是1
message("和:${SUM},差:${DIFF}")
message("积:${PROD},商:${QUOT},余:${MOD}")
支持的运算符包括:+ - * / % | & ^ ~ << >>,跟C语言基本一致。注意,CMake的数学运算只支持整数,不支持浮点数。
math(EXPR VER "${MAJOR}.${MINOR}"),结果CMake报错了。因为 math() 不支持浮点数,点号被当成了非法字符。正确的做法是分开计算,最后用字符串拼接。
3.6 综合示例:一个小脚本
把今天学的知识串起来,写一个完整的CMakeLists.txt片段:
cmake_minimum_required(VERSION 3.10)
project(HelloCMake)
# 定义版本号
set(VERSION_MAJOR 2)
set(VERSION_MINOR 1)
set(VERSION_PATCH 0)
# 计算下一个版本号(假设我们要打补丁)
math(EXPR NEXT_PATCH "${VERSION_PATCH} + 1")
set(NEXT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${NEXT_PATCH}")
# 输出信息
message(STATUS "当前版本:${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
message(STATUS "下一个版本:${NEXT_VERSION}")
# 源文件列表
set(SOURCES main.cpp utils.cpp)
# 可执行文件
add_executable(myapp ${SOURCES})
这个脚本虽然简单,但包含了注释、变量定义、数学运算、消息输出,以及最终生成可执行文件。你可以在自己的项目里试试看。
set() 和 message() 是你最常用的两个命令,math() 在需要计算时很有用。这些基础语法,你会在后面的每一章里反复用到。嗯,今天就到这里,下一章我们聊控制流——if、else、循环这些,到时候你会发现,CMake其实也挺「编程」的。
公众号:蓝海资料掘金营,微信deep3321