目錄
一.CMake策略簡要理解
1.1.第一階段:童年時期(舊行為,The "Old Way")
1.2.第二階段:成長與改進(引入新行為,The "New Way")
1.3.第三階段:與不同項目打交道(項目應對,Project Management)
二.CMake的策略編號——CMPXXXX 編號
三.如何高效地查閱和理解一個策略?
四.設置/查詢策略——cmake_policy
4.1.命令用法介紹
4.?cmake_policy(PUSH)?和?cmake_policy(POP)
4.2.使用實例1——V_proj < V_policy
4.3.使用示例2——V_proj >=?V_policy
4.4.使用示例3——cmake_policy(VERSION )
4.5.使用示例4——?cmake_policy(PUSH) 和 cmake_policy(POP)
一.CMake策略簡要理解
想象一下 CMake 不是一個軟件工具,而是一個在不斷成長和學習的語言翻譯官。他的工作是把?CMakeLists.txt
?這份“項目說明書”(用一種特殊的方言寫成)翻譯成?Makefile
?或?Visual Studio
?項目文件等“施工圖紙”。
1.1.第一階段:童年時期(舊行為,The "Old Way")
在?CMake 3.0?這個“童年時期”,我們的小翻譯官在翻譯?command_a
?這個句子時,掌握得不是很好。他的理解有點死板和模糊(缺陷或歧義)。
-
具體情景:比如,
command_a
?的本意是“把蘋果和梨放進籃子”。但小 CMake 3.0 的理解是:“如果籃子里有東西,就把蘋果和梨放在它旁邊;如果籃子空著,就放進去”。這個規則很復雜,容易出錯,而且不同情況下結果可能不一致。 -
項目們的適應:很多早期的項目(“老雇主”)已經摸透了小翻譯官的這個古怪脾氣。他們的說明書里會特意寫上:“先確保籃子是空的,然后再下?
command_a
?指令”。這樣,雖然翻譯官的規則有缺陷,但項目最終也能得到正確的“施工圖紙”。
此時的狀態:世界運轉正常,老雇主們和年輕的翻譯官配合默契,盡管方式有點別扭。
1.2.第二階段:成長與改進(引入新行為,The "New Way")
時間跳到?CMake 3.20?時代。我們的小翻譯官已經長大,經驗豐富,他發現年輕時對?command_a
?的理解太復雜且容易出錯,決定進行一次改進。
-
做出決定:他意識到,
command_a
?最合理、最不容易出錯的理解應該是:“無論籃子是否為空,都清空籃子,然后把蘋果和梨放進去”。這個新規則簡單、清晰、一致。 -
關鍵的兼容性決策:但是,成熟的 CMake 翻譯官面臨一個難題:
“如果我直接改用新的理解方式,所有那些已經習慣了舊規則的老雇主的說明書,不就會被我‘譯錯’了嗎?他們的項目就會構建失敗!”
-
誕生了“策略”機制:為了解決這個難題,智慧的 CMake 發明了一個絕妙的辦法——“策略開關”。他給這個關于?
command_a
?的改進方案起了一個唯一的編號:CMP0999
。-
OLD
?開關:當這個開關打開時,他繼續使用 3.0 時代那個有缺陷的舊規則進行翻譯。 -
NEW
?開關:當這個開關打開時,他就使用新的、更合理的規則進行翻譯。 -
這個?
CMP0999
?策略隨著?CMake 3.20?版本一同發布。
-
此時的狀態:翻譯官變得更聰明了,但他身上多了一個名為?CMP0999
?的新開關,默認狀態是“向下兼容”。
1.3.第三階段:與不同項目打交道(項目應對,Project Management)
現在,成年的 CMake 3.20 翻譯官開始為不同的項目工作。
當 CMake 解釋器(無論其自身版本是 3.5, 3.20 還是更高)開始處理一個項目時,它會遵循以下步驟來設置每一個策略?CMPXXXX
?的默認狀態:
步驟一:確定基準版本
決策過程始于獲取兩個至關重要的版本號:
-
解析項目要求的最低版本 (
V_proj
):解釋器首先會在項目的 CMakeLists.txt 文件中尋找cmake_minimum_required(VERSION <min-version>)命令。該命令中指定的<min-version>(例如2.8.12或3.20)被定義為V_proj。這個版本號代表了項目開發者向 CMake 聲明的**最低兼容性要求**,其隱含的語義是:“我的項目代碼是按照 CMakeV_proj?版本的行為規范來編寫的,并承諾能在該版本及更高版本上正常工作。” -
查詢策略的引入版本 (
V_policy
):
每一個具體的策略?CMPXXXX
?在 CMake 的源代碼和歷史中都有一個明確的屬性,記錄了它是從哪個 CMake 版本開始被引入的。這個版本號被定義為?V_policy
。例如,策略?CMP0048
(與?project()
?命令相關)是在 3.0.0 版本引入的;策略?CMP0077
(與?option()
?命令相關)是在 3.13.0 版本引入的。
步驟二:基于版本比較的自動決策
對于每一個獨立的策略?CMPXXXX
,解釋器會將上述兩個版本號進行比較,并應用一條核心規則:
-
情況一:如果?
V_proj
?<?V_policy
-
決策結果:將該策略?
CMPXXXX
?的初始狀態設置為?OLD或者不設置
。 -
決策邏輯1——設置為OLD:項目所要求的最低 CMake 版本 (
V_proj
) 早于該策略被引入的版本 (V_policy
)。這意味著該項目是在完全不知道此策略存在的時代編寫的。如果貿然啟用新行為 (NEW
),極有可能破壞項目的構建過程,因為項目的代碼期望的是舊的行為。因此,CMake 為了無條件保證向后兼容性,會自動選擇?OLD
?行為,模擬一個“過去的時代”,確保老項目能繼續構建。 -
決策邏輯2——不設置:事實上呢,很多版本的CMake都會在?
V_proj
?<?V_policy?
時不去設置策略CMPXXXX的初始狀態,我們查詢CMPXXXX的初始狀態也是空,其實這個時候CMake底層還是采取了OLD行為的. -
也就是說,如果?
V_proj
?<?V_policy,
策略?CMPXXXX
?的初始行為是設置為OLD
-
-
情況二:如果?
V_proj
?>=?V_policy
-
決策結果:將該策略?
CMPXXXX
?的初始狀態設置為?NEW
。 -
決策邏輯:項目要求的最低 CMake 版本 (
V_proj
) 等于或晚于該策略被引入的版本 (V_policy
)。這意味著項目開發者聲明他們的項目兼容于一個已經知曉該策略存在的 CMake 版本。CMake 據此認為,項目開發者有責任了解并適應自?V_proj
?以來所有的新行為。因此,默認啟用?NEW
?行為是合理的,這推動了項目向更現代、更一致的行為遷移,也是 CMake 團隊所鼓勵的做法。
-
這個基于版本的自動決策是 CMake 策略機制最核心、最自動化的部分。它確保了用?cmake_minimum_required(VERSION 2.8.12)
?編寫的項目,在完全不做任何修改的情況下,依然能在 CMake 3.28 上成功編譯,因為所有在 2.8.12 之后引入的策略都會被自動設置為?OLD
?狀態。
步驟三:處理用戶的顯式指令(最高優先級覆蓋)
在上述自動決策過程之后,CMake 解釋器會繼續解析?CMakeLists.txt
?文件中的內容。
-
覆蓋檢查:如果在代碼中遇到了一個顯式的策略設置命令:
cmake_policy(SET CMPXXXX NEW)? # 或 OLD
-
最終決策:這條顯式指令擁有絕對的、最高的優先級。它會立即覆蓋第二階段中通過版本比較得出的任何默認狀態。
這個步驟賦予了項目開發者最終的控制權。它允許開發者打破基于版本的自動決策,適用于以下場景:
-
前瞻性啟用:即使項目聲明了一個較低的?
V_proj
,但如果開發者確認其代碼與某個新策略的?NEW
?行為兼容,可以主動啟用它來獲得好處(如更清晰的警告或功能)。 -
保守性回退:即使項目聲明了一個較高的?
V_proj
,但如果發現某個新策略的?NEW
?行為確實導致了問題,可以強制回退到?OLD
?行為作為臨時解決方案。
二.CMake的策略編號——CMPXXXX 編號
CMPXXXX
?是一個策略(Policy)的唯一標識符。策略編號的格式是固定的:CMP
?前綴加上一個?四位數字,例如?CMP0000
,?CMP0123
,?CMP1000
。
-
CMP
: 是?CMake?Policy 的縮寫。 -
XXXX
: 是一個四位數字編號(例如?0001
,?0084
,?1000
?等),每個編號對應一個非常具體、明確的 CMake 行為變化。這個四位數字的編號并非隨意分配,它隱含了策略被引入的時間順序和一定的功能范疇。
編號的規律和解讀
1. 順序增長
最核心的規律是:策略編號大致上是按引入時間的先后順序遞增的。
-
CMP0000
:這是一個特殊的策略,在 CMake 2.6.0 中引入,用于控制?LINK_INTERFACE_LIBRARIES
?的屬性是否默認被初始化。它是編號的起點。 -
CMP00XX
(如?CMP0048
):這些通常是 CMake 3.0 時代或更早引入的策略。數字較小,意味著它們處理的是比較古老的行為變更。 -
CMP01XX
?-?CMP02XX
:這些策略大多在 CMake 3.x 時代的中期被引入(例如 3.5 - 3.15 左右)。 -
CMP03XX
?-?CMP04XX
:這些是相對較新的策略,在近期的 CMake 3.x 版本中引入。 -
CMP1000+
:當編號突破?CMP0999
?后,會進入四位數領域(如?CMP1000
,?CMP1001
)。這通常意味著 CMake 的發展進入了一個新的階段,或者有大量的新策略被引入。例如,CMP1000
?是關于?cmake -E tar
?命令的行為。
如何驗證?
你可以使用命令?cmake --help-policy-list
,輸出的策略列表默認就是按照編號排序的。從上到下瀏覽,你就像在翻閱一部 CMake 的演進史。
2. 數字本身沒有特定含義,但可能有粗略的“系列”感
雖然編號主要是順序的,但有時在特定版本附近引入的一批策略可能會處理相關的主題。
-
例如,在 CMake 3.12 附近,有一批策略(如?
CMP0070
?到?CMP0080
?左右)大量處理的是與生成器表達式(Generator Expressions)、try_compile
?行為、以及現代 CMake 目標特性相關的問題。 -
但這只是一個粗略的趨勢,并非嚴格的規則。不能絕對地說?
CMP07XX
?就一定是關于某個特定功能的。
3. 前導零(Leading Zeros)
編號是四位定長的。所以?CMP0048
?是第 48 個策略,而不是第 480 個。CMP1000
?則是第 1000 個策略。前導零只是為了格式整齊。
三.如何高效地查閱和理解一個策略?
當你遇到一個陌生的策略編號(比如來自一個編譯警告),以下是最高效的處理流程:
-
使用命令行查詢(最快最直接)
在終端中直接運行:cmake --help-policy CMPXXXX
這會立刻顯示出我們之前討論過的完整文檔,包括它的引入版本、新舊行為差異和詳細解釋。這是診斷和解決問題的最權威來源。
-
查閱官方在線手冊
所有策略都按編號順序列在官方的 CMake 策略手冊中。大家可以去官網進行查詢:cmake-policies(7) — CMake 4.1.1 Documentation -
理解引入版本是關鍵
策略文檔中一定會明確指出該策略是從哪個 CMake 版本開始引入的(e.g.,?Introduced in CMake version 3.0.
)。-
這極其重要:它直接告訴你這個行為變化發生在哪個歷史節點。
-
如果你的?
cmake_minimum_required(VERSION X.Y)
?中的?X.Y
?早于策略的引入版本,那么 CMake 會為了兼容性而默認將該策略設置為?OLD
。 -
如果你的?
cmake_minimum_required(VERSION X.Y)
?中的?X.Y
?等于或晚于策略的引入版本,CMake 通常會默認將其設置為?NEW
。
-
舉例說明
假設你遇到了?CMP0082
?這個策略的警告。
-
查詢:
cmake --help-policy CMP0082
-
閱讀摘要:你會發現它是關于?
Installation Step
?中?GoogleTest
(gtest)的命令是否可用的問題。 -
查看引入版本:
Introduced in CMake version 3.14.
。這意味著這個行為變化發生在 CMake 3.14。 -
分析你的項目:
-
如果你的項目聲明?
cmake_minimum_required(VERSION 3.10)
,因為 3.10 < 3.14,CMake 會使用?OLD
?行為來保證兼容,但會給你警告。 -
如果你的項目聲明?
cmake_minimum_required(VERSION 3.14)
?或更高,CMake 會默認使用?NEW
?行為。
-
-
做出決策:
-
如果項目不需要兼容舊版 gtest:你可以安全地?
cmake_policy(SET CMP0082 NEW)
?來消除警告并采用新行為。 -
如果項目嚴重依賴舊行為:你可以顯式地?
cmake_policy(SET CMP0082 OLD)
?來沉默警告并維持舊行為(但需知這是臨時措施,因為舊行為終將被廢棄)。
-
我們來查詢一個
什么意思呢?我們來仔細看看
CMP0048 核心摘要
這個策略控制著?project()
?命令如何管理與版本相關的變量(如?PROJECT_VERSION
)。它引入了新舊行為之間的一個關鍵變化,主要影響那些在調用?project()
?之前或之后自行設置了版本變量的項目。
詳細解釋
1. 背景與問題所在
CMake version 3.0 introduced the ``VERSION`` option of the ``project()``
command to specify a project version as well as the name.
-
意思是:在 CMake 3.0 之前,
project()
?命令只能設置項目名。從 3.0 版本開始,project()
?命令增加了一個?VERSION
?選項,允許你同時指定項目的版本號,例如:project(MyAwesomeApp VERSION 1.2.3)
。
In order to keep
``PROJECT_VERSION`` and related variables consistent with variable
``PROJECT_NAME`` it is necessary to set the ``VERSION`` variables
to the empty string when no ``VERSION`` is given to ``project()``.
-
意思是:為了保持行為的一致性,如果一個?
project()
?命令沒有提供?VERSION
?參數,CMake 認為你需要將版本變量設置為空字符串。這樣,PROJECT_NAME
(總有值)和?PROJECT_VERSION
(可能為空)的狀態就是明確和可預測的。
However, this can change behavior for existing projects that set ``VERSION``
variables themselves since ``project()`` may now clear them.
-
這才是關鍵矛盾:這個新的“清空”行為會破壞那些為舊版 CMake(3.0 之前)編寫的項目。想象一下這個場景:
# 一個 CMake 2.8 的項目 set(PROJECT_VERSION "2.0") # 開發者自己手動設置版本變量 project(MyOldProject)????? # 舊版 project() 不關心 VERSION 變量,所以 PROJECT_VERSION 仍然是 "2.0"
在 CMake 3.0+ 下,如果新行為生效,
project(MyOldProject)
?會因為自己沒有?VERSION
?參數而把?PROJECT_VERSION
?清空!這顯然不是開發者想要的結果。
2. 新舊行為(The OLD and NEW)
The ``OLD`` behavior for this policy is to leave ``VERSION`` variables untouched.
-
OLD 行為(默認,為了兼容):
project()
?命令不會去主動設置或清空任何版本變量(如?PROJECT_VERSION
,?PROJECT_VERSION_MAJOR
?等)。這些變量保持它們之前的值(如果被設置過的話),或者未定義狀態。 -
這是為了確保那些在?
project()
?調用前手動設置了版本變量的老項目能繼續正常工作。
The ``NEW`` behavior for this policy is to set ``VERSION`` as documented by the
``project()`` command.
-
NEW 行為(推薦,更一致):
project()
?命令會嚴格按照其文檔來管理版本變量。-
如果調用?
project(MyProj VERSION 1.2.3)
,它會正常設置?PROJECT_VERSION=1.2.3
,?PROJECT_VERSION_MAJOR=1
?等。 -
如果調用?
project(MyProj)
(沒有?VERSION
?參數),它會將這些版本變量設置為空字符串,以確保行為明確。
-
3. 版本與狀態
This policy was introduced in CMake version 3.0.
-
引入版本:這個策略是伴隨著它所管理的那個特性(
project(VERSION)
)一起在 CMake 3.0 中出現的。
CMake version 3.28.3 warns when the policy is not set and uses ``OLD`` behavior.
-
當前行為:即使你使用的是很新的 CMake(如 3.28.3),如果你沒有明確設置這個策略,CMake 為了最大限度地保證兼容性,仍然會采用?
OLD
?行為。但同時,它會發出警告,提醒你存在這個策略未設置的潛在問題。
?Use the ``cmake_policy()`` command to set it to ``OLD`` or ``NEW`` explicitly.
-
解決方案:你應該在你的?
CMakeLists.txt
?中明確設置這個策略的狀態,以避免警告并明確指定你期望的行為。例如:cmake_policy(SET CMP0048 NEW) # 推薦:采用新的、一致的行為 # 或者 cmake_policy(SET CMP0048 OLD) # 明確要求保持舊行為(通常是為了兼容老腳本)
4. 重要警告
.. note::? The ``OLD`` behavior of a policy is? ``deprecated by definition``? and may be removed in a future version of CMake.
-
終極建議:這是一個非常重要的提示。它告訴你,
OLD
?行為從定義上就被認為是“已廢棄的”。雖然現在還能用,但未來的某個 CMake 版本可能會徹底移除?OLD
?行為,只保留?NEW
?行為。到那時,如果你還在依賴?OLD
?行為,你的項目將會構建失敗。 -
因此,CMake 官方強烈建議你盡快將項目遷移到?
NEW
?行為上。
四.設置/查詢策略——cmake_policy
4.1.命令用法介紹
cmake_policy
?命令的唯一目的,就是管理我們之前討論的策略(Policies),即那些?CMPXXXX
?開關。它允許你查詢、設置、保存和恢復這些策略的狀態。
cmake_policy
?有多個子命令,每個都有其特定的用途。我們將逐一分解。
1.?cmake_policy(SET CMP<NNNN> NEW)
作用:顯式地將單個策略設置為?NEW
?或?OLD
?行為。
-
何時使用:
-
當 CMake 產生策略警告,提示你?
CMPXXXX is not set
?時,你用它來明確指定行為,從而消除警告。 -
當你了解一個策略的新舊行為差異,并主動決定你的項目要采用哪一種時。
-
-
示例:
# 啟用 CMP0077 的新行為(option() 命令尊重普通變量) cmake_policy(SET CMP0077 NEW)# 強制保持 CMP0060 的舊行為(可能因為某些第三方代碼需要) cmake_policy(SET CMP0060 OLD)
-
重要提示:這個設置的影響范圍取決于你在哪里調用它。在頂層?
CMakeLists.txt
?中設置,會影響整個項目。在函數或宏中設置,通常只在該函數或宏的范圍內有效(除非使用其他手段)。
2.?cmake_policy(GET CMP<NNNN> <variable>)
作用:查詢指定策略的當前狀態,并將結果(OLD
、NEW
?或空字符串)存儲到一個變量中。
-
何時使用:通常用于調試或編寫非常復雜的、需要根據當前策略狀態來動態調整行為的 CMake 腳本。日常項目中較少使用。
-
示例:
#把策略 CMP0048 當前的狀態(OLD / NEW / 或未設置)存放到變量 current_cmp0048 里。 cmake_policy(GET CMP0048 current_cmp0048) message(STATUS "The state of CMP0048 is: ${current_cmp0048}")
3.?cmake_policy(VERSION <version>)
作用:請求 CMake 將策略狀態批量設置為指定版本所定義的行為。
-
它與?
cmake_minimum_required(VERSION ...)
?的關系:-
cmake_minimum_required(VERSION X.Y)
:聲明兼容性。“我的項目最低需要 CMake X.Y,請保證在這個版本上的行為。” -
cmake_policy(VERSION X.Y)
:請求行為。“請將我的項目策略設置為盡可能接近 CMake X.Y 的行為。”
-
-
關鍵限制:你通過?
cmake_policy(VERSION)
?指定的版本號?不能低于?cmake_minimum_required
?指定的版本號。它只能用來啟用更新的行為,而不能回溯更舊的行為。 -
何時使用:當你希望項目利用新版本 CMake 的特性,但又不想逐個去設置幾十個策略時。這是一種“批量現代化”的操作。
注意:CMake 里的 3.15 和 3.5 不是小數(decimal),而是 版本號 (version number)。
版本號比較規則是 逐段比較,也就是:
3.5
實際上等于3.5.0
3.15
實際上等于3.15.0
比較時先看 主版本號(major),再看 次版本號(minor),再看 補丁號(patch):
3.5.0 vs 3.15.0↑ ↑ major 相同 = 3 minor 比較 5 < 15 → 所以 3.5.0 < 3.15.0
所以 3.15 > 3.5。
就好比 Python 3.10 其實比 Python 3.9 新,但如果你把它當小數看就會誤以為 3.10 < 3.9,這是一個經典的坑。
典型模式:
cmake_minimum_required(VERSION 3.5) # 保證能在 3.5 上構建
cmake_policy(VERSION 3.15)???????? # 但啟用 3.5 到 3.15 之間所有策略的 NEW 行為
這意味著你的代碼本身是兼容 3.5 的寫法,但構建環境如果提供了 3.15 的 CMake,就能享受到 3.15 的新行為和改進。
CMake 解釋器會這樣執行:
-
建立基準 (
V_proj
):第一行命令將?V_proj
?永久設置為 3.5。這意味著:-
所有在 CMake 3.5?之后引入的策略(即?
V_policy > 3.5
),其默認狀態將被設置為?OLD
(為了兼容性)。 -
所有在 CMake 3.5?及之前引入的策略,其默認狀態將被設置為?
NEW
。
-
-
執行批量覆蓋:第二行命令?
cmake_policy(VERSION 3.15)
?開始工作。它對每一個策略進行如下判斷:-
如果?該策略的?
V_policy
?滿足:3.5< V_policy ≤ 3.15,那么?將這個策略的狀態從默認的?OLD
?覆蓋為?NEW
。因為當執行cmake_policy(VERSION 3.15)
之后,CMake 會認為“既然用戶要求模擬 3.15 版本的行為”,那么所有 在 3.15 及以前引入的策略(也就是這一區間的策略)就應該啟用新行為,于是 CMake 會把它們的狀態 從 OLD 覆蓋為 NEW。 -
如果?該策略的?
V_policy
?> 3.15,那么這條命令不處理它,它保持其默認狀態(在這種情況下是?OLD
,因為?V_proj=3.5 < V_policy
)。 -
如果?該策略的?
V_policy
?≤ 3.5,它已經被?V_proj
?默認設置為?NEW
?了,這條命令不會改變它(保持?NEW
)。
-
所以,它的效果是:?將策略狀態從“模仿 CMake 3.5 的行為”提升為“模仿 CMake 3.15 的行為”,但僅限于那些在 3.5 到 3.15 之間引入的策略。
策略引入版本 V_policy | 默認狀態 (由 V_proj=3.5 決定) | cmake_policy(VERSION 3.15) 之后的狀態 | 說明 |
---|---|---|---|
V_policy ≤ 3.5 | NEW | NEW | 項目聲明的版本已經覆蓋這些策略 → 默認就是 NEW,不受 cmake_policy(VERSION 3.15) 影響 |
3.5 < V_policy ≤ 3.15 | OLD | NEW | 默認是 OLD(兼容舊項目),但 cmake_policy(VERSION 3.15) 會把它們提升為 NEW,這是該指令的核心作用對象 |
V_policy > 3.15 | OLD | OLD | 默認是 OLD(項目聲明版本太舊),而 cmake_policy(VERSION 3.15) 無法提前啟用未來的策略,所以保持 OLD |
4.?cmake_policy(PUSH)
?和?cmake_policy(POP)
作用:這對命令用于管理一個策略棧,是實現策略作用域化的核心工具。
-
PUSH
:將當前所有策略的狀態壓入棧中保存起來。 -
POP
:將棧頂的策略狀態彈出,并恢復所有策略到那個狀態。 -
何時使用:這是極其重要的最佳實踐,用于封裝策略修改,避免產生“副作用”。
-
在你寫的宏(macro)?或函數(function)?內部。
-
當你?
include()
?一個可能修改策略的外部?.cmake
?腳本文件時。 -
在任何你只想臨時改變策略,之后又想恢復原狀的代碼塊中。
-
-
示例:
function(my_complex_function)cmake_policy(PUSH)? # 1. 進入函數,先保存當前的策略狀態# 2. 在函數內部臨時更改一些策略cmake_policy(SET CMP9999 NEW)cmake_policy(SET CMP0001 NEW)# ... 執行一些需要特定策略狀態的復雜操作 ...cmake_policy(POP)?? # 3. 離開函數前,恢復調用函數前的策略狀態 endfunction()
為什么這樣做??這確保了當你調用?
my_complex_function()
?時,無論它內部如何折騰策略,都不會影響到調用它之前的那個環境的策略設置。這就是良好的封裝。這與?
block(SCOPE_FOR POLICIES)
?的關系:block(SCOPE_FOR POLICIES)
?在背后自動為你執行了?PUSH
?和?POP
?操作,是一種更簡潔的語法糖。
4.2.使用實例1——V_proj < V_policy
首先,我們需要知道,當前我的cmake版本號是
接下來我們示例測試的策略編號是CMP0048,這個策略被引入時是cmake 3.0.
那么現在我們就看看例子
項目結構
test/
└── CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)# 在設置之前,先獲取策略 CMP0048 的狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("初始 CMP0048 狀態: ${current_cmp0048}")# 顯式設置策略為 NEW
cmake_policy(SET CMP0048 NEW)# 再次獲取策略狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("設置 NEW 之后 CMP0048 狀態: ${current_cmp0048}")# 下面演示 project() 和 PROJECT_VERSION 的行為
project(MyProject) # 沒有 VERSION 參數,NEW 行為會清空 PROJECT_VERSIONmessage("調用 project() 之后 PROJECT_VERSION: '${PROJECT_VERSION}'")# 手動設置版本
set(PROJECT_VERSION "2.0")
message("手動設置 PROJECT_VERSION 之后: ${PROJECT_VERSION}")
其實我們可以執行下面這個來一鍵搭建出這個目錄結構
mkdir -p test && cat > test/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 2.8)# 在設置之前,先獲取策略 CMP0048 的狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("初始 CMP0048 狀態: ${current_cmp0048}")# 顯式設置策略為 NEW
cmake_policy(SET CMP0048 NEW)# 再次獲取策略狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("設置 NEW 之后 CMP0048 狀態: ${current_cmp0048}")# 下面演示 project() 和 PROJECT_VERSION 的行為
project(MyProject) # 沒有 VERSION 參數,NEW 行為會清空 PROJECT_VERSIONmessage("調用 project() 之后 PROJECT_VERSION: '${PROJECT_VERSION}'")# 手動設置版本
set(PROJECT_VERSION "2.0")
message("手動設置 PROJECT_VERSION 之后: ${PROJECT_VERSION}")
EOF
接下來我們就來構建一下項目
mkdir build && cd build && cmake ..
首先我們看
CMP0048
?的?NEW
?行為規定:如果?project()
?命令沒有?VERSION
?參數,則?PROJECT_VERSION
?等變量應為空。您的?project(MyProject)
?沒有指定版本,所以之后?PROJECT_VERSION
?是空字符串?''
,這符合?NEW
?行為的預期,也驗證了設置生效了。
接下來,
注意:按照我們的推理,過程應該是下面這樣子的
- 從cmake_minimum_required(VERSION 2.8)得知V_proj =2.8
- 從cmp0048引入時的版本號是3.0,得知V_policy=3.0
- V_proj < V_policy,所以默認采取OLD或者不指定
但是我們仔細看一下
在 CMake 3.28 的機器里:
-
CMP0048
策略是 CMake 3.0 引入的。 -
當你寫
cmake_minimum_required(VERSION 2.8)
時,表示“我要兼容 2.8 的行為”。 -
結果:CMake 不會默認設置 CMP0048,所以
cmake_policy(GET CMP0048 current_cmp0048)
得到的是 空字符串。
也就是說:
👉 cmake_minimum_required(VERSION 2.8)
太老,CMake 根本沒法替你決定 CMP0048 用 OLD 還是 NEW,于是它保持 未定義 狀態。
嗯??未定義狀態?我們來好好捋捋
-
每個策略(比如
CMP0048
)都有兩種明確狀態:OLD
/NEW
。 -
但是當你用的 CMake 比該策略更老時(例如:
cmake_minimum_required(VERSION 2.8)
,而CMP0048
是在 3.0 才引入的),解釋器就不知道該策略該走哪邊,于是它保持 未設置。 -
未設置狀態下的規則:
-
cmake_policy(GET CMP0048 var)
→ 得到 空字符串(啥也沒有)。 -
運行時 → CMake 默認退回到 OLD 行為(保證兼容性),但這跟你顯式寫
SET CMP0048 OLD
不是一回事。
-
換句話說:
👉 “未設置” = 表面上跑 OLD 行為,但內部 CMake 還記著這個策略沒被項目明確確認過。
我們可以看個例子看看未定義狀態是不是默認采取OLD?
我們把上面的CMakeLists.txt換成下面這個,然后再重新構建一下項目看看
cmake_minimum_required(VERSION 2.8) # 早于 CMP0048 的引入版本# 嘗試獲取 CMP0048
cmake_policy(GET CMP0048 var)
message("第一次獲取 CMP0048: '${var}'")project(TestUnset)# 先設置一個版本號
set(PROJECT_VERSION "2.0")# 再次調用 project()
project(TestUnset2)# 打印結果
message("PROJECT_VERSION after second project(): '${PROJECT_VERSION}'")# 顯式設置 CMP0048 為 NEW,再調用 project()
cmake_policy(SET CMP0048 NEW)# 嘗試獲取 CMP0048
cmake_policy(GET CMP0048 var)
message("第二次獲取 CMP0048: '${var}'")project(TestUnset3)
message("PROJECT_VERSION after NEW policy: '${PROJECT_VERSION}'")
這個實驗結果完全說明了「未設置 = OLD 行為」的機制。
我們來逐行拆解:
第一次獲取 CMP0048: ''
👉 cmake_policy(GET CMP0048 var)
返回空字符串,說明 CMP0048 處于未設置狀態。
這是因為你 cmake_minimum_required(VERSION 2.8)
,而 CMP0048 是在 CMake 3.0 引入的,2.8 的項目不認識它,所以不設置。
CMake Warning (dev) ... Policy CMP0048 is not set ...
👉 CMake 明確告訴你:CMP0048 沒有設置(unset),并且提示你用 cmake_policy(SET ...)
來決定 NEW / OLD。
PROJECT_VERSION after second project(): '2.0'
👉 這就是 未設置 → OLD 行為 的效果:
-
OLD 行為 =
project()
不會清空已有的PROJECT_VERSION
。 -
所以
"2.0"
被保留了下來。
如果是 NEW 行為,此時應該被清空,結果是 ''
。
第二次獲取 CMP0048: 'NEW'
PROJECT_VERSION after NEW policy: ''
👉 你在腳本里顯式執行了
cmake_policy(SET CMP0048 NEW)
所以第二次獲取時就是 NEW,而 project()
在 NEW 行為下會清空版本號 → 輸出 ''
。
? 總結
-
未設置狀態:不能通過
cmake_policy(GET ...)
獲取(空字符串),但運行時會采用 OLD 行為,保證兼容老版本 CMake。 -
顯式設置為 NEW:行為立刻切換為新語義。
所以實驗流程和輸出完全符合 CMake 的機制。
4.3.使用示例2——V_proj >=?V_policy
V_proj >V_policy的情況
首先,我們需要知道,當前我的cmake版本號是
接下來我們示例測試的策略編號是CMP0048,這個策略被引入時是cmake 3.0.
那么現在我們就看看例子
項目結構
test/
└── CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)# 在設置之前,先獲取策略 CMP0048 的狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("初始 CMP0048 狀態: ${current_cmp0048}")# 顯式設置策略為 OLD
cmake_policy(SET CMP0048 OLD)# 再次獲取策略狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("設置 OLD 之后 CMP0048 狀態: ${current_cmp0048}")# 先手動設置 PROJECT_VERSION
set(PROJECT_VERSION "2.0")# 調用 project()(沒有 VERSION 參數,OLD 行為會保留 PROJECT_VERSION)
project(MyProject)message("調用 project() 之后 PROJECT_VERSION: '${PROJECT_VERSION}'")
其實我們可以執行下面這個來一鍵搭建出這個目錄結構
mkdir -p test && cat > test/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.5)# 在設置之前,先獲取策略 CMP0048 的狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("初始 CMP0048 狀態: ${current_cmp0048}")# 顯式設置策略為 OLD
cmake_policy(SET CMP0048 OLD)# 再次獲取策略狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("設置 OLD 之后 CMP0048 狀態: ${current_cmp0048}")# 先手動設置 PROJECT_VERSION
set(PROJECT_VERSION "2.0")# 調用 project()(沒有 VERSION 參數,OLD 行為會保留 PROJECT_VERSION)
project(MyProject)message("調用 project() 之后 PROJECT_VERSION: '${PROJECT_VERSION}'")
EOF
接下來我們就來構建一下項目
mkdir build && cd build && cmake ..
這很符合我們的預期吧!!!
V_proj =?V_policy的情況
我們接下來看看相同版本的,我們只需要將上面的CMakeLists.txt進行小修改即可
cmake_minimum_required(VERSION 3.0)# 在設置之前,先獲取策略 CMP0048 的狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("初始 CMP0048 狀態: ${current_cmp0048}")# 顯式設置策略為 OLD
cmake_policy(SET CMP0048 OLD)# 再次獲取策略狀態
cmake_policy(GET CMP0048 current_cmp0048)
message("設置 OLD 之后 CMP0048 狀態: ${current_cmp0048}")# 先手動設置 PROJECT_VERSION
set(PROJECT_VERSION "2.0")# 調用 project()(沒有 VERSION 參數,OLD 行為會保留 PROJECT_VERSION)
project(MyProject)message("調用 project() 之后 PROJECT_VERSION: '${PROJECT_VERSION}'")
還是很符合我們的預期的!!
4.4.使用示例3——cmake_policy(VERSION <version>)
項目結構
test/
└── CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(TwoPoliciesDemo)# 設置策略到 2.8
cmake_policy(VERSION 2.8)cmake_policy(GET CMP0046 state)
message("在 VERSION 2.8 下 CMP0046 狀態: '${state}'")cmake_policy(GET CMP0048 state)
message("在 VERSION 2.8 下 CMP0048 狀態: '${state}'")# -------------------------
# 切換到 3.0 的策略默認
cmake_policy(VERSION 3.0)cmake_policy(GET CMP0046 state)
message("在 VERSION 3.0 下 CMP0046 狀態: '${state}'")cmake_policy(GET CMP0048 state)
message("在 VERSION 3.0 下 CMP0048 狀態: '${state}'")
其實我們可以執行下面這個來一鍵搭建出這個目錄結構
mkdir -p test && cat > test/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 2.8)
project(TwoPoliciesDemo)# 設置策略到 2.8
cmake_policy(VERSION 2.8)cmake_policy(GET CMP0046 state)
message("在 VERSION 2.8 下 CMP0046 狀態: '${state}'")cmake_policy(GET CMP0048 state)
message("在 VERSION 2.8 下 CMP0048 狀態: '${state}'")# -------------------------
# 切換到 3.0 的策略默認
cmake_policy(VERSION 3.0)cmake_policy(GET CMP0046 state)
message("在 VERSION 3.0 下 CMP0046 狀態: '${state}'")cmake_policy(GET CMP0048 state)
message("在 VERSION 3.0 下 CMP0048 狀態: '${state}'")
EOF
接下來我們就來構建一下項目
mkdir build && cd build && cmake ..
這很符合我們的預期吧!!!
4.5.使用示例4——?cmake_policy(PUSH)
和 cmake_policy(POP)
我給你寫一個最小的 CMake 例子,演示 cmake_policy(PUSH)
和 cmake_policy(POP)
的作用。
它們的作用就是:
-
PUSH
:保存當前所有策略的狀態(相當于“入棧”)。 -
POP
:恢復到上一次PUSH
時保存的狀態(相當于“出棧”)。
這樣你就可以在某個局部范圍內改策略,出來之后恢復原來的狀態,不影響全局。
項目結構
test/
└── CMakeLists.txt
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(PolicyPushPopDemo)# 一開始獲取 CMP0048 策略的狀態
cmake_policy(GET CMP0048 state)
message("初始 CMP0048 狀態: '${state}'")# 保存當前策略狀態
cmake_policy(PUSH)# 在 PUSH 和 POP 之間,我們可以隨便改策略
cmake_policy(SET CMP0048 NEW)
cmake_policy(GET CMP0048 state)
message("在 PUSH 之后修改 CMP0048 狀態: '${state}'")# 恢復到 PUSH 時的狀態
cmake_policy(POP)# 再次獲取 CMP0048 狀態,驗證它已經恢復
cmake_policy(GET CMP0048 state)
message("POP 之后 CMP0048 狀態: '${state}'")
其實我們可以執行下面這個來一鍵搭建出這個目錄結構
mkdir -p test && cat > test/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 2.8)
project(PolicyPushPopDemo)# 一開始獲取 CMP0048 策略的狀態
cmake_policy(GET CMP0048 state)
message("初始 CMP0048 狀態: '${state}'")# 保存當前策略狀態
cmake_policy(PUSH)# 在 PUSH 和 POP 之間,我們可以隨便改策略
cmake_policy(SET CMP0048 NEW)
cmake_policy(GET CMP0048 state)
message("在 PUSH 之后修改 CMP0048 狀態: '${state}'")# 恢復到 PUSH 時的狀態
cmake_policy(POP)# 再次獲取 CMP0048 狀態,驗證它已經恢復
cmake_policy(GET CMP0048 state)
message("POP 之后 CMP0048 狀態: '${state}'")
EOF
接下來我們就來構建一下項目
mkdir build && cd build && cmake ..
這很符合我們的預期吧!!!
-
PUSH = 入棧保存策略狀態
-
POP = 出棧恢復之前的狀態
-
演示的結果正好說明:中間即使修改過策略,在
POP
之后都會恢復原狀。