FetchContent¶
在 3.11 版本加入。
注意
「使用 依賴關係 指南
」提供了此一般主題的高階介紹。它更廣泛地概述了 FetchContent
模組在整體架構中的位置,包括它與 find_package()
命令的關係。建議在繼續了解以下詳細資訊之前先閱讀該指南。
概述¶
此模組可讓您在組態時透過 ExternalProject
模組支援的任何方法來填充內容。雖然 ExternalProject_Add()
在建置時下載,FetchContent
模組會立即提供內容,讓組態步驟可以在 add_subdirectory()
、include()
或 file()
等命令中使用該內容。
內容填充的詳細資訊應該與執行實際填充的命令分開定義。這種分離可確保在任何內容嘗試使用這些詳細資訊來填充內容之前,先定義所有依賴關係的詳細資訊。這在更複雜的專案層次結構中尤其重要,因為依賴關係可能在多個專案之間共用。
以下顯示宣告一些依賴關係的內容詳細資訊,然後使用個別呼叫確保它們已填充的典型範例
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
FetchContent_Declare(
myCompanyIcons
URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)
FetchContent_MakeAvailable(googletest myCompanyIcons)
FetchContent_MakeAvailable()
命令可確保已填充指定的依賴關係,無論是透過先前的呼叫,或是透過自行填充它們。在執行填充時,如果可能,它也會將它們加入主要建置,以便主要建置可以使用已填充專案的目標等等。如需這些步驟如何執行的資訊,請參閱命令的文件。
當使用階層式專案配置時,階層中較高層級的專案可以覆寫專案階層中較低位置指定的內容的宣告詳細資訊。針對給定依賴關係宣告的第一個詳細資訊優先,無論它發生在專案階層的哪個位置。同樣地,第一個嘗試填充依賴關係的呼叫會「獲勝」,後續的填充會重複使用第一個的結果,而不是再次重複填充。請參閱示範此案例的範例。
FetchContent
模組也支援在單一呼叫中定義和填充內容,而不會檢查內容是否已在其他地方填充。這不應在專案中完成,但可能適用於在CMake 腳本模式中填充內容。如需詳細資訊,請參閱 FetchContent_Populate()
。
命令¶
- FetchContent_Declare¶
FetchContent_Declare( <name> <contentOptions>... [EXCLUDE_FROM_ALL] [SYSTEM] [OVERRIDE_FIND_PACKAGE | FIND_PACKAGE_ARGS args...] )
FetchContent_Declare()
函式會記錄描述如何填充指定內容的選項。如果此專案中較早的位置已記錄此類詳細資訊(無論在專案階層中的哪個位置),則會忽略此呼叫以及後續所有針對相同內容<name>
的呼叫。這種「第一個記錄者獲勝」的方法可讓階層式專案的父專案覆寫子專案的內容詳細資訊。內容
<name>
可以是任何不含空格的字串,但良好的做法是只使用字母、數字和底線。名稱將不區分大小寫,而且它應該能清楚地表示它所代表的內容。它通常是子專案的名稱,或是提供給其最上層project()
命令的值(如果它是 CMake 專案)。對於眾所周知的公開專案,名稱通常應該是專案的正式名稱。選擇不常見的名稱會使其他需要相同內容的專案不太可能使用相同的名稱,進而導致多次填充內容。<contentOptions>
可以是ExternalProject_Add()
命令理解的任何下載、更新或修補選項。組態、建置、安裝和測試步驟會明確停用,因此會忽略與這些步驟相關的選項。SOURCE_SUBDIR
選項是例外,如需詳細資訊,請參閱FetchContent_MakeAvailable()
,了解它如何影響行為。在 3.30 版本中變更:當原則
CMP0168
設定為NEW
時,會忽略一些與輸出相關和與目錄相關的選項。如需詳細資訊,請參閱原則文件。在大多數情況下,
<contentOptions>
只會是幾個選項,定義下載方法和方法特定的詳細資訊,例如提交標籤或封存雜湊。例如FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0 ) FetchContent_Declare( myCompanyIcons URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz URL_HASH MD5=5588a7b18261c20068beabfb4f530b87 ) FetchContent_Declare( myCompanyCertificates SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs SVN_REVISION -r12345 )
如果從遠端位置擷取內容,而且您不控制該伺服器,建議使用雜湊做為
GIT_TAG
,而不是分支或標籤名稱。提交雜湊更安全,而且有助於確認下載的內容是否符合您的預期。在 3.14 版本中變更:下載、更新或修補步驟的命令可以存取終端機。這可能需要用於密碼提示或即時顯示命令進度等事項。
在 3.22 版本中加入:
CMAKE_TLS_VERIFY
、CMAKE_TLS_CAINFO
、CMAKE_NETRC
和CMAKE_NETRC_FILE
變數現在會為它們對應的內容選項提供預設值,就像它們為ExternalProject_Add()
所做的一樣。先前,FetchContent
模組會忽略這些變數。在 3.24 版本中加入
FIND_PACKAGE_ARGS
此選項適用於
FetchContent_MakeAvailable()
命令可能會先嘗試呼叫find_package()
來滿足<name>
的依賴關係的情況。依預設,此類呼叫只會是find_package(<name>)
,但FIND_PACKAGE_ARGS
可用於提供要附加在<name>
之後的其他引數。FIND_PACKAGE_ARGS
也可以在之後沒有任何內容的情況下給定,這表示如果FETCHCONTENT_TRY_FIND_PACKAGE_MODE
設定為OPT_IN
,或未設定時,仍然可以呼叫find_package()
。通常不適合將
REQUIRED
指定為FIND_PACKAGE_ARGS
之後的額外參數之一。這樣做會導致find_package()
呼叫必須成功,因此在FetchContent_Declare()
呼叫中指定的其他詳細資訊將沒有機會被用作後備方案。在
FIND_PACKAGE_ARGS
關鍵字之後的所有內容都會附加到find_package()
呼叫,因此所有其他的<contentOptions>
必須在FIND_PACKAGE_ARGS
關鍵字之前。如果在呼叫FetchContent_Declare()
時,CMAKE_FIND_PACKAGE_TARGETS_GLOBAL
變數設定為 true,則GLOBAL
關鍵字將會附加到find_package()
的參數,如果它尚未被指定的話。如果未提供FIND_PACKAGE_ARGS
,但FETCHCONTENT_TRY_FIND_PACKAGE_MODE
設定為ALWAYS
,也會附加此關鍵字。當給定
FIND_PACKAGE_ARGS
時,不能使用OVERRIDE_FIND_PACKAGE
。相依性提供者 討論了另一種重新導向
FetchContent_MakeAvailable()
呼叫的方法。FIND_PACKAGE_ARGS
用於專案控制,而相依性提供者允許使用者覆寫專案行為。OVERRIDE_FIND_PACKAGE
當
FetchContent_Declare(<name> ...)
呼叫包含此選項時,後續對find_package(<name> ...)
的呼叫將確保已呼叫FetchContent_MakeAvailable(<name>)
,然後使用CMAKE_FIND_PACKAGE_REDIRECTS_DIR
目錄中的組態套件檔案(通常由FetchContent_MakeAvailable()
建立)。這實際上使得FetchContent_MakeAvailable()
覆寫具名相依性的find_package()
,允許前者滿足後者的套件需求。當給定OVERRIDE_FIND_PACKAGE
時,不能使用FIND_PACKAGE_ARGS
。如果已設定相依性提供者,且專案呼叫
find_package()
以取得<name>
相依性,則OVERRIDE_FIND_PACKAGE
不會阻止提供者看到該呼叫。相依性提供者始終有機會攔截任何對find_package()
的直接呼叫,除非該呼叫包含BYPASS_PROVIDER
選項。
於版本 3.25 新增
SYSTEM
如果提供了
SYSTEM
參數,則由FetchContent_MakeAvailable()
新增的子目錄的SYSTEM
目錄屬性將設定為 true。這將影響作為該命令一部分建立的非匯入目標。請參閱SYSTEM
目標屬性文件,以獲得更詳細的效果討論。
於版本 3.28 新增
EXCLUDE_FROM_ALL
如果提供了
EXCLUDE_FROM_ALL
參數,則由FetchContent_MakeAvailable()
新增的子目錄中的目標預設不會包含在ALL
目標中,並且可能會從 IDE 專案檔案中排除。請參閱目錄屬性EXCLUDE_FROM_ALL
的文件,以獲得詳細的效果討論。
- FetchContent_MakeAvailable¶
於版本 3.14 新增。
FetchContent_MakeAvailable(<name1> [<name2>...])
此命令確保在返回時,每個具名相依性都可供專案使用。必須為每個相依性呼叫
FetchContent_Declare()
,而第一個此類呼叫將控制該相依性如何可用,如下所述。如果未設定
<lowercaseName>_SOURCE_DIR
於版本 3.24 新增: 如果設定了相依性提供者,則使用
FETCHCONTENT_MAKEAVAILABLE_SERIAL
作為第一個參數,呼叫提供者的命令,後接對<name>
的第一個FetchContent_Declare()
呼叫的參數。如果SOURCE_DIR
或BINARY_DIR
不是原始宣告參數的一部分,則將使用其預設值新增它們。如果在宣告詳細資訊時,FETCHCONTENT_TRY_FIND_PACKAGE_MODE
設定為NEVER
,則將省略任何FIND_PACKAGE_ARGS
。OVERRIDE_FIND_PACKAGE
關鍵字也始終省略。如果提供者滿足了請求,FetchContent_MakeAvailable()
將認為該相依性已處理,跳過下面的其餘步驟,然後繼續處理列表中的下一個相依性。新增於 3.24 版本:如果允許,將會呼叫
find_package(<name> [<args>...])
,其中<args>...
可能由FetchContent_Declare()
中的FIND_PACKAGE_ARGS
選項提供。在呼叫FetchContent_Declare()
時,FETCHCONTENT_TRY_FIND_PACKAGE_MODE
變數的值決定FetchContent_MakeAvailable()
是否可以呼叫find_package()
。如果在呼叫FetchContent_MakeAvailable()
時,CMAKE_FIND_PACKAGE_TARGETS_GLOBAL
變數設定為 true,它仍然會影響該變數在宣告對應詳細資訊時即使為 false,在後續呼叫find_package()
時建立的任何匯入目標。
如果依賴項未被供應商或
find_package()
呼叫滿足,則FetchContent_MakeAvailable()
會使用以下邏輯使依賴項可用如果依賴項在此次執行中較早時已填充,則以與呼叫
FetchContent_GetProperties()
相同的方式設定<lowercaseName>_POPULATED
、<lowercaseName>_SOURCE_DIR
和<lowercaseName>_BINARY_DIR
變數,然後跳過下面的剩餘步驟,並移動到清單中的下一個依賴項。使用先前呼叫
FetchContent_Declare()
記錄的詳細資訊來填充依賴項。如果沒有記錄任何此類詳細資訊,則停止並發生致命錯誤。FETCHCONTENT_SOURCE_DIR_<uppercaseName>
可用於覆蓋宣告的詳細資訊,並改為使用指定位置提供的內容。新增於 3.24 版本:確保
CMAKE_FIND_PACKAGE_REDIRECTS_DIR
目錄包含一個<lowercaseName>-config.cmake
和一個<lowercaseName>-config-version.cmake
檔案(或等效地,<name>Config.cmake
和<name>ConfigVersion.cmake
)。CMAKE_FIND_PACKAGE_REDIRECTS_DIR
變數指向的目錄會在每次 CMake 執行開始時清除。如果在上一步填充依賴項後不存在設定檔,則會寫入一個最小的設定檔,該設定檔會includes
任何帶有OPTIONAL
標誌的<lowercaseName>-extra.cmake
或<name>Extra.cmake
檔案(因此檔案可以遺失且不會產生警告)。同樣地,如果不存在設定版本檔案,則會寫入一個非常簡單的檔案,該檔案將PACKAGE_VERSION_COMPATIBLE
和PACKAGE_VERSION_EXACT
設定為 true。這可確保未來對依賴項的所有find_package()
呼叫都將使用重新導向的設定檔,而不管任何版本要求。CMake 無法自動判斷任意依賴項的版本,因此它無法設定PACKAGE_VERSION
。當依賴項透過下一步中的add_subdirectory()
引入時,它可以選擇在CMAKE_FIND_PACKAGE_REDIRECTS_DIR
中覆蓋產生的設定版本檔案,並使用也設定PACKAGE_VERSION
的檔案。依賴項也可以寫入<lowercaseName>-extra.cmake
或<name>Extra.cmake
檔案以執行自訂處理,或定義其正常(已安裝)套件設定檔通常會定義的任何變數(許多專案不執行任何自訂處理或設定任何變數,因此無需執行此操作)。如果需要,如果依賴項專案不執行此操作,則主專案可以改為寫入這些檔案。這允許主專案從尚未或無法更新以支援此功能的較舊依賴項中新增遺失的詳細資訊。有關範例,請參閱與 find_package() 整合。如果填充內容的頂層目錄包含一個
CMakeLists.txt
檔案,則呼叫add_subdirectory()
將其新增到主組建中。沒有CMakeLists.txt
檔案並不是錯誤,這允許該命令用於在已知位置提供下載內容但不需要或不支援直接新增到組建中的依賴項。新增於 3.18 版本:可以在宣告的詳細資訊中給定
SOURCE_SUBDIR
選項,以便在頂層目錄下的某處尋找(即SOURCE_SUBDIR
由ExternalProject_Add()
命令使用的方式相同)。使用SOURCE_SUBDIR
提供的路徑必須是相對的,並且它將被視為相對於頂層目錄。它也可以指向不包含CMakeLists.txt
檔案的目錄,甚至可以指向不存在的目錄。這可以用於避免新增頂層目錄中包含CMakeLists.txt
檔案的專案。新增於 3.25 版本:如果在呼叫
FetchContent_Declare()
時包含了SYSTEM
關鍵字,則SYSTEM
關鍵字將會新增到add_subdirectory()
命令中。在版本 3.28 中新增:如果
EXCLUDE_FROM_ALL
關鍵字包含在對FetchContent_Declare()
的呼叫中,則EXCLUDE_FROM_ALL
關鍵字將會被新增至add_subdirectory()
命令中。在版本 3.29 中新增:在呼叫
add_subdirectory()
之前,CMAKE_EXPORT_FIND_PACKAGE_NAME
會被設定為相依性的名稱。
專案應盡可能在對任何相依性呼叫
FetchContent_MakeAvailable()
之前,宣告所有可能使用的相依性的詳細資訊。這樣可以確保,如果任何相依性也是其他一或多個相依性的子相依性,主專案仍然可以控制將會使用的詳細資訊(因為它會優先宣告它們)。在下列程式碼範例中,假設uses_other
相依性也會在內部使用FetchContent
來新增other
相依性。# WRONG: Should declare all details first FetchContent_Declare(uses_other ...) FetchContent_MakeAvailable(uses_other) FetchContent_Declare(other ...) # Will be ignored, uses_other beat us to it FetchContent_MakeAvailable(other) # Would use details declared by uses_other
# CORRECT: All details declared first, so they will take priority FetchContent_Declare(uses_other ...) FetchContent_Declare(other ...) FetchContent_MakeAvailable(uses_other other)
請注意,
CMAKE_VERIFY_INTERFACE_HEADER_SETS
在進入FetchContent_MakeAvailable()
時會被明確地設定為 false,並且會在命令返回之前恢復為其原始值。開發人員通常只希望驗證主專案的標頭集合,而不是任何相依性的標頭集合。這種對CMAKE_VERIFY_INTERFACE_HEADER_SETS
變數的局部操作提供了這種直覺的行為。您可以使用像是CMAKE_PROJECT_INCLUDE
或CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE
的變數來重新啟用所有或某些相依性的驗證。您也可以設定個別目標的VERIFY_INTERFACE_HEADER_SETS
屬性。
- FetchContent_Populate¶
FetchContent_Populate()
命令是一個獨立的呼叫,可以用來執行內容填充作為一個隔離的操作。它很少是正確使用的命令,專案幾乎總是應該使用FetchContent_Declare()
和FetchContent_MakeAvailable()
來代替。FetchContent_Populate()
的主要使用案例是在 CMake 腳本模式 中,作為實作其他更高層級的自訂功能的一部分。FetchContent_Populate( <name> [QUIET] [SUBBUILD_DIR <subBuildDir>] [SOURCE_DIR <srcDir>] [BINARY_DIR <binDir>] ... )
在 <name> 之後至少必須指定一個選項,否則呼叫的解讀方式會有所不同(請參閱 下方)。
FetchContent_Populate()
支援的選項與FetchContent_Declare()
的選項相同,但有一些例外。以下這些與使用FetchContent_Populate()
填充內容無關,因此不支援:EXCLUDE_FROM_ALL
SYSTEM
OVERRIDE_FIND_PACKAGE
FIND_PACKAGE_ARGS
上面簽章中顯示的少數選項,要么是
FetchContent_Populate()
特有的,要么它們的行為與ExternalProject_Add()
對待它們的方式略有不同。QUIET
可以提供
QUIET
選項來隱藏與填充指定內容相關的輸出。如果填充失敗,無論是否給定此選項,都會顯示輸出,以便可以診斷失敗的原因。FETCHCONTENT_QUIET
變數對直接提供內容詳細資訊的此形式的FetchContent_Populate()
呼叫沒有影響。在版本 3.30 中變更:當政策
CMP0168
設定為NEW
時,QUIET
選項和FETCHCONTENT_QUIET
變數沒有影響。在這種情況下,預設情況下輸出仍然是靜音的,但詳細程度由訊息記錄層級控制(請參閱CMAKE_MESSAGE_LOG_LEVEL
和--log-level
)。SUBBUILD_DIR
可以提供
SUBBUILD_DIR
參數來變更建立子建置以執行填充的位置。預設值為${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild
,並且通常不需要覆蓋此預設值。如果指定相對路徑,則會將其解讀為相對於CMAKE_CURRENT_BINARY_DIR
。此選項不應與SOURCE_SUBDIR
選項混淆,後者僅影響FetchContent_MakeAvailable()
命令。在版本 3.30 中變更:當政策
CMP0168
設定為NEW
時,會忽略SUBBUILD_DIR
,因為在這種情況下沒有子建置。SOURCE_DIR
、BINARY_DIR
ExternalProject_Add()
支援SOURCE_DIR
和BINARY_DIR
參數,但FetchContent_Populate()
使用不同的預設值。SOURCE_DIR
預設為${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src
,而BINARY_DIR
預設為${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-build
。如果指定相對路徑,則會將其解讀為相對於CMAKE_CURRENT_BINARY_DIR
。
除了上述明確的選項之外,任何其他無法識別的選項都會以未修改的方式傳遞給
ExternalProject_Add()
,以設定下載、修補和更新步驟。明確禁止以下選項(它們由FetchContent_Populate()
命令停用):CONFIGURE_COMMAND
BUILD_COMMAND
INSTALL_COMMAND
TEST_COMMAND
使用這種形式,會忽略
FETCHCONTENT_FULLY_DISCONNECTED
和FETCHCONTENT_UPDATES_DISCONNECTED
變數以及政策CMP0170
。當此形式的
FetchContent_Populate()
回傳時,將在呼叫者的作用域中設定以下變數:<lowercaseName>_SOURCE_DIR
回傳時,已填充內容所在的位置。
<lowercaseName>_BINARY_DIR
最初預期用作對應建置目錄的目錄,但在使用此命令形式時不太可能相關。
如果在 CMake 指令碼模式 中使用
FetchContent_Populate()
,請注意實作會設定一個子建置,因此需要 CMake 產生器和建置工具可用。如果預設找不到這些工具,則需要在叫用指令碼的命令列上適當設定CMAKE_GENERATOR
和可能的CMAKE_MAKE_PROGRAM
變數。變更於 3.30 版本:如果策略
CMP0168
設定為NEW
,則不使用子建置。在 CMake 指令碼模式 中,這允許在沒有任何建置工具或 CMake 產生器的情況下呼叫FetchContent_Populate()
。新增於 3.18 版本:新增對
DOWNLOAD_NO_EXTRACT
選項的支援。
此命令支援另一種形式,但不再應使用
FetchContent_Populate(<name>)變更於 3.30 版本:此形式已被棄用。策略
CMP0169
為仍然需要使用此形式的專案提供回溯相容性,但專案應更新為改用FetchContent_MakeAvailable()
。在此形式中,傳遞給
FetchContent_Populate()
的唯一引數是<name>
。以此方式使用時,命令會假設內容詳細資訊已由先前呼叫FetchContent_Declare()
記錄。詳細資訊會儲存在全域屬性中,因此它們不受變數或目錄作用域等因素的影響。因此,詳細資訊先前在專案中的哪個位置宣告並不重要,只要它們在呼叫FetchContent_Populate()
之前宣告即可。然後,這些儲存的詳細資訊會用於根據ExternalProject_Add()
的方法來填充內容(請參閱策略CMP0168
,以了解如何完成的重要行為方面)。當此形式的
FetchContent_Populate()
回傳時,將在呼叫者的作用域中設定以下變數:
<lowercaseName>_POPULATED
此值將始終由呼叫設定為
TRUE
。<lowercaseName>_SOURCE_DIR
回傳時,已填充內容所在的位置。
<lowercaseName>_BINARY_DIR
預期用作對應建置目錄的目錄。
也可以使用
FetchContent_GetProperties()
命令,從專案階層中的任何位置擷取這三個變數的值。實作會確保如果內容已在先前的 CMake 執行中填充,則將重複使用該內容,而不是再次重新填充。對於填充涉及下載內容的常見情況,下載成本僅需支付一次。但請注意,在單次 CMake 執行中,多次使用相同的
<name>
呼叫FetchContent_Populate(<name>)
是錯誤的。請參閱FetchContent_GetProperties()
,以了解如何判斷<name>
的填充是否已在目前執行中執行。
- FetchContent_GetProperties¶
使用儲存的內容詳細資訊時,呼叫
FetchContent_MakeAvailable()
或FetchContent_Populate()
會在全域屬性中記錄資訊,這些資訊可以隨時查詢。此資訊可能包括與內容相關聯的原始碼和二進位目錄,以及內容填充是否已在目前設定執行期間處理。FetchContent_GetProperties( <name> [SOURCE_DIR <srcDirVar>] [BINARY_DIR <binDirVar>] [POPULATED <doneVar>] )
SOURCE_DIR
、BINARY_DIR
和POPULATED
選項可用於指定應擷取哪些屬性。每個選項都接受一個值,該值是儲存該屬性的變數名稱。但是,大多數情況下,僅給出<name>
,在這種情況下,呼叫將設定與呼叫FetchContent_MakeAvailable(name)
或FetchContent_Populate(name)
時相同的變數。請注意,如果呼叫是由 相依性提供者 履行,則SOURCE_DIR
和BINARY_DIR
的值可以為空。使用
FetchContent_MakeAvailable()
時,很少需要此命令。它更常用於實作已棄用的模式與FetchContent_Populate()
,這可確保相關變數始終定義,無論是否已在專案中的其他位置執行填充。# WARNING: This pattern is deprecated, don't use it! # # Check if population has already been performed FetchContent_GetProperties(depname) if(NOT depname_POPULATED) # Fetch the content using previously declared details FetchContent_Populate(depname) # Set custom variables, policies, etc. # ... # Bring the populated content into the build add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR}) endif()
- FetchContent_SetPopulated¶
新增於 3.24 版本。
注意
此命令應僅由 相依性提供者 呼叫。在任何其他內容中呼叫它是不受支援的,未來的 CMake 版本在這種情況下可能會停止並出現致命錯誤。
FetchContent_SetPopulated( <name> [SOURCE_DIR <srcDir>] [BINARY_DIR <binDir>] )
如果提供者命令履行
FETCHCONTENT_MAKEAVAILABLE_SERIAL
請求,則必須在回傳之前呼叫此函式。SOURCE_DIR
和BINARY_DIR
引數可用於指定FetchContent_GetProperties()
應為其對應引數回傳的值。僅當SOURCE_DIR
和BINARY_DIR
的含義與內建FetchContent_MakeAvailable()
實作填充時的含義相同時,才提供它們。
變數¶
許多快取變數可能會影響使用 FetchContent_Declare()
呼叫中的詳細資訊來填充內容時的行為。
注意
所有這些變數都旨在讓開發人員自訂行為。它們通常不應由專案設定。
- FETCHCONTENT_BASE_DIR¶
在大多數情況下,儲存的詳細資訊不會指定任何與內部子建置、最終原始碼和建置區域所使用的目錄相關的選項。通常最好將這些決策交由
FetchContent
模組代表專案處理。FETCHCONTENT_BASE_DIR
快取變數控制收集所有內容填充目錄的起始點,但在大多數情況下,開發人員不需要變更此值。預設位置是${CMAKE_BINARY_DIR}/_deps
,但如果開發人員變更此值,他們應盡量保持路徑簡短且剛好在建置樹狀結構的頂層之下,以避免在 Windows 上遇到路徑長度問題。
- FETCHCONTENT_QUIET¶
填充期間的記錄輸出可能非常冗長,導致設定階段非常雜亂。此快取選項(預設為
ON
)會隱藏所有填充輸出,除非遇到錯誤。如果遇到下載掛起的問題,暫時關閉此選項可能會有助於診斷哪個內容填充導致問題。在 3.30 版本中變更:如果原則
CMP0168
設定為NEW
,則會忽略FETCHCONTENT_QUIET
。在這種情況下,輸出預設仍然是靜默的,但詳細程度由訊息記錄層級控制(請參閱CMAKE_MESSAGE_LOG_LEVEL
和--log-level
)。
- FETCHCONTENT_FULLY_DISCONNECTED¶
啟用此選項後,不會嘗試下載或更新任何內容。假設所有內容已在先前的執行中填充,或原始碼目錄已指向開發人員手動提供的現有內容(使用下面進一步描述的選項)。當開發人員知道任何內容詳細資訊都未變更時,將此選項設定為
ON
可以加快設定階段。預設值為OFF
。注意
在建置目錄中首次執行時,
FETCHCONTENT_FULLY_DISCONNECTED
變數不是防止任何網路存取的適當方法。這樣做可能會破壞專案、導致誤導性的錯誤訊息,並隱藏細微的填充失敗。此變數專門用於在 CMake 首次執行後才啟用。如果您想在首次執行時也防止網路存取,請使用相依性提供者並從本機內容填充相依性。在 3.30 版本中變更:當
FETCHCONTENT_FULLY_DISCONNECTED
為 true 時,現在會強制執行原始碼目錄已填充的限制。請參閱原則CMP0170
。
- FETCHCONTENT_UPDATES_DISCONNECTED¶
與
FETCHCONTENT_FULLY_DISCONNECTED
相比,這是較不嚴重的下載/更新控制。FETCHCONTENT_UPDATES_DISCONNECTED
並非略過所有下載和更新邏輯,而僅防止在使用 git 或 hg 下載方法時,更新步驟連線到遠端伺服器。如果更新步驟的詳細資訊發生變更,仍然會進行更新,但更新只會嘗試使用本機已有的資訊進行(因此切換到本機已擷取的不同標籤或提交會成功,但切換到未知的提交雜湊會失敗)。下載步驟不受影響,因此如果先前未下載內容,則啟用此選項時仍會下載。這可以加快設定步驟,但不如FETCHCONTENT_FULLY_DISCONNECTED
快。FETCHCONTENT_UPDATES_DISCONNECTED
的預設值為OFF
。
- FETCHCONTENT_TRY_FIND_PACKAGE_MODE¶
新增於 3.24 版本。
此變數會修改
FetchContent_Declare()
針對給定相依性記錄的詳細資訊。雖然它最終控制FetchContent_MakeAvailable()
的行為,但在呼叫FetchContent_Declare()
時使用的變數值。無論在呼叫FetchContent_MakeAvailable()
時將變數設定為什麼值,都沒有任何區別。由於變數應僅由使用者設定,而不是由專案直接設定,因此它通常會在整個過程中具有相同的值,因此這種區別通常不會被注意到。FETCHCONTENT_TRY_FIND_PACKAGE_MODE
最終控制是否允許FetchContent_MakeAvailable()
呼叫find_package()
來滿足相依性。變數可以設定為下列其中一個值OPT_IN
如果
FetchContent_Declare()
呼叫包含FIND_PACKAGE_ARGS
關鍵字,FetchContent_MakeAvailable()
才會呼叫find_package()
。如果未設定FETCHCONTENT_TRY_FIND_PACKAGE_MODE
,這也是預設行為。ALWAYS
無論
FetchContent_Declare()
呼叫是否包含FIND_PACKAGE_ARGS
關鍵字,find_package()
都可以由FetchContent_MakeAvailable()
呼叫。如果未提供FIND_PACKAGE_ARGS
關鍵字,則行為將如同已提供FIND_PACKAGE_ARGS
,其後沒有額外的引數。NEVER
FetchContent_MakeAvailable()
不會呼叫find_package()
。任何給予FetchContent_Declare()
呼叫的FIND_PACKAGE_ARGS
都會被忽略。
作為一種特殊情況,如果相依性的
FETCHCONTENT_SOURCE_DIR_<uppercaseName>
變數具有非空值,則假設使用者覆寫所有其他讓該相依性可用的方法。FETCHCONTENT_TRY_FIND_PACKAGE_MODE
將對該相依性沒有任何影響,並且FetchContent_MakeAvailable()
將不會嘗試為其呼叫find_package()
。
除了上述之外,還為每個內容名稱定義了下列變數
- FETCHCONTENT_SOURCE_DIR_<uppercaseName>¶
如果設定此選項,則不會對指定的內容執行下載或更新步驟,並且傳回給呼叫者的
<lowercaseName>_SOURCE_DIR
變數會指向此位置。這讓開發人員能夠擁有內容的獨立檢出版本,他們可以自由修改而不會受到建置干擾。建置過程只會使用現有的來源,但仍會定義<lowercaseName>_BINARY_DIR
以指向其自身的建置區域內。強烈建議開發人員使用此機制,而不是編輯預設位置中填入的來源,因為當專案變更內容填入的詳細資訊時,預設位置中的來源變更可能會遺失。
- FETCHCONTENT_UPDATES_DISCONNECTED_<uppercaseName>¶
這是每個內容相當於
FETCHCONTENT_UPDATES_DISCONNECTED
的選項。如果全域選項或此選項為ON
,則 git 和 hg 方法的更新將不會連線到任何遠端以獲取指定內容。它們只會使用本地已有的資訊。針對個別內容停用更新對於那些詳細資訊很少變更的內容很有用,同時仍讓其他頻繁變更的內容啟用更新。
範例¶
典型案例¶
第一個相當簡單的範例確保一些常用的測試框架可供主要建置使用
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG 605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
)
# After the following call, the CMake targets defined by googletest and
# Catch2 will be available to the rest of the build
FetchContent_MakeAvailable(googletest Catch2)
與 find_package() 整合¶
對於先前的範例,如果使用者想要先透過 find_package()
嘗試尋找 googletest
和 Catch2
,然後再嘗試從來源下載並建置它們,則可以將 FETCHCONTENT_TRY_FIND_PACKAGE_MODE
變數設定為 ALWAYS
。這也會影響整個專案中對 FetchContent_Declare()
的任何其他呼叫,這可能無法接受。透過將 FIND_PACKAGE_ARGS
新增至宣告的詳細資訊並將 FETCHCONTENT_TRY_FIND_PACKAGE_MODE
設定為未設定或設定為 OPT_IN
,可以只針對這兩個相依性啟用此行為。
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
FIND_PACKAGE_ARGS NAMES GTest
)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG 605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
FIND_PACKAGE_ARGS
)
# This will try calling find_package() first for both dependencies
FetchContent_MakeAvailable(googletest Catch2)
對於 Catch2
,不需要對 find_package()
提供額外的引數,因此在 FIND_PACKAGE_ARGS
關鍵字之後沒有提供額外的引數。對於 googletest
,它的套件通常稱為 GTest
,因此會新增引數以支援透過該名稱找到它。
如果使用者想要停用 FetchContent_MakeAvailable()
對任何相依性呼叫 find_package()
,即使它在其宣告的詳細資訊中提供了 FIND_PACKAGE_ARGS
,他們也可以將 FETCHCONTENT_TRY_FIND_PACKAGE_MODE
設定為 NEVER
。
如果專案想要指示這兩個相依性應從來源下載並建置,並且應該重新導向 find_package()
呼叫以使用已建置的相依性,則在宣告內容詳細資訊時應使用 OVERRIDE_FIND_PACKAGE
選項
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG 605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
OVERRIDE_FIND_PACKAGE
)
# The following will automatically forward through to FetchContent_MakeAvailable()
find_package(googletest)
find_package(Catch2)
CMake 提供了一個 FindGTest 模組,該模組定義了一些舊專案可能會使用的變數,而不是連結到匯入的目標。為了支援這些情況,我們可以提供一個額外的檔案。為了與 FetchContent
的「先定義者勝出」的原則保持一致,我們只在其他內容尚未執行此操作時才寫出該檔案。
FetchContent_MakeAvailable(googletest)
if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-extra.cmake AND
NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletestExtra.cmake)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-extra.cmake
[=[
if("${GTEST_LIBRARIES}" STREQUAL "" AND TARGET GTest::gtest)
set(GTEST_LIBRARIES GTest::gtest)
endif()
if("${GTEST_MAIN_LIBRARIES}" STREQUAL "" AND TARGET GTest::gtest_main)
set(GTEST_MAIN_LIBRARIES GTest::gtest_main)
endif()
if("${GTEST_BOTH_LIBRARIES}" STREQUAL "")
set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
endif()
]=])
endif()
專案也可能會使用 find_package(GTest)
而不是 find_package(googletest)
,但是可以使用 CMAKE_FIND_PACKAGE_REDIRECTS_DIR
區域將後者作為前者的相依性引入。這可能足以滿足典型的 find_package(GTest)
呼叫。
FetchContent_MakeAvailable(googletest)
if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config.cmake AND
NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/GTestConfig.cmake)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config.cmake
[=[
include(CMakeFindDependencyMacro)
find_dependency(googletest)
]=])
endif()
if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config-version.cmake AND
NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/GTestConfigVersion.cmake)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config-version.cmake
[=[
include(${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-config-version.cmake OPTIONAL)
if(NOT PACKAGE_VERSION_COMPATIBLE)
include(${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletestConfigVersion.cmake OPTIONAL)
endif()
]=])
endif()
覆寫尋找 CMakeLists.txt 的位置¶
如果子專案的 CMakeLists.txt
檔案不在其來源樹的最上層,則可以使用 SOURCE_SUBDIR
選項來告訴 FetchContent
在哪裡找到它。以下範例顯示如何使用該選項,並且還在將子專案提取到主要建置之前設定對子專案有意義的變數(設定為 INTERNAL
快取變數,以避免出現原則 CMP0077
的問題)
include(FetchContent)
FetchContent_Declare(
protobuf
GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
GIT_TAG ae50d9b9902526efd6c7a1907d09739f959c6297 # v3.15.0
SOURCE_SUBDIR cmake
)
set(protobuf_BUILD_TESTS OFF CACHE INTERNAL "")
FetchContent_MakeAvailable(protobuf)
複雜的相依性階層¶
在更複雜的專案階層中,相依性關係可能會更複雜。考慮一個階層,其中 projA
是最上層的專案,它直接相依於專案 projB
和 projC
。projB
和 projC
都可以獨立建置,它們也都相依於另一個專案 projD
。projB
另外還相依於 projE
。此範例假設所有五個專案都在公司的 git 伺服器上可用。每個專案的 CMakeLists.txt
可能會有類似以下的區段
include(FetchContent)
FetchContent_Declare(
projB
GIT_REPOSITORY git@mycompany.com:git/projB.git
GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d
)
FetchContent_Declare(
projC
GIT_REPOSITORY git@mycompany.com:git/projC.git
GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9
)
FetchContent_Declare(
projD
GIT_REPOSITORY git@mycompany.com:git/projD.git
GIT_TAG origin/integrationBranch
)
FetchContent_Declare(
projE
GIT_REPOSITORY git@mycompany.com:git/projE.git
GIT_TAG v2.3-rc1
)
# Order is important, see notes in the discussion further below
FetchContent_MakeAvailable(projD projB projC)
include(FetchContent)
FetchContent_Declare(
projD
GIT_REPOSITORY git@mycompany.com:git/projD.git
GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632
)
FetchContent_Declare(
projE
GIT_REPOSITORY git@mycompany.com:git/projE.git
GIT_TAG 68e20f674a48be38d60e129f600faf7d
)
FetchContent_MakeAvailable(projD projE)
include(FetchContent)
FetchContent_Declare(
projD
GIT_REPOSITORY git@mycompany.com:git/projD.git
GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a
)
FetchContent_MakeAvailable(projD)
以上內容應注意以下幾個重點
projB
和projC
為projD
定義了不同的內容詳細資訊,但是projA
也為projD
定義了一組內容詳細資訊。因為projA
會先定義它們,所以不會使用projB
和projC
的詳細資訊。projA
定義的覆寫詳細資訊不需要與projB
或projC
的詳細資訊相符,但是較高層級的專案有責任確保它定義的詳細資訊仍然對子專案有意義。在
projA
對FetchContent_MakeAvailable()
的呼叫中,projD
列在projB
和projC
之前,因此它會在projB
或projC
之前填入。projA
並不一定要執行此操作,這樣做可以確保projA
完全控制將projD
引入建置的環境(目錄屬性特別相關)。雖然
projA
定義了projE
的內容詳細資訊,但它不需要明確呼叫FetchContent_MakeAvailable(projE)
或FetchContent_Populate(projD)
本身。相反地,它將這個任務留給子專案projB
。對於較高層級的專案,通常只需定義覆寫的內容詳細資訊,然後將實際的填充工作留給子專案即可。這可以避免在專案階層的每一層不必要地重複相同的操作,但只有在相依性設定的目錄屬性預期不會影響共享相依性(在本例中為projE
)的填充時,才應該這樣做。
填充內容但不將其加入建置¶
專案不一定總是需要將填充的內容加入建置。有時專案只是希望讓下載的內容在可預測的位置可用。下一個範例確保一組標準的公司工具鏈檔案(甚至可能是工具鏈二進位檔案本身)可以提早可用,以便用於相同的建置。
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
FetchContent_Declare(
mycom_toolchains
URL https://intranet.mycompany.com//toolchains_1.3.2.tar.gz
)
FetchContent_MakeAvailable(mycom_toolchains)
project(CrossCompileExample)
可以將專案配置為使用其中一個下載的工具鏈,如下所示
cmake -DCMAKE_TOOLCHAIN_FILE=_deps/mycom_toolchains-src/toolchain_arm.cmake /path/to/src
當 CMake 處理 CMakeLists.txt
檔案時,它會將 tarball 下載並解壓縮到相對於建置目錄的 _deps/mycompany_toolchains-src
中。在到達 CMAKE_TOOLCHAIN_FILE
變數之前,不會使用它,此時 CMake 會在相對於建置目錄的位置尋找指定的工具鏈檔案。因為 tarball 已經下載並解壓縮,所以工具鏈檔案會存在,即使是第一次在建置目錄中執行 cmake 也是如此。
在 CMake 腳本模式中填充內容¶
最後這個範例示範了如何使用 CMake 的 腳本模式下載並解壓縮韌體 tarball。FetchContent_Populate()
的呼叫指定了所有內容詳細資訊,並且解壓縮的韌體將放置在目前工作目錄下的 firmware
目錄中。
getFirmware.cmake
¶# NOTE: Intended to be run in script mode with cmake -P
include(FetchContent)
FetchContent_Populate(
firmware
URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz
URL_HASH MD5=68247684da89b608d466253762b0ff11
SOURCE_DIR firmware
)