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_VERIFYCMAKE_TLS_CAINFOCMAKE_NETRCCMAKE_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_DIRBINARY_DIR 不是原始宣告參數的一部分,則將使用其預設值新增它們。如果在宣告詳細資訊時,FETCHCONTENT_TRY_FIND_PACKAGE_MODE 設定為 NEVER,則將省略任何 FIND_PACKAGE_ARGSOVERRIDE_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_COMPATIBLEPACKAGE_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_SUBDIRExternalProject_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_INCLUDECMAKE_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_DIRBINARY_DIR

ExternalProject_Add() 支援 SOURCE_DIRBINARY_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_DISCONNECTEDFETCHCONTENT_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_DIRBINARY_DIRPOPULATED 選項可用於指定應擷取哪些屬性。每個選項都接受一個值,該值是儲存該屬性的變數名稱。但是,大多數情況下,僅給出 <name>,在這種情況下,呼叫將設定與呼叫 FetchContent_MakeAvailable(name)FetchContent_Populate(name) 時相同的變數。請注意,如果呼叫是由 相依性提供者 履行,則 SOURCE_DIRBINARY_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_DIRBINARY_DIR 引數可用於指定 FetchContent_GetProperties() 應為其對應引數回傳的值。僅當 SOURCE_DIRBINARY_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() 嘗試尋找 googletestCatch2,然後再嘗試從來源下載並建置它們,則可以將 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 是最上層的專案,它直接相依於專案 projBprojCprojBprojC 都可以獨立建置,它們也都相依於另一個專案 projDprojB 另外還相依於 projE。此範例假設所有五個專案都在公司的 git 伺服器上可用。每個專案的 CMakeLists.txt 可能會有類似以下的區段

projA
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)
projB
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)
projC
include(FetchContent)
FetchContent_Declare(
  projD
  GIT_REPOSITORY git@mycompany.com:git/projD.git
  GIT_TAG        7d9a17ad2c962aa13e2fbb8043fb6b8a
)

FetchContent_MakeAvailable(projD)

以上內容應注意以下幾個重點

  • projBprojCprojD 定義了不同的內容詳細資訊,但是 projA 也為 projD 定義了一組內容詳細資訊。因為 projA 會先定義它們,所以不會使用 projBprojC 的詳細資訊。projA 定義的覆寫詳細資訊不需要與 projBprojC 的詳細資訊相符,但是較高層級的專案有責任確保它定義的詳細資訊仍然對子專案有意義。

  • projAFetchContent_MakeAvailable() 的呼叫中,projD 列在 projBprojC 之前,因此它會在 projBprojC 之前填入。 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
)