【CMake】策略

目錄

一.CMake策略簡要理解

1.1.第一階段:童年時期(舊行為,The "Old Way")

1.2.第二階段:成長與改進(引入新行為,The "New Way")

1.3.第三階段:與不同項目打交道(項目應對,Project Management)

二.CMake的策略編號——CMPXXXX 編號

三.如何高效地查閱和理解一個策略?

四.設置/查詢策略——cmake_policy

4.1.命令用法介紹

1.?cmake_policy(SET CMPNEW)

2.?cmake_policy(GET CMP)

3.?cmake_policy(VERSION )

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?的默認狀態:

步驟一:確定基準版本

決策過程始于獲取兩個至關重要的版本號:

  1. 解析項目要求的最低版本 (V_proj):解釋器首先會在項目的 CMakeLists.txt 文件中尋找cmake_minimum_required(VERSION <min-version>)命令。該命令中指定的<min-version>(例如2.8.12或3.20)被定義為V_proj。這個版本號代表了項目開發者向 CMake 聲明的**最低兼容性要求**,其隱含的語義是:“我的項目代碼是按照 CMakeV_proj?版本的行為規范來編寫的,并承諾能在該版本及更高版本上正常工作。”

  2. 查詢策略的引入版本 (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 個策略。前導零只是為了格式整齊。


三.如何高效地查閱和理解一個策略?

當你遇到一個陌生的策略編號(比如來自一個編譯警告),以下是最高效的處理流程:

  1. 使用命令行查詢(最快最直接)
    在終端中直接運行:

    cmake --help-policy CMPXXXX

    這會立刻顯示出我們之前討論過的完整文檔,包括它的引入版本新舊行為差異詳細解釋。這是診斷和解決問題的最權威來源。

  2. 查閱官方在線手冊
    所有策略都按編號順序列在官方的 CMake 策略手冊中。大家可以去官網進行查詢:cmake-policies(7) — CMake 4.1.1 Documentation

  3. 理解引入版本是關鍵
    策略文檔中一定會明確指出該策略是從哪個 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?這個策略的警告。

  1. 查詢cmake --help-policy CMP0082

  2. 閱讀摘要:你會發現它是關于?Installation Step?中?GoogleTest(gtest)的命令是否可用的問題。

  3. 查看引入版本Introduced in CMake version 3.14.。這意味著這個行為變化發生在 CMake 3.14。

  4. 分析你的項目

    • 如果你的項目聲明?cmake_minimum_required(VERSION 3.10),因為 3.10 < 3.14,CMake 會使用?OLD?行為來保證兼容,但會給你警告。

    • 如果你的項目聲明?cmake_minimum_required(VERSION 3.14)?或更高,CMake 會默認使用?NEW?行為。

  5. 做出決策

    • 如果項目不需要兼容舊版 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>)

作用:查詢指定策略的當前狀態,并將結果(OLDNEW?或空字符串)存儲到一個變量中。

  • 何時使用:通常用于調試或編寫非常復雜的、需要根據當前策略狀態來動態調整行為的 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.153.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 解釋器會這樣執行:

  1. 建立基準 (V_proj)第一行命令將?V_proj?永久設置為 3.5。這意味著:

    • 所有在 CMake 3.5?之后引入的策略(即?V_policy > 3.5),其默認狀態將被設置為?OLD(為了兼容性)。

    • 所有在 CMake 3.5?及之前引入的策略,其默認狀態將被設置為?NEW

  2. 執行批量覆蓋:第二行命令?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.5NEWNEW項目聲明的版本已經覆蓋這些策略 → 默認就是 NEW,不受 cmake_policy(VERSION 3.15) 影響
3.5 < V_policy ≤ 3.15OLDNEW默認是 OLD(兼容舊項目),但 cmake_policy(VERSION 3.15) 會把它們提升為 NEW,這是該指令的核心作用對象
V_policy > 3.15OLDOLD默認是 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 之后都會恢復原狀。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921180.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921180.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921180.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LLM中的function call

1. 概念 **Function Call&#xff08;函數調用&#xff09;**是指在編程中&#xff0c;程序可以通過調用預定義的函數來執行特定的操作。在LLM中&#xff0c;函數調用的概念擴展了模型的能力&#xff0c;使其不僅能夠生成文本&#xff0c;還能與外部系統進行交互。通過函數調用…

【系統架構設計(13)】項目管理上:盈虧平衡分析與進度管理

文章目錄零、核心思想&#xff1a;經濟性與時效性的動態平衡一、盈虧平衡分析&#xff1a;項目的經濟生命線1、核心公式與決策邏輯二、進度管理&#xff1a;項目的時效生命線1. **工作分解結構&#xff08;WBS&#xff09;**2. 進度管理流程3、關鍵路徑法關鍵路徑法&#xff08…

【SuperSocket 】利用 TaskCompletionSource 在 SuperSocket 中實現跨模塊異步處理客戶端消息

利用 TaskCompletionSource 在 SuperSocket 中實現跨模塊異步處理客戶端消息 在使用 SuperSocket 構建 TCP 服務時&#xff0c;我們經常會遇到這樣的需求&#xff1a; 服務端接收到客戶端數據后&#xff0c;需要將數據交給其他模塊處理處理完成后再將結果返回給調用模塊或客戶端…

《IC驗證必看|semaphore與mailbox的核心區別》

月薪30K驗證工程師必答&#xff1a;SystemVerilog中semaphore與mailbox的核心區別&#xff0c;及必須用semaphore的場景深度解析 在驗證工程師的技能體系里&#xff0c;線程同步與資源管控是區分“基礎會用”&#xff08;20K水平&#xff09;和“精通工程化”&#xff08;30K水…

Spring線程池ThreadPoolTaskExecutor?詳解

ThreadPoolTaskExecutor?寫法Bean(name "taskExecutor") public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor();executor.setCorePoolSize(8); // 8核CPU服務器建議值executor.setMaxPoolSize(…

Unity之安裝教學

UnityHub下載 下載官網地址&#xff1a;Unity Hub下載地址 打開網址右上角&#xff0c;登錄/注冊賬號 登錄完畢后&#xff0c;點擊下載 安裝Unity Hub 雙擊傻瓜式安裝 安裝完成 啟動UnityHub 雙擊啟動 左上角設置 設置中文 左上角登錄賬號 添加免費許可證 設置-許可證-添加 安裝…

Redis 集群模式與高可用機制

最近在準備面試&#xff0c;正把平時積累的筆記、項目中遇到的問題與解決方案、對核心原理的理解&#xff0c;以及高頻業務場景的應對策略系統梳理一遍&#xff0c;既能加深記憶&#xff0c;也能讓知識體系更扎實&#xff0c;供大家參考&#xff0c;歡迎討論。在分布式環境下&a…

Flutter + Web:深度解析雙向通信的混合應用開發實踐

Flutter Web&#xff1a;深度解析雙向通信的混合應用開發實踐 前言 在當今快速發展的移動應用開發領域&#xff0c;開發者們始終在尋求一種能夠平衡開發效率、跨平臺能力和用戶體驗的完美方案。原生開發性能卓越&#xff0c;但雙平臺&#xff08;iOS/Android&#xff09;開發…

如何查看Linux系統中文件夾或文件的大小

在日常運維和開發工作中&#xff0c;了解文件夾和文件占用的磁盤空間是非常重要的。尤其是當你在服務器上部署應用&#xff08;如 Jenkins&#xff09;時&#xff0c;合理監控磁盤使用情況可以避免磁盤空間不足導致的各種問題。在 Linux 系統中&#xff0c;我們可以使用一些簡單…

豪華酒店品牌自營APP差異對比分析到產品重構

一、萬豪國際集團旗下豪華酒店品牌及統一APP 萬豪旗下奢華品牌均整合于 「萬豪旅享家(Marriott Bonvoy)」APP,會員可通過該平臺預訂、管理積分及享受跨品牌服務。以下為核心豪華品牌: 1. 經典奢華品牌 麗思卡爾頓酒店(The Ritz-Carlton) 定位:頂級奢華,以管家服務、歷…

ESLint 相關

no-unused-vars 等常見報錯提醒關閉 1. no-unused-vars 報錯示例&#xff1a; useMemo is defined but never used no-unused-vars解決方式 方法一&#xff1a;局部禁用某一行 // eslint-disable-next-line no-unused-vars const result useMemo(() > {}, []);方法二&…

1分鐘生成爆款相聲對話視頻!Coze智能體工作流詳細搭建教程,小白也能輕松上手

最近看到一個賬號&#xff0c;用AI將傳統相聲對話做成趣味短視頻&#xff0c;單條播放量輕松破百萬。這種視 頻看似復雜&#xff0c;其實用Coze智能體工作流1分鐘就能搞定&#xff0c;完全不需要剪輯基礎。工作流功能 用Coze一鍵生成爆款相聲對話視頻&#xff0c;無需剪輯直接發…

pinia狀態管理工具

pinia狀態管理工具Pinia 是 Vue.js 官方推薦的新一代狀態管理庫&#xff0c;可以看作是 Vuex 的替代品。1. 什么是 Pinia&#xff1f; Pinia 是 Vue 的專屬狀態管理庫&#xff0c;它允許你跨組件或頁面共享狀態。由 Vue.js 核心團隊維護&#xff0c;并且對 TypeScript 有著極其…

【初始web3】什么是web3

前言你是否還記得&#xff0c;曾經在社交媒體上發布精彩內容&#xff0c;平臺卻隨意封禁你的賬號&#xff1f;你是否曾疑惑&#xff0c;為什么你創造的數據價值億萬&#xff0c;而你自己卻一無所獲&#xff1f;這&#xff0c;就是Web2時代的痛。而Web3的到來&#xff0c;正試圖…

構建下一代互聯網:解碼Web3、區塊鏈、協議與云計算的協同演進

我們正站在互聯網歷史性變革的門口。從只能讀取信息的Web1&#xff0c;到可以讀寫、高度中心化的Web2&#xff0c;我們即將邁入一個價值可以直接傳遞的Web3時代。這個新時代并非由單一技術驅動&#xff0c;而是由區塊鏈、去中心化協議和云計算等一系列技術的融合與協同所構建。…

小迪安全v2023學習筆記(七十六講)—— Fuzz模糊測試口令爆破目錄爆破參數爆破Payload爆破

文章目錄前記WEB攻防——第七十六天Fuzz模糊測試篇&JS算法口令&隱藏參數&盲Payload&未知文件目錄Fuzz知識含義Fuzz的核心思想Fuzz應用場景Fuzz應用Fuzz字典項目Fuzz技術 - 用戶口令-常規&模塊&JS插件常規模塊JS插件JsEncrypterBurpCryptoFuzz技術 - 目…

在windows server 2022搭建gitlab……但是失敗了

在windows server 2022搭建gitlab……但是失敗了1. 前言2. 安裝ubuntu環境2. 安裝docker3. 映射3.1 端口映射3.2 路徑映射1. 前言 上一篇&#xff1a;在windows本地機搭建gitlab 本來按理來說沒必要另起一篇&#xff0c;但是沒想到&#xff0c;在新機器的windows server 2022…

藍橋杯算法之基礎知識(4)

目錄 Ⅰ.sorted排序 Ⅱ.排序具體的方法 &#xff08;1&#xff09;sort的神方法&#xff08;注意是sort&#xff09; &#xff08;2&#xff09;sorted的神方法&#xff08;注意這里是sorted&#xff09; 常見場景 1. 單關鍵字排序 2. 多關鍵字排序 3.按倒序字符串排序&#xf…

GOFLY開源客服系統-處理gin框架下的session中間件

了解更多&#xff0c;搜索:"程序員老狼" 在當今數字化時代&#xff0c;在線客服系統已成為企業與客戶溝通的重要橋梁。作為GOFLY客服系統的開發者&#xff0c;我今天要分享我們如何在系統中實現安全可靠的會話管理機制——這是保障用戶數據安全的核心技術。 為什么…

Burp Suite 插件 | 提供強大的框架自動化安全掃描功能。目前支持1000+POC、支持動態加載POC、指定框架掃描。

工具介紹 Rinte 是一款專為滲透測試人員設計的 Burp Suite 插件&#xff0c;提供強大的自動化安全掃描功能。該插件集成了框架檢測、漏洞掃描和敏感路徑掃描等多種功能&#xff0c;幫助安全研究人員快速識別目標系統的安全漏洞。支持1000框架POC、支持動態加載POC、指定框架掃描…