当前位置: 首页 > news >正文

【CMake】环境变量

目录

一.环境变量简单介绍

1.1.示例1——设置与清除

1.2.示例2——全局唯一性和全局可见性

1.3.示例3

1.4.示例4

1.5.示例5


一.环境变量简单介绍

什么是环境变量?

我们看看官网是怎么说环境变量的:cmake-language(7) — CMake 4.1.1 Documentation

环境变量是操作系统提供给所有运行中程序(进程)的一套全局键值对。

CMake 作为一个程序,在启动时会继承它所在 shell(如命令行终端)的所有环境变量。

CMake 自己也提供了一套机制来操作这些变量。

它与 CMake 普通变量的核心区别如下:

1. 作用域 (Scope)

  • 全局性环境变量在同一个 CMake 进程内部的作用域是全局的。这意味着一旦你设置或修改了一个环境变量,你可以在同一个 CMake 运行过程中的任何地方(无论是在根目录的 CMakeLists.txt,还是在深层子目录的 CMakeLists.txt,或是在任何 .cmake 脚本文件中)访问到它。

  • 不被缓存环境变量不会被存入 CMake 的缓存(即项目根目录下的 CMakeCache.txt 文件)。缓存变量可以通过 set(... CACHE ...) 命令被缓存,从而在多次运行 cmake 时保持值不变。环境变量没有这个特性,每次重新运行 cmake 命令时,环境变量都会重新从外部系统环境中初始化。

2. 引用方式 (References)

  • 你不能像引用普通变量(${MY_VARIABLE})那样直接引用环境变量。

  • 你必须使用特殊的 $ENV{<变量名>} 语法。这里的 ENV 是一个操作符,它告诉 CMake 你要访问的是环境变量,而不是普通变量。

  • 示例: 如果你想获取系统环境变量 PATH 的值,你需要这样写:$ENV{PATH}

3. 初始化和修改 (Initialization)

  • 初始值: 当你在终端输入 cmake ... 命令时,CMake 进程启动那一刻,它所拥有的环境变量集合就是你的终端 shell 当时的环境变量集合的副本。

  • 修改的局限性: 这是最关键的一点。

    • 你可以使用 set(ENV{<变量名>} <值>) 命令来设置或修改一个环境变量。

    • 你也可以使用 unset(ENV{<变量名>}) 命令来删除一个环境变量。

    • 但是,这些修改仅仅发生在当前这个正在运行的 CMake 进程内部! 它会产生以下重要影响:

      • 不影响系统: 它绝对不会改变你操作系统的环境变量,也不会改变你终端 shell 的环境变量。

      • 不回传: 当 CMake 进程结束时,它对环境变量所做的任何修改都会随之消失,不会写回给调用它的父进程(也就是你的终端 shell)。

      • 不影响后续进程: 在 CMake 执行过程中,它可能会启动其他的子进程(例如调用 execute_process 命令,或者编译完成后进行构建和测试)。默认情况下,这些子进程启动时会继承最初从父 shell 那里获得的环境变量副本。但是启动子进程之后,如果父进程又修改了环境变量,那么子进程就看不到修改后的环境变量值(除非使用特定命令,见下一条)。

4. 相关工具 (Tools)

  • cmake -E env: 正因为直接在 CMake 脚本中修改环境变量对后续构建/测试进程无效,CMake 提供了一个命令行工具来解决这个问题。你可以在调用 CMake 时,使用 cmake -E env VAR1=value1 VAR2=value2 <command> ... 来为一个单独的命令临时创建一个修改后的环境,然后在这个新环境中运行该命令。这常用于为 build 或 test 阶段设置特定的环境。

  • cmake -E environment: 这个命令可以列出当前 CMake 进程所能看到的所有环境变量及其值。它是一个非常有用的调试工具,可以帮你确认环境变量的值是否如你所预期。

使用环境变量

我们可以去官网看看是怎么说的:set — CMake 4.1.1 Documentation

set(ENV{<变量名>} [<值>])

该命令用于将环境变量设置为指定值。此后在当前进程中通过 $ENV{<变量名>} 获取该环境变量时,将返回新设置的值。

需要注意的是,此命令仅对当前 CMake 进程有效:

  • 不会影响调用 CMake 的父进程的环境;

  • 不会改变整个系统的环境变量设置;

  • 也不会影响后续构建或测试进程的环境。

如果 ENV{<变量名>} 后未提供参数,或 <值> 为空字符串,则该命令将清除该环境变量当前已存在的值。

若在 <值> 后提供了额外参数,这些参数将被忽略。同时,如果检测到有多余参数存在,CMake 会发出作者警告(author warning)。

1.1.示例1——设置与清除

📂 项目目录结构

env_demo/
└── CMakeLists.txt

🔹 env_demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(EnvVarDemo)# 1. 设置环境变量
set(ENV{MY_ENV_VAR} "hello world")
message("设置后: $ENV{MY_ENV_VAR}")# 2. 清除方式一:赋空字符串
set(ENV{MY_ENV_VAR} "")
message("清除方式一后: '$ENV{MY_ENV_VAR}'")# 3. 再次设置
set(ENV{MY_ENV_VAR} "hello again")
message("再次设置后: $ENV{MY_ENV_VAR}")# 4. 清除方式二:不给值
set(ENV{MY_ENV_VAR})
message("清除方式二后: '$ENV{MY_ENV_VAR}'")

其实我们可以通过下面这个命令来一键搭建出这个目录结构和文件

mkdir -p env_demo && \
cat > env_demo/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.15)
project(EnvVarDemo)# 1. 设置环境变量
set(ENV{MY_ENV_VAR} "hello world")
message("设置后: $ENV{MY_ENV_VAR}")# 2. 清除方式一:赋空字符串
set(ENV{MY_ENV_VAR} "")
message("清除方式一后: '$ENV{MY_ENV_VAR}'")# 3. 再次设置
set(ENV{MY_ENV_VAR} "hello again")
message("再次设置后: $ENV{MY_ENV_VAR}")# 4. 清除方式二:不给值
set(ENV{MY_ENV_VAR})
message("清除方式二后: '$ENV{MY_ENV_VAR}'")
EOF

接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

两种清理方式都是没有问题的!!

1.2.示例2——全局唯一性和全局可见性

项目结构

demo/
├── CMakeLists.txt              # 顶层
├── subdir/
│   ├── CMakeLists.txt          # 子目录
│   └── subsubdir/
│       ├── CMakeLists.txt      # 子子目录
│       └── helper.cmake        # 脚本

顶层 CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(GlobalEnvDemo)# 在顶层设置环境变量
set(ENV{MY_VAR} "HelloFromTop")
message(STATUS "顶层看到的 MY_VAR: '$ENV{MY_VAR}'")# 进入子目录
add_subdirectory(subdir)# 在所有子目录之后再打印一次,看看是否被修改过
message(STATUS "顶层最后看到的 MY_VAR: '$ENV{MY_VAR}'")

子目录 subdir/CMakeLists.txt

# 子目录也能访问
message(STATUS "子目录看到的 MY_VAR: '$ENV{MY_VAR}'")# 进入子子目录
add_subdirectory(subsubdir)

子子目录 subdir/subsubdir/CMakeLists.txt

# 子子目录访问环境变量
message(STATUS "子子目录看到的 MY_VAR: '$ENV{MY_VAR}'")# 在子子目录里修改环境变量
set(ENV{MY_VAR} "ModifiedInSubSubDir")
message(STATUS "子子目录修改后的 MY_VAR: '$ENV{MY_VAR}'")# 引入 helper 脚本
include(helper.cmake)

脚本 subdir/subsubdir/helper.cmake

# helper.cmake 也能看到修改过的值
message(STATUS "helper.cmake 看到的 MY_VAR: '$ENV{MY_VAR}'")

我们可以运行下面这个bash语句来一键搭建出这个目录结构和文件

mkdir -p demo/subdir/subsubdir && \
cat > demo/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.18)
project(GlobalEnvDemo)# 在顶层设置环境变量
set(ENV{MY_VAR} "HelloFromTop")
message(STATUS "顶层看到的 MY_VAR: '$ENV{MY_VAR}'")# 进入子目录
add_subdirectory(subdir)# 在所有子目录之后再打印一次,看看是否被修改过
message(STATUS "顶层最后看到的 MY_VAR: '$ENV{MY_VAR}'")
EOFcat > demo/subdir/CMakeLists.txt <<'EOF'
# 子目录也能访问
message(STATUS "子目录看到的 MY_VAR: '$ENV{MY_VAR}'")# 进入子子目录
add_subdirectory(subsubdir)
EOFcat > demo/subdir/subsubdir/CMakeLists.txt <<'EOF'
# 子子目录访问环境变量
message(STATUS "子子目录看到的 MY_VAR: '$ENV{MY_VAR}'")# 在子子目录里修改环境变量
set(ENV{MY_VAR} "ModifiedInSubSubDir")
message(STATUS "子子目录修改后的 MY_VAR: '$ENV{MY_VAR}'")# 引入 helper 脚本
include(helper.cmake)
EOFcat > demo/subdir/subsubdir/helper.cmake <<'EOF'
# helper.cmake 也能看到修改过的值
message(STATUS "helper.cmake 看到的 MY_VAR: '$ENV{MY_VAR}'")
EOF

现在我们就来构建我们的项目

rm -rf build && mkdir build && cd build && cmake ..


✅ 这就展示了:

  1. 环境变量在同一个 CMake 进程里是 全局可见的

  2. 子子目录修改了环境变量之后,顶层 CMakeLists.txt 后续部分也能看到修改后的值

1.3.示例3

初始值: 当你在终端输入 cmake ... 命令时,CMake 进程启动那一刻,它所拥有的环境变量集合就是你的终端 shell 当时的环境变量集合的副本。

案例1

在你的 终端 里先设置一个环境变量,例如

export MY_VAR=HelloFromShell

写一个简单的 CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(InitEnvDemo)# 打印 CMake 启动时继承的环境变量
message(STATUS "CMake 看到的 MY_VAR: '$ENV{MY_VAR}'")

运行:

mkdir build && cd build && cmake ..

运行结果

结论

CMake 启动时,确实拿到了 终端 shell 的环境变量副本

  • 如果你在终端里没设置 MY_VAR,那 CMake 里就是空。

  • 如果你在运行 cmake .. 之后再修改 shell 的 MY_VAR,CMake 进程是看不到的(因为它只在启动时复制了一份)。

我们来看看

export MY_VAR=Hello

再次启动看看

这说明 CMake 每次启动时都是从当前 shell 环境复制一份副本。


1.4.示例4

我给你写一个最小例子,演示 CMake 内部修改环境变量不会影响系统或终端 shell

打开终端,先设置一个环境变量:

export MY_VAR=OriginalValue

写一个简单的 CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(NoSystemImpactDemo)# 打印启动时 shell 的环境变量
message(STATUS "CMake 启动时看到的 MY_VAR: '$ENV{MY_VAR}'")# 修改 CMake 内部环境变量
set(ENV{MY_VAR} "ModifiedInCMake")
message(STATUS "CMake 内部修改后的 MY_VAR: '$ENV{MY_VAR}'")

运行 CMake:

rm -rf build && mkdir build && cd build && cmake ..

CMake 内部修改了环境变量set(ENV{MY_VAR} ...)),只对 CMake 进程和它启动的子进程有效

系统环境或父 shell 完全不受影响,CMake 退出后修改的值消失。

1.5.示例5

我们都说子进程会继承父进程的环境变量,我们来看看是不是这么一回事。

📂 项目目录结构

env_demo/
└── CMakeLists.txt

🔹 env_demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(MultiChildEnvDemo)# Step 1: CMake 启动时的环境变量
message(STATUS "CMake 启动时看到的 MY_VAR: '$ENV{MY_VAR}'")# Step 2: 修改环境变量
set(ENV{MY_VAR} "FirstValue")
message(STATUS "CMake 修改后的 MY_VAR: '$ENV{MY_VAR}'")# Step 3: 启动第一个子进程
execute_process(COMMAND printenv MY_VAROUTPUT_VARIABLE OUT1OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "第一个子进程看到的 MY_VAR: '${OUT1}'")# Step 4: 再修改环境变量
set(ENV{MY_VAR} "SecondValue")
message(STATUS "CMake 再次修改 MY_VAR: '$ENV{MY_VAR}'")# Step 5: 启动第二个子进程
execute_process(COMMAND printenv MY_VAROUTPUT_VARIABLE OUT2OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "第二个子进程看到的 MY_VAR: '${OUT2}'")

 运行 CMake:

rm -rf build && mkdir build && cd build && cmake ..

  • 第一次修改"FirstValue"

    • 第一个子进程 继承了 "FirstValue"

  • 第二次修改"SecondValue"

    • 第二个子进程 继承了 "SecondValue"


文章转载自:

http://oBwnDwzp.dwgcx.cn
http://VQfAYzDJ.dwgcx.cn
http://P2TPqxpO.dwgcx.cn
http://bQloNs7y.dwgcx.cn
http://B7Y9608i.dwgcx.cn
http://D0bmMTah.dwgcx.cn
http://PIvcQoVl.dwgcx.cn
http://3fSweL2r.dwgcx.cn
http://pIFdgR8Y.dwgcx.cn
http://RlJhBydD.dwgcx.cn
http://huQegsKm.dwgcx.cn
http://7vK7v8Q1.dwgcx.cn
http://y7lfNavi.dwgcx.cn
http://l0aytEkj.dwgcx.cn
http://Eqz3G60g.dwgcx.cn
http://0p2gJA1u.dwgcx.cn
http://QCD09Sbo.dwgcx.cn
http://E9ozyuyE.dwgcx.cn
http://iw85Fe0h.dwgcx.cn
http://bqtlnN5g.dwgcx.cn
http://8FjvXpPj.dwgcx.cn
http://3fnOSUFU.dwgcx.cn
http://ciRFKanv.dwgcx.cn
http://F7AQtgpn.dwgcx.cn
http://UtJqSw4c.dwgcx.cn
http://KBAf5fH6.dwgcx.cn
http://CRM2KlXS.dwgcx.cn
http://gYt7HlQy.dwgcx.cn
http://GeGkV0Wj.dwgcx.cn
http://0oZH8yKb.dwgcx.cn
http://www.dtcms.com/a/382410.html

相关文章:

  • 贪心算法应用:广告投放优化问题详解
  • VSCode AI编程插件
  • 题解:P4711 「化学」相对分子质量
  • QGIS构建问题
  • 【飞书多维表格插件】
  • 云原生与多云策略:构建弹性、开放的数据底座
  • Java接口入门:从零掌握行为规范
  • Java基础常见知识点
  • Linux epoll 事件模型终极指南:深入解析 epoll_event 与事件类型
  • 简单学习HTML+CSS+JavaScript
  • 4 Python开发环境准备
  • 人源化抗体:从临床应用到未来趋势,3 大领域突破 + 4 大发展方向全解析
  • Scrapy框架入门:快速掌握爬虫精髓
  • 2.1线性表
  • Java 21 虚拟线程高并发落地:中间件适配、场景匹配与细节优化的技术实践
  • 炒股进阶理论知识
  • 07_Softmax回归、损失函数、分类
  • 复杂系统迭代中多变量测试的实施经验
  • 智能体综述:从 Agentic AI 到 AI Agent
  • MICAPS:气象信息综合分析与处理系统概述
  • Python中实现数据库事务回滚的方法
  • CodeAct范式
  • 有监督机器学习算法案例(Python)
  • MaxStateSuper 已经成功实现了输入与状态的统一
  • 技术面:Spring (bean的生命周期、创建方式、注入方式、作用域)
  • HUST-STAR电控组视觉任务
  • Redis 高并发方案适用的场景
  • 【开题答辩全过程】以 E家洁管理系统为例,包含答辩的问题和答案
  • 李宏毅 Deep Learning
  • 公众号网页授权报错:redirect_uri域名与后台配置不一致,错误代码10003