政策¶
有時候,CMake 會推出與舊版本不完全向後相容的新功能或變更。當有人嘗試使用舊的 CMakeLists 檔案搭配新版本的 CMake 時,這可能會造成問題。為了協助終端使用者和開發人員解決這些問題,我們引入了 cmake-policies
。政策是一種機制,有助於改善向後相容性,並追蹤不同 CMake 版本之間的相容性問題。
設計目標¶
CMake 政策機制有四個主要設計目標
現有專案應使用比專案作者使用的 CMake 更新的版本進行建置。
使用者不應需要編輯程式碼才能建置專案。
可能會發出警告,但專案應該可以建置。
不應因相容性要求而妨礙新介面或舊介面中的錯誤修正的正確性。降低最新介面的正確性對新專案是不公平的。
對 CMake 所做的任何可能需要變更專案的 CMakeLists 檔案的變更,都應記錄下來。
每個變更也應具有唯一的識別碼,可用於參考警告和錯誤訊息。
僅當專案以某種方式指出它受支援時,才會啟用新行為。
我們必須能夠最終移除實作與舊版 CMake 相容性的程式碼。
這種移除是必要的,以保持程式碼的整潔並允許內部重構。
在移除之後,嘗試建置為舊版編寫的專案必須失敗,並顯示提供資訊的訊息。
CMake 中的所有政策都以 CMPNNNN 的形式指派名稱,其中 NNNN 是整數值。政策通常支援兩種行為:一種舊行為,可保留與早期 CMake 版本的相容性;以及一種新行為,被認為是正確的,並且是新專案的首選。每個政策都有文件詳細說明變更的動機,以及新舊行為。
設定政策¶
專案可以設定每個政策的設定,以請求舊的或新的行為。當 CMake 遇到可能受特定政策影響的使用者程式碼時,它會檢查專案是否已設定該政策。如果已設定政策(為 OLD
或 NEW
),則 CMake 會遵循指定的行為。如果尚未設定政策,則會使用舊行為,但會發出警告,告知專案作者設定政策。
有幾種方法可以設定政策的行為。最快的方法是將所有政策設定為對應於專案編寫時所使用的 CMake 版本。設定政策版本會請求對應 CMake 版本或更早版本中引入的所有政策的新行為。在較新版本中引入的政策會標示為「未設定」,以便產生適當的警告訊息。政策版本是使用 cmake_policy
命令的 VERSION
簽章來設定。例如,程式碼
cmake_policy(VERSION 3.20)
會請求對所有在 CMake 3.20 或更早版本中引入的政策的新行為。
cmake_minimum_required
命令會要求 CMake 的最低版本,並會呼叫 cmake_policy
。專案應始終以以下行開頭
cmake_minimum_required(VERSION 3.20)
project(MyProject)
# ...code using CMake 3.20 policies
這表示執行 CMake 的人必須至少擁有 3.20 版。如果他們執行的是舊版本的 CMake,則會顯示錯誤訊息,告知他們專案至少需要指定版本的 CMake。
當然,應該將「3.20」替換為您目前正在編寫的 CMake 版本。您也可以根據需要單獨設定每個政策;這有時對想要逐步轉換其專案以使用新行為的專案作者很有幫助,或者可以消除有關依賴舊行為的警告。cmake_policy
命令的 SET
選項可用於明確要求特定政策的舊行為或新行為。
例如,CMake 2.6 引入了政策 CMP0002
,該政策要求所有邏輯目標名稱都是全域唯一的(重複的目標名稱以前在某些情況下會因意外而運作,但未被診斷出來)。使用重複目標名稱並意外運作的專案將收到引用該政策的警告。可以使用以下程式碼來消除警告
cmake_policy(SET CMP0002 OLD)
這明確告知 CMake 對該政策使用舊行為(靜默接受重複的目標名稱)。另一個選項是使用以下程式碼
cmake_policy(SET CMP0002 NEW)
明確告知 CMake 使用新行為,並在建立重複目標時產生錯誤。將此添加到專案後,在作者刪除任何重複的目標名稱之前,將無法建置。
當新版本的 CMake 發佈時,它會引入新的政策,這些政策仍然可以建置舊專案,因為預設情況下,它們不會要求任何新政策的 NEW
行為。開始新專案時,應始終使用 cmake_minimum_required
命令指定要支援的最新 CMake 版本。這將確保專案編寫為使用該版本的 CMake 中的政策運作,而不是使用任何舊行為。如果未設定任何政策版本,CMake 將會發出警告並假設政策版本為 2.4。這允許不指定 cmake_minimum_required
的現有專案像使用 CMake 2.4 一樣進行建置。
政策堆疊¶
政策設定使用堆疊來限定範圍。當進入專案的新子目錄時(使用 add_subdirectory
),會推送堆疊的新層級,並在離開時彈出。因此,在專案的一個目錄中設定政策不會影響父目錄或同級目錄,但會影響子目錄。
當專案包含單獨維護但建置在樹狀結構內的子專案時,此功能很有用。專案中的最上層 CMakeLists 檔案可以寫入
cmake_policy(VERSION 2.6)
project(MyProject)
add_subdirectory(OtherProject)
# ... code requiring new behavior as of CMake 2.6 ...
而 OtherProject/CMakeLists.txt
檔案包含
cmake_policy(VERSION 2.4)
projectS(OtherProject)
# ... code that builds with CMake 2.4 ...
這允許專案更新到 CMake 2.6,而子專案、模組和包含的檔案會繼續使用 CMake 2.4 進行建置,直到其維護者更新它們為止。
使用者程式碼可以使用 cmake_policy
命令來推送和彈出其自己的堆疊層級,前提是每次推送都配對一個彈出。當暫時請求一小段程式碼的不同行為時,此功能很有用。例如,政策 CMP0003
會移除在使用新行為時曾經包含的額外連結目錄。在逐步更新專案時,可能難以使用其餘目標正常建置特定目標。程式碼
cmake_policy(PUSH)
cmake_policy(SET CMP0003 OLD) # use old-style link for now
add_executable(myexe ...)
cmake_policy(POP)
會消除警告並對該目標使用舊行為。您可以透過從命令列執行 CMake 來取得政策清單和特定政策的說明,如下所示
cmake --help-command cmake_policy
cmake --help-policies
cmake --help-policy CMP0003
為新版本的 CMake 更新專案¶
當 CMake 版本引入新的政策時,可能會為某些現有專案產生警告。這些警告表示可能需要對專案進行變更,以處理新政策。雖然舊版本的專案可以繼續建置並顯示警告,但應更新專案開發樹狀結構以考慮新政策。有兩種更新樹狀結構的方法:一次性更新和逐步更新。哪一種更容易取決於專案的大小以及哪些新政策會產生警告。
一次性方法¶
為新版本的 CMake 更新專案的最簡單方法是僅變更在專案頂部設定的政策版本。然後,嘗試使用新的 CMake 版本進行建置以解決問題。例如,若要更新專案以使用 CMake 3.20 進行建置,可以在最上層 CMakeLists 檔案的開頭寫入
cmake_minimum_required(VERSION 3.20)
這會告知 CMake 對在 CMake 3.20 及以下版本中引入的每個政策使用新行為。使用 CMake 3.20 建置此專案時,不會產生有關政策的警告,因為它知道較新版本中沒有引入任何政策。但是,如果專案依賴於舊的政策行為,則它可能無法建置,因為 CMake 現在使用新行為而沒有警告。由新增政策版本行的專案作者負責解決這些問題。
逐步方法¶
另一種針對新版 CMake 更新專案的方法是逐一處理每個警告。這種方法的一個優點是,專案在整個過程中會持續建置,因此可以逐步進行變更。
當 CMake 遇到需要判斷是否使用舊行為或新行為的策略時,它會檢查專案是否已設定該策略。如果策略已設定,CMake 會靜默地使用對應的行為。如果策略未設定,CMake 會使用舊行為,但會警告作者該策略未設定。
在許多情況下,警告訊息會指向導致警告的 CMakeLists 檔案中的確切程式碼行。在某些情況下,必須等到 CMake 為專案產生原生建置系統規則時才能診斷出問題,因此警告不會包含明確的上下文資訊。在這些情況下,CMake 會嘗試提供一些關於可能需要變更程式碼的位置的資訊。這些「產生時」策略的文件應指出應在專案程式碼的哪個位置設定策略以使其生效。
為了逐步更新專案,應一次處理一個警告。如下所述,可能會發生幾種情況。
程式碼正確時隱藏警告¶
許多策略警告的產生可能僅僅是因為專案未設定該策略,即使專案在新行為下可能運作正常(CMake 無法知道其中的差異)。對於關於某個策略的警告 CMP<NNNN>
,您可以透過在專案頂部新增
cmake_policy(SET CMP<NNNN> NEW)
並嘗試建置它,來檢查是否屬於這種情況。如果專案在新行為下能正常建置,請移至下一個策略警告。如果專案無法正常建置,則可能適用其他情況之一。
在不更新程式碼的情況下隱藏警告¶
使用者可以透過在專案頂部新增
cmake_policy(SET CMP<NNNN> OLD)
來抑制所有 CMP<NNNN>
警告的實例。然而,我們鼓勵專案作者更新其程式碼,使其能與所有策略的新行為一起運作。這一點尤其重要,因為(遙遠的)未來版本的 CMake 可能會移除對舊行為的支援,並針對請求它們的專案產生錯誤(這會告訴使用者取得舊版本的 CMake 來建置專案)。
透過更新程式碼來隱藏警告¶
當專案無法在某個策略的新行為下正常運作時,需要更新程式碼。為了處理某個策略 CMP<NNNN>
的警告,請在專案頂部新增
cmake_policy(SET CMP<NNNN> NEW)
然後修正程式碼以使其在新行為下運作。
如果出現多個警告實例,同時修正所有實例可能太困難:相反地,開發人員可以透過使用 cmake_policy
命令的 PUSH/POP 簽名,一次修正一個。
cmake_policy(PUSH)
cmake_policy(SET CMP<NNNN> NEW)
# ... code updated for new policy behavior ...
cmake_policy(POP)
這將請求已修正的小段程式碼使用新行為。其他策略警告的實例可能仍然會出現,且必須分開修正。
更新專案策略版本¶
在處理完所有策略警告並使專案在新版 CMake 中能順利建置後,還剩一個步驟。現在應更新專案頂部設定的策略版本,以符合新的 CMake 版本,就像上面描述的一步到位方法一樣。例如,在更新專案以使用 CMake 3.20 順利建置後,使用者可以將專案頂部更新為以下程式碼行
cmake_minimum_required(VERSION 3.20)
這會將 CMake 3.20 或更低版本中引入的所有策略設定為使用新行為。然後,使用者可以掃過其餘程式碼,並移除使用 cmake_policy
命令逐步請求新行為的呼叫。最終結果應與一步到位方法相同,但可以逐步實現。
支援多個 CMake 版本¶
某些專案可能想要同時支援幾個 CMake 版本。目標是在舊版本上建置,同時也能在新版本上運作而沒有警告。為了同時支援 CMake 2.4 和 2.6,可以編寫如下程式碼:
cmake_minimum_required(VERSION 2.4)
if(COMMAND cmake_policy)
# policy settings ...
cmake_policy(SET CMP0003 NEW)
endif()
這會將策略設定為使用 CMake 2.6 建置,並忽略 CMake 2.4 的策略。為了同時支援 CMake 2.6 和 CMake 2.8 的某些策略,可以編寫如下程式碼:
cmake_minimum_required(VERSION 2.6)
if(POLICY CMP1234)
# policies not known to CMake 2.6 ...
cmake_policy(SET CMP1234 NEW)
endif()
這會將策略設定為使用 CMake 2.8 建置,並忽略 CMake 2.6 的策略。如果已知專案可以同時使用 CMake 2.6 和 CMake 2.8 的新策略建置,則使用者可以編寫:
cmake_minimum_required(VERSION 2.6)
if (NOT ${CMAKE_VERSION} VERSION_LESS 2.8)
cmake_policy(VERSION 2.8)
endif()
檢查 CMake 版本¶
CMake 是一個不斷發展的程式,隨著新版本的發佈,會引入新的功能或命令。因此,可能會出現您想要使用當前版本 CMake 中的命令,但在舊版本中沒有的情況。處理這種情況有幾種方法;一種選擇是使用 if
命令來檢查新命令是否存在。例如
# test if the command exists
if(COMMAND some_new_command)
# use the command
some_new_command( ARGS...)
endif()
或者,可以透過評估 CMAKE_VERSION
變數來測試正在執行的 CMake 實際版本
# look for newer versions of CMake
if(${CMAKE_VERSION} VERSION_GREATER 3.20)
# do something special here
endif()
最後,某些新發布的 CMake 版本可能不再支援您正在使用的某些行為(儘管我們盡量避免這種情況)。在這些情況下,請使用 CMake 策略,如 cmake-policies
手冊中所述。