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 也可以在後面不接任何內容的情況下給出,這表示如果 find_package() 仍然可以呼叫,如果 FETCHCONTENT_TRY_FIND_PACKAGE_MODE 設定為 OPT_IN,或未設定。

通常不適合將 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

如果已設定 依賴關係提供者,並且專案對 <name> 依賴關係呼叫 find_package(),則 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,它仍然會影響在該變數進而呼叫 find_package() 時建立的任何匯入目標,即使在宣告對應細節時該變數為 false。

如果依賴關係未被提供者或 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 執行開始時都會清除。如果在上一步中填充依賴關係後不存在配置檔案,則將寫入一個最小的檔案,該檔案 包含 任何帶有 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 新增: 如果 SYSTEM 關鍵字包含在對 FetchContent_Declare() 的呼叫中,則 SYSTEM 關鍵字將被新增至 add_subdirectory() 命令。

    版本 3.28 新增: 如果 EXCLUDE_FROM_ALL 關鍵字包含在對 FetchContent_Declare() 的呼叫中,則 EXCLUDE_FROM_ALL 關鍵字將被新增至 add_subdirectory() 命令。

    版本 3.29 新增: CMAKE_EXPORT_FIND_PACKAGE_NAME 在呼叫 add_subdirectory() 之前,會設定為相依性名稱。

專案應旨在呼叫任何相依性的 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)

請注意,進入 FetchContent_MakeAvailable() 時,CMAKE_VERIFY_INTERFACE_HEADER_SETS 會明確設定為 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_DIR, BINARY_DIR

SOURCE_DIRBINARY_DIR 引數受 ExternalProject_Add() 支援,但 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>) 是一種錯誤。有關如何確定是否已在當前執行中執行了 <name> 的填充,請參閱 FetchContent_GetProperties()

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_MakeAvailable() 呼叫包含 FIND_PACKAGE_ARGS 關鍵字時,find_package() 才會呼叫。如果未設定 FETCHCONTENT_TRY_FIND_PACKAGE_MODE ,這也是預設行為。

ALWAYS

find_package() 可以被 FetchContent_MakeAvailable() 呼叫,無論 FetchContent_Declare() 呼叫是否包含 FIND_PACKAGE_ARGS 關鍵字。如果沒有給定 FIND_PACKAGE_ARGS 關鍵字,行為將如同已提供 FIND_PACKAGE_ARGS,且其後沒有額外參數一樣。

絕對不要

FetchContent_MakeAvailable() 將不會呼叫 find_package()FIND_PACKAGE_ARGS 給予 FetchContent_Declare() 呼叫的任何參數都將被忽略。

作為一個特殊情況,如果 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 的詳細資訊匹配,但由較高層級的專案來確保其定義的詳細資訊對於子專案仍然有意義。

  • projA 呼叫 FetchContent_MakeAvailable() 時,projD 列在 projBprojC 之前,因此它將在 projBprojC 之前填充。 projA 不是必須這樣做,這樣做可以確保 projA 完全控制將 projD 引入建置的環境(目錄屬性尤其相關)。

  • 雖然 projAprojE 定義了內容詳細資訊,但它不需要顯式呼叫 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 變數在到達 project() 命令時才使用,此時 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
)