add_definitions已被弃用,应改用target_compile_definitions:它支持按target、配置和可见性(PRIVATE/PUBLIC/INTERFACE)精准控制预处理定义,避免全局污染,且兼容生成器表达式与跨平台字符串转义。

add_definitions 已被弃用,别再用了
从 CMake 3.12 开始,add_definitions 就被标记为 deprecated,3.20 之后文档明确建议停用。它不区分目录作用域、不支持生成器表达式、无法按配置(如 Debug/Release)条件化定义,而且会全局污染所有 target —— 即使你只想让某个 libA 知道 ENABLE_LOGGING,它也会悄悄塞进 test_bench 的编译命令里。
用 target_compile_definitions 替代,按需、按配置、按 target 绑定
这是当前唯一推荐的方式,语义清晰、作用域精准、支持条件表达式。关键点:
-
target_compile_definitions必须在add_executable或add_library之后调用,且只影响指定 target 及其依赖(除非显式用INTERFACE) - 定义无值宏:直接写名字,如
MY_FEATURE - 定义带值宏:用
=赋值,如VERSION_STRING="\"1.2.3\""(注意双引号和转义) - 按配置定义:加
$<debug></debug>等生成器表达式,例如$<debug>DEBUG=1</debug> - 导出给下游使用:加
INTERFACE或PUBLIC修饰符,比如PUBLIC ENABLE_FOO=1
示例:
add_library(mylib src.cpp)
target_compile_definitions(mylib
PRIVATE MY_INTERNAL_FLAG
PUBLIC ENABLE_LOGGING=1
$<CONFIG:Debug>DEBUG_BUILD=1
)
为什么不用 add_compile_options(-D...)?
虽然 add_compile_options(-DFOO=1) 看似能用,但它和 add_definitions 一样是全局的,且绕过了 CMake 对宏定义的类型检查与作用域管理。更严重的是:它把 -D 当作普通编译选项透传,CMake 不会帮你做 shell 转义或配置隔离,跨平台时容易出错(比如 Windows 上 cmd 对引号处理和 Linux bash 不同)。遇到 -DMY_STR="hello world" 这种带空格的值,几乎必然失败。
立即学习“C++免费学习笔记(深入)”;
宏定义里的字符串字面量怎么安全传递?
这是最常踩的坑:CMake 不会自动帮你加双引号,target_compile_definitions(mylib PRIVATE MSG="hello") 实际生成的是 -DMSG=hello,而 C++ 代码里 std::cout 会报错,因为预处理器展开后是 <code>std::cout (未定义标识符)。
正确做法只有两种:
- 手动加转义双引号:
target_compile_definitions(mylib PRIVATE MSG="\"hello\"")→ 生成-DMSG="hello" - 用
string(REPLACE ...)构造(适合动态内容),但多数情况直接写转义更直白
如果宏值来自变量,务必用 ${} 展开后再套引号:set(VER "2.1.0"); target_compile_definitions(mylib PRIVATE VERSION="\"${VER}\"")
compile_commands.json 里,调试时直接搜 -D 最可靠;别依赖 IDE 的“跳转定义”或 CMake GUI 的变量面板——它们常缓存旧状态。


















