必须用set()明确定义变量,否则引用为空;变量需用${}引用,普通变量默认局部有效,跨作用域需PARENT_SCOPE,缓存变量用CACHE关键字并注意FORCE覆盖规则。

要在CMakeLists.txt中让项目版本号、源文件列表或开关选项能被多处复用,就必须明确定义变量——不定义就直接引用会得到空字符串,导致target_sources()找不到文件或if()判断永远为假。
定义普通变量:用set()赋值
在CMakeLists.txt任意位置写set(变量名 值)即可创建普通变量,比如set(PROJECT_VERSION "1.2.0")。
变量值默认是字符串,哪怕你写set(COUNT 42),它也存为字符串"42";多个值用空格分隔,CMake自动用分号拼成列表:set(SOURCES main.cpp utils.h test.c) → ${SOURCES}实际是"main.cpp;utils.h;test.c"。
【变量名不要以CMAKE_开头】,否则可能覆盖CMake内置变量,引发不可预测的构建失败。
如果想让变量在父作用域(比如上层CMakeLists.txt)也生效,必须显式加PARENT_SCOPE参数:set(MY_FLAG ON PARENT_SCOPE)。不加这个,函数或add_subdirectory子目录里改了变量,父级完全感知不到。
引用变量:${}语法不能省略括号
所有变量引用必须包裹在${}中,例如${PROJECT_VERSION}、${SOURCES}。
写成$PROJECT_VERSION或${PROJECT_VERSION都是语法错误,CMake会静默忽略或报错“Parse error”。
嵌套引用合法但易读性差:set(BASE_NAME "app") → set(TARGET_NAME "${BASE_NAME}_core") → add_executable(${TARGET_NAME} ${SOURCES})。
区分普通变量和缓存变量
方法一:普通变量只在当前CMakeLists.txt或函数内有效,配置完成后就消失,适合临时计算值:
set(TMP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src")
CMake 4.3.2 Windows x86_64 历史版本安装包,适合旧项目兼容、构建环境回退、CMakeLists.txt 迁移验证、Visual Studio/Ninja/Makefile 生成器测试和 C/C++ 项目维护。
方法二:缓存变量带CACHE关键字,写入CMakeCache.txt,用户可在cmake-gui里修改,适合可配置选项:
set(ENABLE_TESTS ON CACHE BOOL "Enable unit tests")
方法三:环境变量用ENV{}语法,用于读取系统PATH、HOME等,但不能跨平台可靠写入:
set(ENV{MY_ENV_VAR} "custom_value")
注意:缓存变量一旦写入CMakeCache.txt,下次cmake命令不加-U或手动删缓存,它的值就不会被set()重新覆盖——除非加上FORCE参数。
作用域实操验证路径
第一步:在根目录CMakeLists.txt中定义set(ROOT_VAR "from_root"),并调用add_subdirectory(src)。
第二步:进入src/CMakeLists.txt,直接写message("sub: ${ROOT_VAR}")——能打印出"from_root",说明子目录继承了父目录普通变量副本。
第三步:在src/CMakeLists.txt中执行set(ROOT_VAR "changed_in_sub" PARENT_SCOPE),再回到根目录下message("root after sub: ${ROOT_VAR}")——此时输出变为"changed_in_sub"。
第四步:删掉PARENT_SCOPE再试一次,根目录的ROOT_VAR保持不变。这证明普通变量默认不向上回传。

















