匯入與匯出指南¶
簡介¶
在本指南中,我們將介紹 IMPORTED
目標的概念,並示範如何將磁碟上現有的執行檔或函式庫檔案匯入到 CMake 專案中。然後,我們將展示 CMake 如何支援從一個基於 CMake 的專案匯出目標,並將它們匯入到另一個專案中。最後,我們將示範如何封裝一個帶有組態檔的專案,以便輕鬆整合到其他 CMake 專案中。本指南和完整的範例原始碼可以在 CMake 原始碼樹的 Help/guide/importing-exporting
目錄中找到。
匯入目標¶
IMPORTED
目標用於將 CMake 專案外部的檔案轉換為專案內部的邏輯目標。IMPORTED
目標是使用 add_executable()
和 add_library()
命令的 IMPORTED
選項建立的。不會為 IMPORTED
目標產生建置檔案。一旦匯入,IMPORTED
目標可以像專案中的任何其他目標一樣被引用,並為外部執行檔和函式庫提供方便、彈性的參考。
預設情況下,IMPORTED
目標名稱的作用域在其建立的目錄及其下級目錄中。我們可以使用 GLOBAL
選項來擴展可見性,以便該目標在建置系統中全域可存取。
關於 IMPORTED
目標的詳細資訊,可以通過設定名稱以 IMPORTED_
和 INTERFACE_
開頭的屬性來指定。例如,IMPORTED_LOCATION
包含磁碟上目標的完整路徑。
匯入執行檔¶
首先,我們將逐步完成一個簡單的範例,該範例建立一個 IMPORTED
執行檔目標,然後從 add_custom_command()
命令中引用它。
我們需要進行一些設定才能開始。我們想要建立一個執行檔,執行時會在目前目錄中建立一個基本的 main.cc
檔案。這個專案的細節並不重要。導航到 Help/guide/importing-exporting/MyExe
,建立一個建置目錄,執行 cmake
並建置和安裝專案。
$ cd Help/guide/importing-exporting/MyExe
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build .
$ cmake --install . --prefix <install location>
$ <install location>/myexe
$ ls
[...] main.cc [...]
現在我們可以將這個執行檔匯入到另一個 CMake 專案中。本節的原始碼可在 Help/guide/importing-exporting/Importing
中找到。在 CMakeLists 檔案中,使用 add_executable()
命令建立一個名為 myexe
的新目標。使用 IMPORTED
選項告訴 CMake 這個目標引用一個位於專案外部的執行檔。不會產生建置它的規則,並且 IMPORTED
目標屬性將設定為 true
。
add_executable(myexe IMPORTED)
接下來,使用 set_property()
命令設定目標的 IMPORTED_LOCATION
屬性。這將告訴 CMake 目標在磁碟上的位置。位置可能需要調整為上一步中指定的 <安裝位置>
。
set_property(TARGET myexe PROPERTY
IMPORTED_LOCATION "../InstallMyExe/bin/myexe")
現在我們可以像引用專案中建置的任何目標一樣引用這個 IMPORTED
目標。在這個例子中,假設我們想在專案中使用產生的原始碼檔案。在 add_custom_command()
命令中使用 IMPORTED
目標
add_custom_command(OUTPUT main.cc COMMAND myexe)
由於 COMMAND
指定了一個執行檔目標名稱,它將自動被上面 IMPORTED_LOCATION
屬性給定的執行檔位置替換。
最後,使用 add_custom_command()
的輸出
add_executable(mynewexe main.cc)
匯入函式庫¶
以類似的方式,可以通過 IMPORTED
目標存取來自其他專案的函式庫。
注意:本節範例的完整原始碼未提供,留給讀者作為練習。
在 CMakeLists 檔案中,新增一個 IMPORTED
函式庫並指定其在磁碟上的位置
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY
IMPORTED_LOCATION "/path/to/libfoo.a")
然後在我們的專案中使用 IMPORTED
函式庫
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE foo)
在 Windows 上,可以將 .dll 及其 .lib 匯入函式庫一起匯入
add_library(bar SHARED IMPORTED)
set_property(TARGET bar PROPERTY
IMPORTED_LOCATION "c:/path/to/bar.dll")
set_property(TARGET bar PROPERTY
IMPORTED_IMPLIB "c:/path/to/bar.lib")
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE bar)
具有多個組態的函式庫可以使用單個目標匯入
find_library(math_REL NAMES m)
find_library(math_DBG NAMES md)
add_library(math STATIC IMPORTED GLOBAL)
set_target_properties(math PROPERTIES
IMPORTED_LOCATION "${math_REL}"
IMPORTED_LOCATION_DEBUG "${math_DBG}"
IMPORTED_CONFIGURATIONS "RELEASE;DEBUG"
)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe PRIVATE math)
產生的建置系統將在發布組態中建置時將 myexe
連結到 m.lib
,在偵錯組態中建置時將其連結到 md.lib
。
匯出目標¶
雖然 IMPORTED
目標本身很有用,但它們仍然要求匯入它們的專案知道目標檔案在磁碟上的位置。IMPORTED
目標的真正威力在於,提供目標檔案的專案也提供了一個 CMake 檔案來幫助匯入它們。可以設定一個專案來產生必要的資訊,以便其他 CMake 專案可以輕鬆地使用它,無論是從建置目錄、本地安裝還是封裝時。
在剩餘的章節中,我們將逐步完成一組範例專案。第一個專案將建立和安裝一個函式庫以及相應的 CMake 組態和套件檔案。第二個專案將使用產生的套件。
首先,讓我們看看 Help/guide/importing-exporting/MathFunctions
目錄中的 MathFunctions
專案。這裡我們有一個標頭檔 MathFunctions.h
,它宣告了一個 sqrt
函數
#pragma once
namespace MathFunctions {
double sqrt(double x);
}
以及一個對應的原始碼檔案 MathFunctions.cxx
#include "MathFunctions.h"
#include <cmath>
namespace MathFunctions {
double sqrt(double x)
{
return std::sqrt(x);
}
}
不必太擔心 C++ 檔案的細節,它們只是作為一個簡單的範例,將在許多建置系統上編譯和執行。
現在我們可以為 MathFunctions
專案建立一個 CMakeLists.txt
檔案。首先指定 cmake_minimum_required()
版本和 project()
名稱
cmake_minimum_required(VERSION 3.15)
project(MathFunctions)
# make cache variables for install destinations
include(GNUInstallDirs)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
包含 GNUInstallDirs
模組是為了使專案能夠靈活地安裝到不同的平台佈局中,方法是將目錄作為快取變數提供。
使用 add_library()
命令建立一個名為 MathFunctions
的函式庫
add_library(MathFunctions STATIC MathFunctions.cxx)
然後使用 target_include_directories()
命令指定目標的包含目錄
target_include_directories(MathFunctions
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
我們需要告訴 CMake,我們想要使用不同的包含目錄,具體取決於我們是正在建置函式庫還是從已安裝的位置使用它。如果我們不這樣做,當 CMake 建立匯出資訊時,它將匯出一個特定於當前建置目錄的路徑,並且對於其他專案將無效。我們可以使用 產生器 表達式
來指定,如果我們正在建置函式庫,則包含當前原始碼目錄。否則,當安裝時,包含 include
目錄。有關更多詳細資訊,請參閱 建立可重定位套件 章節。
install(TARGETS)
和 install(EXPORT)
命令協同工作,以安裝目標(在本例中為函式庫)和一個 CMake 檔案,該檔案旨在使其易於將目標匯入到另一個 CMake 專案中。
首先,在 install(TARGETS)
命令中,我們將指定目標、EXPORT
名稱和目標安裝位置的目的地。
install(TARGETS MathFunctions
EXPORT MathFunctionsTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
在這裡,EXPORT
選項告訴 CMake 建立一個名為 MathFunctionsTargets
的匯出。IMPORTED
目標已設定適當的屬性,以定義其 使用需求,例如 INTERFACE_INCLUDE_DIRECTORIES
、INTERFACE_COMPILE_DEFINITIONS
和其他相關的內建 INTERFACE_
屬性。在 COMPATIBLE_INTERFACE_STRING
和其他 相容介面屬性 中列出的使用者定義屬性的 INTERFACE
變體也會傳播到產生的 IMPORTED
目標。例如,在本例中,IMPORTED
目標的 INTERFACE_INCLUDE_DIRECTORIES
屬性將填充 INCLUDES DESTINATION
屬性指定的目錄。由於給出了相對路徑,因此將其視為相對於 CMAKE_INSTALL_PREFIX
。
請注意,我們尚未要求 CMake 安裝匯出。
我們不想忘記使用 install(FILES)
命令安裝 MathFunctions.h
標頭檔。標頭檔應安裝到 target_include_directories()
命令在上面指定的 include
目錄中。
install(FILES MathFunctions.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
現在 MathFunctions
函式庫和標頭檔已安裝,我們還需要明確安裝 MathFunctionsTargets
匯出詳細資訊。使用 install(EXPORT)
命令匯出 install(TARGETS)
命令中定義的 MathFunctionsTargets
中的目標。
install(EXPORT MathFunctionsTargets
FILE MathFunctionsTargets.cmake
NAMESPACE MathFunctions::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)
此命令產生 MathFunctionsTargets.cmake
檔案,並安排將其安裝到 ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
。該檔案包含適合下游使用的程式碼,用於從安裝樹匯入安裝命令中列出的所有目標。
NAMESPACE
選項將 MathFunctions::
前置於寫入匯出檔案的目標名稱。雙冒號的約定給了 CMake 一個提示,表明該名稱是 IMPORTED
目標,當下游專案使用它時。這樣,如果未找到提供它的套件,CMake 可以發出診斷訊息。
產生的匯出檔案包含建立 IMPORTED
函式庫的程式碼。
# Create imported target MathFunctions::MathFunctions
add_library(MathFunctions::MathFunctions STATIC IMPORTED)
set_target_properties(MathFunctions::MathFunctions PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
此程式碼與我們在 匯入函式庫 章節中手動建立的範例非常相似。請注意,${_IMPORT_PREFIX}
是相對於檔案位置計算的。
外部專案可以使用 include()
命令載入此檔案,並從安裝樹引用 MathFunctions
函式庫,就好像它是在自己的樹中建置的一樣。例如
1 include(GNUInstallDirs)
2 include(${INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions/MathFunctionTargets.cmake)
3 add_executable(myexe src1.c src2.c)
4 target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)
第 2 行載入目標 CMake 檔案。雖然我們只匯出了一個目標,但此檔案可以匯入任意數量的目標。它們的位置是相對於檔案位置計算的,以便可以輕鬆移動安裝樹。第 4 行引用匯入的 MathFunctions
函式庫。產生的建置系統將連結到安裝位置的函式庫。
執行檔也可以使用相同的過程匯出和匯入。
任意數量的目標安裝可以與相同的匯出名稱關聯。匯出名稱被認為是全域的,因此任何目錄都可以貢獻目標安裝。install(EXPORT)
命令只需要調用一次即可安裝引用所有目標的檔案。下面是一個範例,說明如何將多個匯出合併到一個匯出檔案中,即使它們位於專案的不同子目錄中。
# A/CMakeLists.txt
add_executable(myexe src1.c)
install(TARGETS myexe DESTINATION lib/myproj
EXPORT myproj-targets)
# B/CMakeLists.txt
add_library(foo STATIC foo1.c)
install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)
# Top CMakeLists.txt
add_subdirectory (A)
add_subdirectory (B)
install(EXPORT myproj-targets DESTINATION lib/myproj)
建立套件¶
至此,MathFunctions
專案正在匯出其他專案使用所需的目標資訊。我們可以透過產生組態檔,使 CMake find_package()
命令可以找到我們的專案,從而使這個專案更容易被其他專案使用。
首先,我們需要在 CMakeLists.txt
檔案中進行一些新增。首先,包含 CMakePackageConfigHelpers
模組,以存取一些用於建立組態檔的輔助函數。
include(CMakePackageConfigHelpers)
然後,我們將建立一個套件組態檔和一個套件版本檔。
建立套件組態檔¶
使用 configure_package_config_file()
命令(由 CMakePackageConfigHelpers
提供)來產生套件組態檔。請注意,應使用此命令而不是普通的 configure_file()
命令。它有助於通過避免在已安裝的組態檔中硬編碼路徑來確保產生的套件是可重定位的。給定 INSTALL_DESTINATION
的路徑必須是將安裝 MathFunctionsConfig.cmake
檔案的目的地。我們將在下一節中檢查套件組態檔的內容。
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)
使用 install(FILES)
命令安裝產生的組態檔。MathFunctionsConfigVersion.cmake
和 MathFunctionsConfig.cmake
都安裝到相同的位置,完成套件的建立。
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)
現在我們需要建立套件組態檔本身。在本例中,Config.cmake.in
檔案非常簡單,但足以讓下游使用 IMPORTED
目標。
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/MathFunctionsTargets.cmake")
check_required_components(MathFunctions)
檔案的第一行僅包含字串 @PACKAGE_INIT@
。這會在檔案配置時展開,並允許使用以 PACKAGE_
為前綴的可重定位路徑。它還提供了 set_and_check()
和 check_required_components()
巨集。
check_required_components
輔助巨集通過檢查所有必需組件的 <Package>_<Component>_FOUND
變數來確保已找到所有請求的非可選組件。即使套件沒有任何組件,也應在套件組態檔的末尾調用此巨集。這樣,CMake 可以確保下游專案沒有指定任何不存在的組件。如果 check_required_components
失敗,則 <Package>_FOUND
變數將設定為 FALSE,並且該套件被視為未找到。
set_and_check()
巨集應在組態檔中使用,而不是普通的 set()
命令來設定目錄和檔案位置。如果引用的檔案或目錄不存在,則巨集將失敗。
如果應由 MathFunctions
套件提供任何巨集,則它們應位於單獨的檔案中,該檔案安裝到與 MathFunctionsConfig.cmake
檔案相同的位置,並從那裡包含。
套件的所有必需依賴項也必須在套件組態檔中找到。 假設我們的專案中需要 Stats
函式庫。在 CMakeLists 檔案中,我們將新增
find_package(Stats 2.6.4 REQUIRED)
target_link_libraries(MathFunctions PUBLIC Stats::Types)
由於 Stats::Types
目標是 MathFunctions
的 PUBLIC
依賴項,因此下游也必須找到 Stats
套件並連結到 Stats::Types
函式庫。應在組態檔中找到 Stats
套件以確保這一點。
include(CMakeFindDependencyMacro)
find_dependency(Stats 2.6.4)
來自 CMakeFindDependencyMacro
模組的 find_dependency
巨集通過傳播套件是否為 REQUIRED
或 QUIET
等來提供幫助。find_dependency
巨集還將 MathFunctions_FOUND
設定為 False
,如果未找到依賴項,則會顯示診斷訊息,指出 MathFunctions
套件在沒有 Stats
套件的情況下無法使用。
練習: 將一個必需的函式庫新增到 MathFunctions
專案。
建立套件版本檔¶
CMakePackageConfigHelpers
模組提供了 write_basic_package_version_file()
命令,用於建立簡單的套件版本檔。當調用 find_package()
時,CMake 會讀取此檔案以確定與請求版本的相容性,並設定一些特定於版本的變數,例如 <PackageName>_VERSION
、<PackageName>_VERSION_MAJOR
、<PackageName>_VERSION_MINOR
等。有關更多詳細資訊,請參閱 cmake-packages
文件。
set(version 3.4.1)
set_property(TARGET MathFunctions PROPERTY VERSION ${version})
set_property(TARGET MathFunctions PROPERTY SOVERSION 3)
set_property(TARGET MathFunctions PROPERTY
INTERFACE_MathFunctions_MAJOR_VERSION 3)
set_property(TARGET MathFunctions APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING MathFunctions_MAJOR_VERSION
)
# generate the version file for the config file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
VERSION "${version}"
COMPATIBILITY AnyNewerVersion
)
在我們的範例中,MathFunctions_MAJOR_VERSION
被定義為 COMPATIBLE_INTERFACE_STRING
,這表示它在任何依賴項的相依性之間必須相容。透過在這個版本和下一個版本的 MathFunctions
中設定這個自訂定義的使用者屬性,如果嘗試將版本 3 與版本 4 一起使用,cmake
將發出診斷訊息。如果套件的不同主要版本設計為不相容,套件可以選擇採用這種模式。
從建置樹匯出目標¶
通常,專案在被外部專案使用之前會先建置和安裝。然而,在某些情況下,希望直接從建置樹匯出目標。然後,外部專案可以使用這些目標,該專案參考建置樹,而無需進行安裝。export()
命令用於產生一個檔案,該檔案從專案建置樹匯出目標。
如果我們希望我們的範例專案也可以從建置目錄中使用,我們只需要在 CMakeLists.txt
中加入以下內容
export(EXPORT MathFunctionsTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/MathFunctionsTargets.cmake"
NAMESPACE MathFunctions::
)
在這裡,我們使用 export()
命令來為建置樹產生匯出目標。在這種情況下,我們將在建置目錄的 cmake
子目錄中建立一個名為 MathFunctionsTargets.cmake
的檔案。產生的檔案包含匯入目標所需的程式碼,並且可以由知道專案建置樹的外部專案載入。這個檔案是建置樹特定的,並且不可重新定位。
可以建立一個合適的套件組態檔案和套件版本檔案,為建置樹定義一個套件,該套件可以在不安裝的情況下使用。建置樹的使用者可以簡單地確保 CMAKE_PREFIX_PATH
包含建置目錄,或者在快取中將 MathFunctions_DIR
設定為 <build_dir>/MathFunctions
。
此功能的一個範例應用是在跨編譯時在主機平台上建置可執行檔。包含可執行檔的專案可以在主機平台上建置,然後為另一個平台進行跨編譯的專案可以載入它。
建置和安裝套件¶
此時,我們已經為我們的專案產生了一個可重新定位的 CMake 組態,該組態可以在專案安裝後使用。讓我們嘗試建置 MathFunctions
專案
mkdir MathFunctions_build
cd MathFunctions_build
cmake ../MathFunctions
cmake --build .
在建置目錄中,請注意檔案 MathFunctionsTargets.cmake
已在 cmake
子目錄中建立。
現在安裝專案
$ cmake --install . --prefix "/home/myuser/installdir"
建立可重新定位的套件¶
由 install(EXPORT)
建立的套件設計為可重新定位,使用相對於套件本身位置的路徑。它們不得參考套件建置所在機器上的絕對路徑,這些路徑在套件可能安裝的機器上將不存在。
在為 EXPORT
定義目標的介面時,請記住,包含目錄應指定為相對於 CMAKE_INSTALL_PREFIX
的相對路徑,但不應明確包含 CMAKE_INSTALL_PREFIX
target_include_directories(tgt INTERFACE
# Wrong, not relocatable:
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/TgtName>
)
target_include_directories(tgt INTERFACE
# Ok, relocatable:
$<INSTALL_INTERFACE:include/TgtName>
)
$<INSTALL_PREFIX>
產生器運算式可以用作安裝前綴的佔位符,而不會導致不可重新定位的套件。如果使用複雜的產生器運算式,這是必要的
target_include_directories(tgt INTERFACE
# Ok, relocatable:
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/TgtName>
)
這也適用於參考外部相依性的路徑。不建議使用可能包含路徑的任何屬性填充,例如 INTERFACE_INCLUDE_DIRECTORIES
或 INTERFACE_LINK_LIBRARIES
,這些路徑與相依性相關。例如,以下程式碼可能不適用於可重新定位的套件
target_link_libraries(MathFunctions INTERFACE
${Foo_LIBRARIES} ${Bar_LIBRARIES}
)
target_include_directories(MathFunctions INTERFACE
"$<INSTALL_INTERFACE:${Foo_INCLUDE_DIRS};${Bar_INCLUDE_DIRS}>"
)
被參考的變數可能包含程式庫和包含目錄的絕對路徑,如同在建立套件的機器上找到的那樣。這將建立一個套件,其中包含硬編碼的相依性路徑,不適合重新定位。
理想情況下,應該透過它們自己的 匯入目標 來使用這些相依性,這些目標具有自己的 IMPORTED_LOCATION
和使用需求屬性,例如 INTERFACE_INCLUDE_DIRECTORIES
已適當填充。然後,這些匯入的目標可以與 target_link_libraries()
命令一起用於 MathFunctions
target_link_libraries(MathFunctions INTERFACE Foo::Foo Bar::Bar)
透過這種方法,套件僅透過 匯入目標 的名稱來參考其外部相依性。當消費者使用已安裝的套件時,消費者將執行適當的 find_package()
命令(透過上面描述的 find_dependency
巨集)來尋找相依性,並在他們自己的機器上使用適當的路徑填充匯入的目標。
使用套件組態檔案¶
現在我們準備好建立一個專案來使用已安裝的 MathFunctions
程式庫。在本節中,我們將使用來自 Help\guide\importing-exporting\Downstream
的原始碼。在這個目錄中,有一個名為 main.cc
的原始檔,它使用 MathFunctions
程式庫來計算給定數字的平方根,然後印出結果
// A simple program that outputs the square root of a number
#include <iostream>
#include <string>
#include "MathFunctions.h"
int main(int argc, char* argv[])
{
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
// convert input to double
double const inputValue = std::stod(argv[1]);
// calculate square root
double const sqrt = MathFunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << sqrt
<< std::endl;
return 0;
}
和之前一樣,我們將從 CMakeLists.txt
檔案中的 cmake_minimum_required()
和 project()
命令開始。對於這個專案,我們還將指定 C++ 標準。
cmake_minimum_required(VERSION 3.15)
project(Downstream)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
我們可以使用 find_package()
命令
find_package(MathFunctions 3.4.1 EXACT)
建立可執行檔
add_executable(myexe main.cc)
並連結到 MathFunctions
程式庫
target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)
就這樣!現在讓我們嘗試建置 Downstream
專案。
mkdir Downstream_build
cd Downstream_build
cmake ../Downstream
cmake --build .
在 CMake 組態期間可能出現警告
CMake Warning at CMakeLists.txt:4 (find_package):
By not providing "FindMathFunctions.cmake" in CMAKE_MODULE_PATH this
project has asked CMake to find a package configuration file provided by
"MathFunctions", but CMake did not find one.
Could not find a package configuration file provided by "MathFunctions"
with any of the following names:
MathFunctionsConfig.cmake
mathfunctions-config.cmake
Add the installation prefix of "MathFunctions" to CMAKE_PREFIX_PATH or set
"MathFunctions_DIR" to a directory containing one of the above files. If
"MathFunctions" provides a separate development package or SDK, be sure it
has been installed.
將 CMAKE_PREFIX_PATH
設定為先前安裝 MathFunctions 的位置,然後重試。確保新建立的可執行檔如預期般執行。
新增組件¶
讓我們編輯 MathFunctions
專案以使用組件。本節的原始碼可以在 Help\guide\importing-exporting\MathFunctionsComponents
中找到。這個專案的 CMakeLists 檔案新增了兩個子目錄:Addition
和 SquareRoot
。
cmake_minimum_required(VERSION 3.15)
project(MathFunctionsComponents)
# make cache variables for install destinations
include(GNUInstallDirs)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_subdirectory(Addition)
add_subdirectory(SquareRoot)
產生並安裝套件組態和套件版本檔案
include(CMakePackageConfigHelpers)
# set version
set(version 3.4.1)
# generate the version file for the config file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
VERSION "${version}"
COMPATIBILITY AnyNewerVersion
)
# create config file
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
# install config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)
如果在下游使用 find_package()
時指定了 COMPONENTS
,它們會列在 <PackageName>_FIND_COMPONENTS
變數中。我們可以使用這個變數來驗證所有必要的組件目標都包含在 Config.cmake.in
中。同時,這個函數將充當自訂的 check_required_components
巨集,以確保下游只嘗試使用支援的組件。
@PACKAGE_INIT@
set(_MathFunctions_supported_components Addition SquareRoot)
foreach(_comp ${MathFunctions_FIND_COMPONENTS})
if (NOT _comp IN_LIST _MathFunctions_supported_components)
set(MathFunctions_FOUND False)
set(MathFunctions_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/MathFunctions${_comp}Targets.cmake")
endforeach()
在這裡,MathFunctions_NOT_FOUND_MESSAGE
被設定為一個診斷訊息,指出由於指定了無效的組件而找不到套件。可以為 _FOUND
變數設定為 False
的任何情況設定這個訊息變數,並且它將顯示給使用者。
Addition
和 SquareRoot
目錄是相似的。讓我們看看其中一個 CMakeLists 檔案
# create library
add_library(SquareRoot STATIC SquareRoot.cxx)
add_library(MathFunctions::SquareRoot ALIAS SquareRoot)
# add include directories
target_include_directories(SquareRoot
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
# install the target and create export-set
install(TARGETS SquareRoot
EXPORT SquareRootTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# install header file
install(FILES SquareRoot.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# generate and install export file
install(EXPORT SquareRootTargets
FILE MathFunctionsSquareRootTargets.cmake
NAMESPACE MathFunctions::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MathFunctions
)
現在我們可以按照前面章節中描述的方式建置專案。為了測試使用這個套件,我們可以使用 Help\guide\importing-exporting\DownstreamComponents
中的專案。與之前的 Downstream
專案相比,有兩個不同之處。首先,我們需要尋找套件組件。將 find_package
行從
find_package(MathFunctions 3.4.1 EXACT)
改為
find_package(MathFunctions 3.4 COMPONENTS Addition SquareRoot)
以及 target_link_libraries
行從
target_link_libraries(myexe PRIVATE MathFunctions::MathFunctions)
改為
target_link_libraries(myexe PRIVATE MathFunctions::Addition MathFunctions::SquareRoot)
在 main.cc
中,將 #include MathFunctions.h
替換為
#include "Addition.h"
#include "SquareRoot.h"
最後,使用 Addition
程式庫
double const sum = MathFunctions::add(inputValue, inputValue);
std::cout << inputValue << " + " << inputValue << " = " << sum << std::endl;
建置 Downstream
專案並確認它可以找到並使用套件組件。