目錄
一.環境變量簡單介紹
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 ..
? 這就展示了:
-
環境變量在同一個 CMake 進程里是 全局可見的。
-
子子目錄修改了環境變量之后,頂層
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"
-