23、条件判断:if/elseif/else/endif的常用条件,如WIN32、UNIX、MSVC
条件判断,说白了就是让CMake根据当前环境“见人说人话,见鬼说鬼话”。
我在刚接触CMake时,总觉得这玩意儿不就是个构建工具嘛,搞这么复杂干嘛?后来接手一个跨平台项目,Windows上跑得好好的,换到Linux就崩了。嗯,从那以后,我再也不敢小看条件判断了。
一、条件判断的基本语法
CMake的条件判断,跟大多数语言差不多。结构是:
if(条件)
# 条件为真时执行
elseif(另一个条件)
# 另一个条件为真时执行
else()
# 以上都不满足时执行
endif()
注意,endif()后面要加括号。我见过不少新手写成endif,结果CMake直接报错。这个坑,我当年也踩过。
核心要点:条件判断的括号不能省,if、elseif、else、endif都要带括号。
二、最常用的系统判断条件
我个人习惯把系统判断条件分成三类:操作系统、编译器、构建类型。咱们一个一个说。
2.1 操作系统相关
| 条件 | 说明 | 典型场景 |
|---|---|---|
WIN32 |
Windows系统(包括32位和64位) | 调用Windows API、链接WinSock库 |
UNIX |
类Unix系统(Linux、macOS、BSD等) | 使用POSIX接口、链接pthread库 |
APPLE |
macOS或iOS系统 | 使用CoreFoundation框架 |
CYGWIN |
Cygwin环境 | 特殊交叉编译场景 |
举个例子,我在项目中遇到过需要区分Windows和Linux的情况:
if(WIN32)
target_link_libraries(myapp ws2_32)
target_compile_definitions(myapp PRIVATE PLATFORM_WINDOWS)
elseif(UNIX)
target_link_libraries(myapp pthread)
target_compile_definitions(myapp PRIVATE PLATFORM_UNIX)
endif()
你想想看,如果不加这个判断,Windows上链接pthread库,那编译肯定过不去。
小提示:WIN32在64位Windows上也是真值。别被名字骗了,它不表示“32位系统”。
2.2 编译器相关
| 条件 | 说明 | 典型场景 |
|---|---|---|
MSVC |
Microsoft Visual C++编译器 | 处理MSVC特有的编译选项 |
MINGW |
MinGW编译器 | Windows上使用GCC |
CMAKE_COMPILER_IS_GNUCC |
GCC编译器(C语言) | 设置GCC特有的警告选项 |
CMAKE_COMPILER_IS_GNUCXX |
GCC编译器(C++语言) | 设置GCC特有的C++选项 |
我曾经在Windows上用MSVC编译一个项目,结果发现MSVC不支持-Wall这种GCC风格的警告选项。解决办法就是加个判断:
if(MSVC)
target_compile_options(myapp PRIVATE /W4)
elseif(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(myapp PRIVATE -Wall -Wextra)
endif()
注意:MSVC和WIN32不是一回事。WIN32表示操作系统是Windows,MSVC表示编译器是Visual C++。你可以在Windows上用MinGW,这时候WIN32为真,但MSVC为假。
三、条件判断的进阶用法
3.1 变量是否定义
有时候我们需要检查某个变量是否被定义:
if(DEFINED MY_VARIABLE)
message("MY_VARIABLE 已经被定义了")
else()
message("MY_VARIABLE 还没定义")
endif()
这个在检查用户是否传入了某个选项时特别有用。
3.2 版本号比较
CMake支持直接比较版本号:
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0")
target_compile_features(myapp PRIVATE cxx_std_17)
endif()
常用的比较操作符有:VERSION_LESS、VERSION_GREATER、VERSION_EQUAL、VERSION_LESS_EQUAL、VERSION_GREATER_EQUAL。
3.3 逻辑运算
条件可以组合使用:
if(WIN32 AND MSVC)
# Windows + MSVC 的特殊处理
endif()
if(UNIX OR APPLE)
# Unix或macOS的处理
endif()
if(NOT MSVC)
# 非MSVC编译器的处理
endif()
重要:逻辑运算符AND、OR、NOT必须大写。写成小写and、or、not,CMake会当成普通变量名处理。
四、知识体系图
下面这张图总结了条件判断的核心逻辑:
五、避坑指南
我总结几个常见的坑,都是我曾经踩过的:
- 大小写问题:CMake的条件关键字不区分大小写,但变量名区分。比如
WIN32和win32都行,但MY_VAR和my_var是两回事。 - 引号问题:如果变量值可能为空,最好加引号。比如
if("${MY_VAR}" STREQUAL "hello"),避免变量未定义时报错。 - elseif的写法:是
elseif(),不是else if()。中间没有空格。这个我写错过好几次。 - endif的括号:一定要写
endif(),不能只写endif。老版本的CMake可能允许,但新版本已经废弃了这种写法。
曾经踩过的坑:我在一个项目里用了if(MSVC)来判断Windows平台,结果用户用MinGW编译时,这个条件为假,导致链接了一堆不兼容的库。后来我才明白,应该用if(WIN32)判断操作系统,用if(MSVC)判断编译器。两者不能混用。
六、实战建议
最后,给几个我个人习惯的建议:
- 写条件判断时,先写最具体的条件,再写通用的条件。比如先判断
MSVC,再判断WIN32。 - 复杂条件用
AND、OR组合时,加括号明确优先级。虽然CMake的优先级是固定的,但加括号能让代码更易读。 - 条件判断里尽量用CMake内置的变量,比如
CMAKE_SYSTEM_NAME、CMAKE_CXX_COMPILER_ID,这些比自定义变量更可靠。 - 如果条件判断太多,考虑用
cmake_policy或者generator expressions来简化。生成器表达式在复杂场景下比if/elseif更优雅。
条件判断是CMake的基石。掌握了它,你就能写出真正跨平台的构建脚本。下次遇到跨平台问题,先想想:是不是条件判断没写对?
公众号:蓝海资料掘金营,微信deep3321