if()、elseif()、else()、endif() 中变量不加${},字符串比较必须用STREQUAL等操作符;if(ENABLE_LOG)先判断变量是否定义,再判其值是否为ON/1/TRUE等真值;跨平台应优先用WIN32/APPLE精确匹配,避免UNIX误覆盖;数值比较用LESS/EQUAL,字符串比较用STRLESS/STREQUAL。

直接写 if()、elseif()、else()、endif(),但变量名不加 ${},字符串比较必须用 STREQUAL 等操作符——这是最常踩坑的点。
if(ENABLE_LOG) 这种写法到底在判断什么?
它不是在展开变量值,而是在做「布尔存在性+真值联合判断」:先检查 ENABLE_LOG 是否被定义,再看它的值是否属于 CMake 认为的真值(如 ON、1、TRUE、非空字符串)。如果变量未定义,整个条件直接为假;如果定义了但值是 OFF 或空,也判为假。
常见错误现象:
- 写了
set(ENABLE_LOG "yes"),但if(ENABLE_LOG)不成立——因为"yes"是字符串,不是布尔真值 - 写了
if(${ENABLE_LOG}),CMake 报错或行为异常——${}展开后可能变成空或非法 token,触发语法错误
正确做法:
- 用布尔语义赋值:
set(ENABLE_LOG ON)或option(ENABLE_LOG "Enable logging" ON) - 若必须用字符串判断,请显式用比较操作符:
if(ENABLE_LOG STREQUAL "yes")
跨平台判断要怎么写才可靠?
CMake 提供了 WIN32、APPLE、UNIX 这类预定义变量,但它们不是互斥的:UNIX 在 macOS 和 Linux 下都为真,APPLE 仅在 macOS 下为真。直接链式写 if(WIN32) elseif(APPLE) elseif(UNIX) 会导致 macOS 同时命中前两个分支(如果顺序不对)或漏判。
推荐写法:
- 优先用精确匹配:
if(WIN32)、if(APPLE)、if(LINUX)(需先find_package(CMakeParseArguments)或手动检测CMAKE_SYSTEM_NAME) - 或用嵌套排除:
if(WIN32)→elseif(APPLE)→else()(此时else覆盖所有非 Windows/macOS 的 UNIX-like 系统,包括 Linux、FreeBSD) - 避免只依赖
UNIX做差异化配置,除非你明确接受它覆盖所有类 Unix 系统
数值和字符串比较为什么不能混用?
LESS、EQUAL 等是纯数值比较操作符,遇到字符串会静默转成 0,导致逻辑失效。比如 if("10" LESS "2") 实际比较的是 10 LESS 2 → 假;而 if("10" STRLESS "2") 才是按字典序比较 → 真(因为 "10" 字符串首字符 '1' '2')。
容易忽略的细节:
-
CMAKE_BUILD_TYPE是字符串,必须用STREQUAL:if(CMAKE_BUILD_TYPE STREQUAL "Debug") -
CMAKE_CXX_STANDARD是数字,可用GREATER_EQUAL:if(CMAKE_CXX_STANDARD GREATER_EQUAL 17) - 正则匹配用
MATCHES:if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
嵌套 if 和 elseif 容易出什么问题?
嵌套本身没问题,但 CMake 的 elseif() 只关联最近一层的 if(),且每个 if() 必须有对应 endif()。缩进不匹配、漏写 endif()、或在 elseif() 里又开了新 if() 却没闭合,都会导致解析失败或逻辑错位。
实操建议:
- 每个
if()后立刻写endif(),再往中间填内容,避免漏闭合 - 复杂嵌套时,在
endif()后加注释:endif() # if(APPLE),提高可读性 - 避免多层嵌套判断同一类条件(如反复判断平台),改用单层
if()+elseif()链更清晰
真正难处理的是变量作用域和求值时机——if() 内部定义的变量默认只在当前作用域有效,且所有变量展开发生在条件解析前。这意味着你不能靠一个 if 块里 set 的变量去影响外层另一个 if 的判断逻辑,除非显式用 PARENT_SCOPE 传递。这点极易被忽略,却直接影响条件分支的实际效果。


















