步驟 11:新增匯出組態

在教學的安裝 測試期間,我們新增了讓 CMake 安裝專案的函式庫與標頭檔的功能。在封裝 安裝程式期間,我們新增了將這些資訊封裝起來以便分發給其他人的功能。

下一步是加入必要的資訊,讓其他 CMake 專案可以使用我們的專案,無論是從建置目錄、本機安裝還是封裝時。

第一步是更新我們的install(TARGETS)指令,不僅要指定一個 DESTINATION,還要指定一個 EXPORTEXPORT 關鍵字會產生一個 CMake 檔案,其中包含從安裝樹匯入 install 指令中列出的所有目標的程式碼。因此,讓我們繼續明確地 EXPORT MathFunctions 函式庫,方法是將 MathFunctions/CMakeLists.txt 中的 install 指令更新為如下所示

MathFunctions/CMakeLists.txt
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs}
        EXPORT MathFunctionsTargets
        DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)

既然我們已經匯出 MathFunctions,我們也需要明確地安裝產生的 MathFunctionsTargets.cmake 檔案。這可以透過在最上層 CMakeLists.txt 的底部新增以下內容來完成

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

此時,您應該嘗試執行 CMake。如果一切都設定正確,您將會看到 CMake 會產生一個類似以下的錯誤

Target "MathFunctions" INTERFACE_INCLUDE_DIRECTORIES property contains
path:

  "/Users/robert/Documents/CMakeClass/Tutorial/Step11/MathFunctions"

which is prefixed in the source directory.

CMake 告訴您,在產生匯出資訊時,它將匯出一個與目前機器本質上相關的路徑,並且在其他機器上將無效。解決方案是更新 MathFunctionstarget_include_directories(),以便理解當從建置目錄以及從安裝/封裝中使用時,它需要不同的 INTERFACE 位置。這意味著要將 MathFunctionstarget_include_directories() 呼叫轉換成如下所示

MathFunctions/CMakeLists.txt
target_include_directories(MathFunctions
                           INTERFACE
                            $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
                            $<INSTALL_INTERFACE:include>
                           )

一旦更新完成,我們可以重新執行 CMake 並驗證它不再發出警告。

此時,我們已讓 CMake 正確地封裝所需的目標資訊,但我們仍然需要產生一個 MathFunctionsConfig.cmake,以便 CMake 的 find_package() 指令可以找到我們的專案。因此,讓我們繼續在專案的最上層新增一個名為 Config.cmake.in 的檔案,其內容如下

Config.cmake.in

@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake" )

然後,為了正確配置並安裝該檔案,請將以下內容新增至最上層 CMakeLists.txt 的底部

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)

接下來,我們執行 configure_package_config_file()。此指令會配置提供的檔案,但與標準的 configure_file() 方式有一些特定的差異。為了正確使用此函式,輸入檔案除了所需的內容之外,還應該有一行文字 @PACKAGE_INIT@。該變數將被替換為一段程式碼,該程式碼將設定的值轉換為相對路徑。這些新值可以使用相同的名稱來參考,但要在前面加上 PACKAGE_ 字首。

CMakeLists.txt
install(EXPORT MathFunctionsTargets
  FILE MathFunctionsTargets.cmake
  DESTINATION lib/cmake/MathFunctions
)

include(CMakePackageConfigHelpers)
# generate the config file that includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
  INSTALL_DESTINATION "lib/cmake/MathFunctions"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

接下來是 write_basic_package_version_file()。此指令會寫入一個檔案,該檔案由 find_package() 使用,用於記錄所需套件的版本和相容性。在這裡,我們使用 Tutorial_VERSION_* 變數,並表示它與 AnyNewerVersion 相容,這表示此版本或任何更高版本都與請求的版本相容。

CMakeLists.txt
write_basic_package_version_file(
  "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
  VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
  COMPATIBILITY AnyNewerVersion
)

最後,將兩個產生的檔案設定為要安裝

CMakeLists.txt
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake
  DESTINATION lib/cmake/MathFunctions
  )

此時,我們已經為我們的專案產生了一個可重新定位的 CMake 組態,該組態可以在專案安裝或封裝後使用。如果我們希望我們的專案也可以從建置目錄中使用,我們只需將以下內容新增至最上層 CMakeLists.txt 的底部

CMakeLists.txt
export(EXPORT MathFunctionsTargets
  FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)

透過這個匯出呼叫,我們現在產生一個 MathFunctionsTargets.cmake,允許其他專案使用建置目錄中已設定的 MathFunctionsConfig.cmake,而無需安裝。