cmake-buildsystem(7)¶
簡介¶
基於 CMake 的建置系統組織為一組高階邏輯目標。每個目標對應於一個執行檔或函式庫,或是一個包含自訂命令的自訂目標。目標之間的相依性在建置系統中表達,以決定建置順序以及響應變更的重新生成規則。
二進制目標¶
執行檔和函式庫使用 add_executable()
和 add_library()
命令定義。產生的二進制檔案具有適合目標平台的 PREFIX
、SUFFIX
和副檔名。二進制目標之間的相依性使用 target_link_libraries()
命令表達
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
archive
定義為 STATIC
函式庫 -- 一個包含從 archive.cpp
、zip.cpp
和 lzma.cpp
編譯的物件的封存檔。zipapp
定義為透過編譯和連結 zipapp.cpp
所形成的執行檔。當連結 zipapp
執行檔時,會連結 archive
靜態函式庫。
執行檔¶
執行檔是透過將物件檔案連結在一起而建立的二進制檔案,其中一個物件檔案包含程式進入點,例如 main
。
add_executable()
命令定義一個執行檔目標
add_executable(mytool mytool.cpp)
CMake 產生建置規則,將原始碼檔案編譯成物件檔案,並將它們連結成一個執行檔。
執行檔的連結相依性可以使用 target_link_libraries()
命令指定。連結器從從執行檔自身的原始碼檔案編譯的物件檔案開始,然後透過搜尋連結的函式庫來解析剩餘的符號相依性。
諸如 add_custom_command()
之類的命令,會產生在建置時執行的規則,可以透明地使用 EXECUTABLE
目標作為 COMMAND
執行檔。建置系統規則將確保執行檔在嘗試執行命令之前被建置。
靜態函式庫¶
靜態函式庫是物件檔案的封存檔。它們是由封存器而不是連結器產生的。執行檔、共享函式庫 和 模組函式庫 可以連結到靜態函式庫作為相依性。連結器根據需要從靜態函式庫中選擇物件檔案的子集,以解析符號並將其連結到消耗二進制檔案中。每個連結到靜態函式庫的二進制檔案都會獲得其自己的符號副本,而靜態函式庫本身在執行時期不需要。
add_library()
命令在以 STATIC
函式庫類型呼叫時,定義一個靜態函式庫目標
add_library(archive STATIC archive.cpp zip.cpp lzma.cpp)
或者,當 BUILD_SHARED_LIBS
變數為 false 時,不使用類型
add_library(archive archive.cpp zip.cpp lzma.cpp)
CMake 產生建置規則,將原始碼檔案編譯成物件檔案,並將它們封存到一個靜態函式庫中。
靜態函式庫的連結相依性可以使用 target_link_libraries()
命令指定。由於靜態函式庫是封存檔而不是連結的二進制檔案,因此它們的連結相依性的物件檔案不包含在函式庫本身中(除了指定為直接連結相依性的 物件函式庫 之外)。相反,CMake 會記錄靜態函式庫的連結相依性,以便在連結消耗二進制檔案時可傳遞地使用。
Apple Frameworks¶
共享函式庫 和 靜態函式庫 可以使用 FRAMEWORK
目標屬性標記,以建立 macOS 或 iOS Framework。具有 FRAMEWORK
目標屬性的函式庫也應設定 FRAMEWORK_VERSION
目標屬性。按照 macOS 慣例,此屬性通常設定為值 "A"。MACOSX_FRAMEWORK_IDENTIFIER
設定 CFBundleIdentifier
鍵,並且它唯一地識別 bundle。
add_library(MyFramework SHARED MyFramework.cpp)
set_target_properties(MyFramework PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A # Version "A" is macOS convention
MACOSX_FRAMEWORK_IDENTIFIER org.cmake.MyFramework
)
模組函式庫¶
模組函式庫是透過將物件檔案連結在一起而建立的二進制檔案。與 共享函式庫 不同,模組函式庫可能不會被其他二進制檔案作為相依性連結 -- 不要在 target_link_libraries()
命令的右手邊命名它們。相反,模組函式庫是應用程式可以在執行時期按需動態載入的插件,例如,透過 dlopen
。
add_library()
命令在以 MODULE
函式庫類型呼叫時,定義一個模組函式庫目標
add_library(archivePlugin MODULE 7z.cpp)
CMake 產生建置規則,將原始碼檔案編譯成物件檔案,並將它們連結成一個模組函式庫。
模組函式庫的連結相依性可以使用 target_link_libraries()
命令指定。連結器從從模組函式庫自身的原始碼檔案編譯的物件檔案開始,然後透過搜尋連結的函式庫來解析剩餘的符號相依性。
物件函式庫¶
物件函式庫是透過編譯原始碼檔案而建立的物件檔案的集合,沒有任何封存或連結。物件檔案可以在連結 執行檔、共享函式庫 和 模組函式庫 時使用,或者在封存 靜態函式庫 時使用。
add_library()
命令在以 OBJECT
函式庫類型呼叫時,定義一個物件函式庫目標
add_library(archiveObjs OBJECT archive.cpp zip.cpp lzma.cpp)
CMake 產生建置規則,將原始碼檔案編譯成物件檔案。
其他目標可以透過使用 產生器表達式
語法 $<TARGET_OBJECTS:name>
將物件檔案指定為原始碼輸入
add_library(archiveExtras STATIC $<TARGET_OBJECTS:archiveObjs> extras.cpp)
add_executable(test_exe $<TARGET_OBJECTS:archiveObjs> test.cpp)
消耗目標使用來自它們自身原始碼和來自命名的物件函式庫的物件檔案進行連結(或封存)。
或者,物件函式庫可以指定為其他目標的連結相依性
add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archiveObjs)
add_executable(test_exe test.cpp)
target_link_libraries(test_exe archiveObjs)
消耗目標使用來自它們自身原始碼和來自透過 target_link_libraries()
指定為直接連結相依性的物件函式庫的物件檔案進行連結(或封存)。請參閱 連結物件函式庫。
物件函式庫不能在 add_custom_command(TARGET)
命令簽章的使用中作為 TARGET
使用。但是,物件列表可以被 add_custom_command(OUTPUT)
或 file(GENERATE)
透過使用 $<TARGET_OBJECTS:objlib>
來使用。
建置規範和使用需求¶
目標根據它們自己的 建置規範 以及從它們的連結相依性傳播的 使用需求 進行建置。兩者都可以使用目標特定的 命令 來指定。
例如
add_library(archive SHARED archive.cpp zip.cpp)
if (LZMA_FOUND)
# Add a source implementing support for lzma.
target_sources(archive PRIVATE lzma.cpp)
# Compile the 'archive' library sources with '-DBUILDING_WITH_LZMA'.
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA)
endif()
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
add_executable(consumer consumer.cpp)
# Link 'consumer' to 'archive'. This also consumes its usage requirements,
# so 'consumer.cpp' is compiled with '-DUSING_ARCHIVE_LIB'.
target_link_libraries(consumer archive)
目標命令¶
目標特定命令填充 二進制目標 的 建置規範 和 二進制目標、介面函式庫 和 匯入目標 的 使用需求。
調用必須指定範圍關鍵字,每個關鍵字都會影響其後參數的能見度。範圍如下:
命令如下:
target_compile_definitions()
填充
COMPILE_DEFINITIONS
建置規範和INTERFACE_COMPILE_DEFINITIONS
使用需求屬性。例如,呼叫
target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA INTERFACE USING_ARCHIVE_LIB )
將
BUILDING_WITH_LZMA
附加到目標的COMPILE_DEFINITIONS
屬性,並將USING_ARCHIVE_LIB
附加到目標的INTERFACE_COMPILE_DEFINITIONS
屬性。target_compile_options()
填充
COMPILE_OPTIONS
建置規範和INTERFACE_COMPILE_OPTIONS
使用需求屬性。target_compile_features()
在版本 3.1 中新增。
填充
COMPILE_FEATURES
建置規範和INTERFACE_COMPILE_FEATURES
使用需求屬性。target_include_directories()
填充
INCLUDE_DIRECTORIES
建置規範和INTERFACE_INCLUDE_DIRECTORIES
使用需求屬性。使用SYSTEM
選項,它還填充INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
使用需求。為了方便起見,可以啟用
CMAKE_INCLUDE_CURRENT_DIR
變數,以將原始碼目錄和對應的建置目錄作為所有目標上的INCLUDE_DIRECTORIES
添加。同樣地,可以啟用CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
變數,以將它們作為所有目標上的INTERFACE_INCLUDE_DIRECTORIES
添加。target_sources()
在版本 3.1 中新增。
填充
SOURCES
建置規範和INTERFACE_SOURCES
使用需求屬性。它還支援指定 檔案集,它可以添加 C++ 模組原始碼和未在
SOURCES
和INTERFACE_SOURCES
屬性中列出的標頭。檔案集也可以使用包含標頭的包含目錄來填充INCLUDE_DIRECTORIES
建置規範和INTERFACE_INCLUDE_DIRECTORIES
使用需求屬性。target_precompile_headers()
在版本 3.16 中新增。
填充
PRECOMPILE_HEADERS
建置規範和INTERFACE_PRECOMPILE_HEADERS
使用需求屬性。target_link_libraries()
填充
LINK_LIBRARIES
建置規範和INTERFACE_LINK_LIBRARIES
使用需求屬性。這是連結相依性及其 使用需求 被可傳遞地傳播以影響目標的編譯和連結的主要機制。
target_link_directories()
在版本 3.13 中新增。
填充
LINK_DIRECTORIES
建置規範和INTERFACE_LINK_DIRECTORIES
使用需求屬性。target_link_options()
在版本 3.13 中新增。
填充
LINK_OPTIONS
建置規範和INTERFACE_LINK_OPTIONS
使用需求屬性。
目標建置規範¶
二進制目標 的建置規範由目標屬性表示。對於以下每個 編譯 和 連結 屬性,目標的編譯和連結都受到其自身值以及對應的 使用需求 屬性(以 INTERFACE_
前綴命名)的影響,這些屬性從連結相依性的可傳遞閉包中收集。
目標編譯屬性¶
這些代表用於編譯目標的 建置規範。
COMPILE_DEFINITIONS
用於編譯目標中原始碼的編譯定義列表。這些以未指定的順序透過
-D
標誌或等效標誌傳遞給編譯器。DEFINE_SYMBOL
目標屬性也用作編譯定義,作為SHARED
和MODULE
函式庫目標的特殊便利情況。COMPILE_OPTIONS
用於編譯目標中原始碼的編譯選項列表。這些以出現的順序作為標誌傳遞給編譯器。
編譯選項會自動為 shell 逸出。
某些編譯選項最好透過專用設定來指定,例如
POSITION_INDEPENDENT_CODE
目標屬性。COMPILE_FEATURES
在版本 3.1 中新增。
編譯目標中原始碼所需的
編譯功能
列表。通常,這些確保目標的原始碼使用足夠的語言標準級別進行編譯。INCLUDE_DIRECTORIES
用於編譯目標中原始碼的包含目錄列表。這些以出現的順序透過
-I
或-isystem
標誌或等效標誌傳遞給編譯器。為了方便起見,可以啟用
CMAKE_INCLUDE_CURRENT_DIR
變數,以將原始碼目錄和對應的建置目錄作為所有目標上的INCLUDE_DIRECTORIES
新增。SOURCES
與目標相關聯的原始碼檔案列表。 這包括在透過
add_executable()
、add_library()
或add_custom_target()
命令建立目標時指定的原始碼。 它也包括透過target_sources()
命令新增的原始碼,但不包括 檔案集合。PRECOMPILE_HEADERS
在版本 3.16 中新增。
要在目標中編譯原始碼時預先編譯和包含的標頭檔列表。
AUTOMOC_MACRO_NAMES
在版本 3.10 中新增。
AUTOMOC
使用的巨集名稱列表,用於判斷目標中的 C++ 原始碼是否需要由moc
處理。AUTOUIC_OPTIONS
在版本 3.0 中新增。
AUTOUIC
在為目標調用uic
時使用的選項列表。
目標連結屬性¶
這些代表用於連結目標的建置規格。
LINK_LIBRARIES
用於連結目標的連結庫列表(如果目標是可執行檔、共享庫或模組庫)。 靜態庫和共享庫的條目會透過其連結產物的路徑,或使用
-l
標誌或等效項傳遞給連結器。物件庫的條目會透過其物件檔案的路徑傳遞給連結器。此外,對於編譯和連結目標本身,使用需求會從命名靜態庫、共享庫、介面庫、物件庫和匯入目標的
LINK_LIBRARIES
條目傳播,並在其INTERFACE_LINK_LIBRARIES
屬性的遞移閉包中收集。LINK_DIRECTORIES
在版本 3.13 中新增。
用於連結目標的連結目錄列表(如果目標是可執行檔、共享庫或模組庫)。 目錄會使用
-L
標誌或等效項傳遞給連結器。LINK_OPTIONS
在版本 3.13 中新增。
用於連結目標的連結選項列表(如果目標是可執行檔、共享庫或模組庫)。 選項會以標誌形式,按照出現順序傳遞給連結器。
連結選項會自動為 Shell 逸出。
LINK_DEPENDS
連結目標所依賴的檔案列表(如果目標是可執行檔、共享庫或模組庫)。 例如,透過
LINK_OPTIONS
指定的連結器腳本可能會在此處列出,以便變更它們會導致二進位檔再次連結。
目標使用需求¶
目標的使用需求是傳播給消費者的設定,消費者透過 target_link_libraries()
連結到目標,以便正確地與其編譯和連結。 它們由可遞移的編譯和連結屬性表示。
請注意,使用需求並非旨在讓下游僅為了方便而使用特定的 COMPILE_OPTIONS
、COMPILE_DEFINITIONS
等等。 屬性的內容必須是需求,而不僅僅是建議。
請參閱 cmake-packages(7)
手冊的建立可重新定位的套件章節,以了解在建立用於重新發佈的套件時,指定使用需求時必須格外注意的事項。
目標的使用需求可以遞移地傳播給依賴項。 target_link_libraries()
命令具有 PRIVATE
、INTERFACE
和 PUBLIC
關鍵字,以控制傳播。
add_library(archive archive.cpp)
target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
add_library(serialization serialization.cpp)
target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB)
add_library(archiveExtras extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)
target_link_libraries(archiveExtras PRIVATE serialization)
# archiveExtras is compiled with -DUSING_ARCHIVE_LIB
# and -DUSING_SERIALIZATION_LIB
add_executable(consumer consumer.cpp)
# consumer is compiled with -DUSING_ARCHIVE_LIB
target_link_libraries(consumer archiveExtras)
由於 archive
是 archiveExtras
的 PUBLIC
依賴項,因此它的使用需求也會傳播到 consumer
。
由於 serialization
是 archiveExtras
的 PRIVATE
依賴項,因此它的使用需求不會傳播到 consumer
。
一般來說,如果依賴項僅由程式庫的實作使用,而不在標頭檔中使用,則應在使用 PRIVATE
關鍵字的 target_link_libraries()
中指定依賴項。 如果依賴項也在程式庫的標頭檔中使用(例如用於類別繼承),則應將其指定為 PUBLIC
依賴項。 僅由標頭檔使用,而程式庫的實作不使用的依賴項應指定為 INTERFACE
依賴項。 可以使用每個關鍵字的多個用法調用 target_link_libraries()
命令
target_link_libraries(archiveExtras
PUBLIC archive
PRIVATE serialization
)
使用需求透過從依賴項讀取目標屬性的 INTERFACE_
變體,並將值附加到運算元的非 INTERFACE_
變體來傳播。 例如,將讀取依賴項的 INTERFACE_INCLUDE_DIRECTORIES
,並將其附加到運算元的 INCLUDE_DIRECTORIES
。 如果順序相關且已維護,且 target_link_libraries()
呼叫產生的順序不允許正確編譯,則直接使用適當的命令設定屬性可能會更新順序。
例如,如果目標的連結庫必須以 lib1
lib2
lib3
的順序指定,但包含目錄必須以 lib3
lib1
lib2
的順序指定
target_link_libraries(myExe lib1 lib2 lib3)
target_include_directories(myExe
PRIVATE $<TARGET_PROPERTY:lib3,INTERFACE_INCLUDE_DIRECTORIES>)
請注意,在為將使用 install(EXPORT)
命令匯出的安裝目標指定使用需求時,必須格外小心。 請參閱建立套件以取得更多資訊。
可遞移的編譯屬性¶
這些代表消費者編譯的使用需求。
INTERFACE_COMPILE_DEFINITIONS
用於編譯目標消費者中原始碼的編譯定義列表。 通常,這些由目標的標頭檔使用。
INTERFACE_COMPILE_OPTIONS
用於編譯目標消費者中原始碼的編譯選項列表。
INTERFACE_COMPILE_FEATURES
在版本 3.1 中新增。
編譯目標消費者中原始碼所需的編譯功能列表。 通常,這些功能可確保在使用足夠的語言標準層級編譯消費者時處理目標的標頭檔。
INTERFACE_INCLUDE_DIRECTORIES
用於編譯目標消費者中原始碼的包含目錄列表。 通常,這些是目標標頭檔的位置。
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
當指定為包含目錄時(例如,透過
INCLUDE_DIRECTORIES
或INTERFACE_INCLUDE_DIRECTORIES
),應在編譯目標消費者中的原始碼時視為「系統」包含目錄的目錄列表。INTERFACE_SOURCES
要與目標消費者關聯的原始碼檔案列表。
INTERFACE_PRECOMPILE_HEADERS
在版本 3.16 中新增。
要在編譯目標消費者中的原始碼時預先編譯和包含的標頭檔列表。
INTERFACE_AUTOMOC_MACRO_NAMES
在版本 3.27 中新增。
AUTOMOC
使用的巨集名稱列表,用於判斷目標消費者中的 C++ 原始碼是否需要由moc
處理。INTERFACE_AUTOUIC_OPTIONS
在版本 3.0 中新增。
AUTOUIC
在為目標消費者調用uic
時使用的選項列表。
可遞移的連結屬性¶
這些代表連結消費者的使用需求。
INTERFACE_LINK_LIBRARIES
用於連結目標消費者的連結庫列表,適用於可執行檔、共享庫或模組庫。 這些是目標的可遞移依賴項。
此外,對於編譯和連結目標消費者,使用需求會從命名靜態庫、共享庫、介面庫、物件庫和匯入目標的
INTERFACE_LINK_LIBRARIES
條目的遞移閉包中收集,INTERFACE_LINK_DIRECTORIES
在版本 3.13 中新增。
用於連結目標消費者的連結目錄列表,適用於可執行檔、共享庫或模組庫。
INTERFACE_LINK_OPTIONS
在版本 3.13 中新增。
用於連結目標消費者的連結選項列表,適用於可執行檔、共享庫或模組庫。
INTERFACE_LINK_DEPENDS
在版本 3.13 中新增。
連結目標消費者所依賴的檔案列表,適用於可執行檔、共享庫或模組庫。
自訂可遞移屬性¶
在版本 3.30 中新增。
TARGET_PROPERTY
產生器表達式會評估上述建置規格和使用需求屬性作為內建的可遞移屬性。 它也支援由目標及其連結依賴項上的 TRANSITIVE_COMPILE_PROPERTIES
和 TRANSITIVE_LINK_PROPERTIES
屬性定義的自訂可遞移屬性。
例如
add_library(example INTERFACE)
set_target_properties(example PROPERTIES
TRANSITIVE_COMPILE_PROPERTIES "CUSTOM_C"
TRANSITIVE_LINK_PROPERTIES "CUSTOM_L"
INTERFACE_CUSTOM_C "EXAMPLE_CUSTOM_C"
INTERFACE_CUSTOM_L "EXAMPLE_CUSTOM_L"
)
add_library(mylib STATIC mylib.c)
target_link_libraries(mylib PRIVATE example)
set_target_properties(mylib PROPERTIES
CUSTOM_C "MYLIB_PRIVATE_CUSTOM_C"
CUSTOM_L "MYLIB_PRIVATE_CUSTOM_L"
INTERFACE_CUSTOM_C "MYLIB_IFACE_CUSTOM_C"
INTERFACE_CUSTOM_L "MYLIB_IFACE_CUSTOM_L"
)
add_executable(myexe myexe.c)
target_link_libraries(myexe PRIVATE mylib)
set_target_properties(myexe PROPERTIES
CUSTOM_C "MYEXE_CUSTOM_C"
CUSTOM_L "MYEXE_CUSTOM_L"
)
add_custom_target(print ALL VERBATIM
COMMAND ${CMAKE_COMMAND} -E echo
# Prints "MYLIB_PRIVATE_CUSTOM_C;EXAMPLE_CUSTOM_C"
"$<TARGET_PROPERTY:mylib,CUSTOM_C>"
# Prints "MYLIB_PRIVATE_CUSTOM_L;EXAMPLE_CUSTOM_L"
"$<TARGET_PROPERTY:mylib,CUSTOM_L>"
# Prints "MYEXE_CUSTOM_C"
"$<TARGET_PROPERTY:myexe,CUSTOM_C>"
# Prints "MYEXE_CUSTOM_L;MYLIB_IFACE_CUSTOM_L;EXAMPLE_CUSTOM_L"
"$<TARGET_PROPERTY:myexe,CUSTOM_L>"
)
相容的介面屬性¶
某些目標屬性需要在目標與每個依賴項的介面之間相容。 例如,POSITION_INDEPENDENT_CODE
目標屬性可以指定一個布林值,指示目標是否應編譯為位置無關程式碼,這會產生平台特定的後果。 目標也可以指定使用需求 INTERFACE_POSITION_INDEPENDENT_CODE
,以傳達消費者必須編譯為位置無關程式碼。
add_executable(exe1 exe1.cpp)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE ON)
add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1)
在這裡,exe1
和 exe2
都將編譯為位置無關程式碼。 lib1
也將編譯為位置無關程式碼,因為這是 SHARED
程式庫的預設設定。 如果依賴項具有衝突、不相容的需求,cmake(1)
會發出診斷訊息
add_library(lib1 SHARED lib1.cpp)
set_property(TARGET lib1 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(lib2 SHARED lib2.cpp)
set_property(TARGET lib2 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)
set_property(TARGET exe1 PROPERTY POSITION_INDEPENDENT_CODE OFF)
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1 lib2)
lib1
需求 INTERFACE_POSITION_INDEPENDENT_CODE
與 exe1
目標的 POSITION_INDEPENDENT_CODE
屬性「不相容」。 程式庫要求消費者建置為位置無關程式碼,而可執行檔指定不建置為位置無關程式碼,因此會發出診斷訊息。
lib1
和 lib2
需求「不相容」。 其中一個要求消費者建置為位置無關程式碼,而另一個要求消費者不建置為位置無關程式碼。 由於 exe2
連結到兩者,且它們之間存在衝突,因此會發出 CMake 錯誤訊息
CMake Error: The INTERFACE_POSITION_INDEPENDENT_CODE property of "lib2" does
not agree with the value of POSITION_INDEPENDENT_CODE already determined
for "exe2".
為了「相容」,POSITION_INDEPENDENT_CODE
屬性(如果已設定)在布林意義上,必須與設定該屬性的所有遞移指定依賴項的 INTERFACE_POSITION_INDEPENDENT_CODE
屬性相同。
可以透過在 COMPATIBLE_INTERFACE_BOOL
目標屬性的內容中指定屬性,將「相容的介面需求」的此屬性擴展到其他屬性。 每個指定的屬性在消費目標與每個依賴項中帶有 INTERFACE_
前綴的對應屬性之間必須相容
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CUSTOM_PROP ON)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_BOOL CUSTOM_PROP
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CUSTOM_PROP OFF)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # CUSTOM_PROP will be ON
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
非布林屬性也可以參與「相容的介面」計算。 在 COMPATIBLE_INTERFACE_STRING
屬性中指定的屬性在所有遞移指定的依賴項中必須未指定或比較為相同的字串。 這對於確保多個不相容版本的程式庫不會透過目標的可遞移需求連結在一起非常有用
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_LIB_VERSION 2)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_STRING LIB_VERSION
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_LIB_VERSION 3)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2) # LIB_VERSION will be "2"
add_executable(exe2 exe2.cpp)
target_link_libraries(exe2 lib1Version2 lib1Version3) # Diagnostic
COMPATIBLE_INTERFACE_NUMBER_MAX
目標屬性指定內容將以數值方式評估,並計算所有指定項中的最大數字
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)
add_library(lib1Version3 SHARED lib1_v3.cpp)
set_property(TARGET lib1Version3 PROPERTY INTERFACE_CONTAINER_SIZE_REQUIRED 1000)
add_executable(exe1 exe1.cpp)
# CONTAINER_SIZE_REQUIRED will be "200"
target_link_libraries(exe1 lib1Version2)
add_executable(exe2 exe2.cpp)
# CONTAINER_SIZE_REQUIRED will be "1000"
target_link_libraries(exe2 lib1Version2 lib1Version3)
同樣地,COMPATIBLE_INTERFACE_NUMBER_MIN
可用於計算依賴項屬性的數值最小值。
可以在產生時使用產生器表達式在消費者中讀取每個計算出的「相容」屬性值。
請注意,對於每個被依賴者,每個相容的介面屬性中指定的屬性集不得與任何其他屬性中指定的集合相交。
屬性來源偵錯¶
由於建置規格可以由依賴項決定,因此建立目標的程式碼與負責設定建置規格的程式碼缺乏局部性可能會使程式碼更難以推理。 cmake(1)
提供偵錯工具,以列印可能由依賴項決定的屬性內容的來源。 可以在 CMAKE_DEBUG_TARGET_PROPERTIES
變數文件中找到可以偵錯的屬性列表
set(CMAKE_DEBUG_TARGET_PROPERTIES
INCLUDE_DIRECTORIES
COMPILE_DEFINITIONS
POSITION_INDEPENDENT_CODE
CONTAINER_SIZE_REQUIRED
LIB_VERSION
)
add_executable(exe1 exe1.cpp)
在 COMPATIBLE_INTERFACE_BOOL
或 COMPATIBLE_INTERFACE_STRING
中列出的屬性的情況下,偵錯輸出會顯示哪個目標負責設定屬性,以及哪些其他依賴項也定義了該屬性。 在 COMPATIBLE_INTERFACE_NUMBER_MAX
和 COMPATIBLE_INTERFACE_NUMBER_MIN
的情況下,偵錯輸出會顯示來自每個依賴項的屬性值,以及該值是否決定了新的極值。
使用產生器表達式的建置規格¶
建置規格可以使用產生器表達式,其中包含可能是有條件的或僅在產生時才知道的內容。 例如,可以使用 TARGET_PROPERTY
表達式讀取屬性的計算出的「相容」值
add_library(lib1Version2 SHARED lib1_v2.cpp)
set_property(TARGET lib1Version2 PROPERTY
INTERFACE_CONTAINER_SIZE_REQUIRED 200)
set_property(TARGET lib1Version2 APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CONTAINER_SIZE_REQUIRED
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1Version2)
target_compile_definitions(exe1 PRIVATE
CONTAINER_SIZE=$<TARGET_PROPERTY:CONTAINER_SIZE_REQUIRED>
)
在這種情況下,exe1
原始碼檔案將使用 -DCONTAINER_SIZE=200
進行編譯。
一元 TARGET_PROPERTY
產生器表達式和 TARGET_POLICY
產生器表達式會使用消費目標上下文進行評估。 這表示使用需求規格可能會根據消費者而以不同的方式評估
add_library(lib1 lib1.cpp)
target_compile_definitions(lib1 INTERFACE
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:LIB1_WITH_EXE>
$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>:LIB1_WITH_SHARED_LIB>
$<$<TARGET_POLICY:CMP0182>:CONSUMER_CMP0182_NEW>
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1)
cmake_policy(SET CMP0182 NEW)
add_library(shared_lib shared_lib.cpp)
target_link_libraries(shared_lib lib1)
exe1
可執行檔將使用 -DLIB1_WITH_EXE
進行編譯,而 shared_lib
共享庫將使用 -DLIB1_WITH_SHARED_LIB
和 -DCONSUMER_CMP0182_NEW
進行編譯,因為在建立 shared_lib
目標時,政策 CMP0182
為 NEW
。
BUILD_INTERFACE
表達式包裝了僅在從同一建置系統中的目標使用時,或從使用 export()
命令匯出到建置目錄的目標使用時使用的需求。 INSTALL_INTERFACE
表達式包裝了僅在從已安裝並使用 install(EXPORT)
命令匯出的目標使用時使用的需求
add_library(ClimbingStats climbingstats.cpp)
target_compile_definitions(ClimbingStats INTERFACE
$<BUILD_INTERFACE:ClimbingStats_FROM_BUILD_LOCATION>
$<INSTALL_INTERFACE:ClimbingStats_FROM_INSTALLED_LOCATION>
)
install(TARGETS ClimbingStats EXPORT libExport ${InstallArgs})
install(EXPORT libExport NAMESPACE Upstream::
DESTINATION lib/cmake/ClimbingStats)
export(EXPORT libExport NAMESPACE Upstream::)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 ClimbingStats)
在這種情況下,exe1
可執行檔將使用 -DClimbingStats_FROM_BUILD_LOCATION
進行編譯。 匯出命令產生省略了 INSTALL_INTERFACE
或 BUILD_INTERFACE
的 IMPORTED
目標,並剝離了 *_INTERFACE
標記。 消費 ClimbingStats
套件的單獨專案將包含
find_package(ClimbingStats REQUIRED)
add_executable(Downstream main.cpp)
target_link_libraries(Downstream Upstream::ClimbingStats)
取決於 ClimbingStats
套件是從建置位置還是安裝位置使用,Downstream
目標將會使用 -DClimbingStats_FROM_BUILD_LOCATION
或 -DClimbingStats_FROM_INSTALL_LOCATION
進行編譯。有關套件和匯出的更多資訊,請參閱 cmake-packages(7)
手冊。
包含目錄和使用需求¶
當包含目錄指定為使用需求,以及與產生器運算式一起使用時,需要一些特別的考量。target_include_directories()
命令接受相對和絕對包含目錄
add_library(lib1 lib1.cpp)
target_include_directories(lib1 PRIVATE
/absolute/path
relative/path
)
相對路徑會根據命令出現的來源目錄進行解譯。在 INTERFACE_INCLUDE_DIRECTORIES
的 IMPORTED
目標中不允許使用相對路徑。
在使用了非平凡產生器運算式的情況下,INSTALL_PREFIX
運算式可以用於 INSTALL_INTERFACE
運算式的引數中。它是一個替換標記,當被消費專案匯入時,會展開為安裝前綴。
包含目錄使用需求通常在建置樹和安裝樹之間有所不同。BUILD_INTERFACE
和 INSTALL_INTERFACE
產生器運算式可用於根據使用位置描述不同的使用需求。相對路徑允許在 INSTALL_INTERFACE
運算式中使用,並根據安裝前綴進行解譯。例如
add_library(ClimbingStats climbingstats.cpp)
target_include_directories(ClimbingStats INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
$<INSTALL_INTERFACE:/absolute/path>
$<INSTALL_INTERFACE:relative/path>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/$<CONFIG>/generated>
)
提供了兩個與包含目錄使用需求相關的便捷 API。CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
變數可以啟用,效果等同於
set_property(TARGET tgt APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}>
)
對於每個受影響的目標。已安裝目標的便利之處在於 install(TARGETS)
命令的 INCLUDES DESTINATION
组件
install(TARGETS foo bar bat EXPORT tgts ${dest_args}
INCLUDES DESTINATION include
)
install(EXPORT tgts ${other_args})
install(FILES ${headers} DESTINATION include)
這等同於將 ${CMAKE_INSTALL_PREFIX}/include
附加到每個由 install(EXPORT)
生成的已安裝 IMPORTED
目標的 INTERFACE_INCLUDE_DIRECTORIES
。
當使用 匯入目標 的 INTERFACE_INCLUDE_DIRECTORIES
時,屬性中的條目可能會被視為系統包含目錄。其效果取決於工具鏈,但一個常見的效果是忽略在這些目錄中找到的標頭檔的編譯器警告。已安裝目標的 SYSTEM
屬性決定了此行為(請參閱 EXPORT_NO_SYSTEM
屬性,以了解如何修改目標的已安裝值)。也可以通過在消費者上設定 NO_SYSTEM_FROM_IMPORTED
目標屬性,來更改消費者如何解釋已消費的匯入目標的系統行為。
如果二進制目標以遞移方式連結到 macOS FRAMEWORK
,則框架的 Headers
目錄也會被視為使用需求。這具有與將框架目錄作為包含目錄傳遞相同的效果。
連結庫和產生器運算式¶
與建置規範類似,link libraries
可以使用產生器運算式條件來指定。但是,由於使用需求的消費是基於從連結的依賴項中收集而來的,因此還有一個額外的限制,即連結依賴項必須形成“有向無環圖”。也就是說,如果連結到目標取決於目標屬性的值,則該目標屬性可能不依賴於連結的依賴項
add_library(lib1 lib1.cpp)
add_library(lib2 lib2.cpp)
target_link_libraries(lib1 PUBLIC
$<$<TARGET_PROPERTY:POSITION_INDEPENDENT_CODE>:lib2>
)
add_library(lib3 lib3.cpp)
set_property(TARGET lib3 PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 lib1 lib3)
由於 exe1
目標的 POSITION_INDEPENDENT_CODE
屬性的值取決於連結庫(lib3
),並且連結 exe1
的邊緣由相同的 POSITION_INDEPENDENT_CODE
屬性決定,因此上面的依賴關係圖包含一個循環。cmake(1)
會發出錯誤訊息。
輸出成品¶
由 add_library()
和 add_executable()
命令建立的建置系統目標會建立規則以產生二進制輸出。二進制輸出的確切輸出位置只能在產生時確定,因為它可能取決於建置配置和連結依賴項的連結語言等。TARGET_FILE
、TARGET_LINKER_FILE
和相關的運算式可用於存取生成的二進制檔案的名稱和位置。但是,這些運算式不適用於 OBJECT
庫,因為此類庫不會生成與運算式相關的單個檔案。
目標可能會建置三種類型的輸出成品,詳情在以下章節中說明。它們的分類在 DLL 平台和非 DLL 平台之間有所不同。所有基於 Windows 的系統(包括 Cygwin)都是 DLL 平台。
執行時期輸出成品¶
建置系統目標的執行時期輸出成品可能是
由
add_executable()
命令建立的可執行目標的可執行檔(例如.exe
)。在 DLL 平台上:由
add_library()
命令使用SHARED
選項建立的共享庫目標的可執行檔(例如.dll
)。
可以使用 RUNTIME_OUTPUT_DIRECTORY
和 RUNTIME_OUTPUT_NAME
目標屬性來控制建置樹中執行時期輸出成品的的位置和名稱。
庫輸出成品¶
建置系統目標的庫輸出成品可能是
由
add_library()
命令使用MODULE
選項建立的模組庫目標的可加載模組文件(例如.dll
或.so
)。在非 DLL 平台上:由
add_library()
命令使用SHARED
選項建立的共享庫目標的共享庫文件(例如.so
或.dylib
)。
可以使用 LIBRARY_OUTPUT_DIRECTORY
和 LIBRARY_OUTPUT_NAME
目標屬性來控制建置樹中庫輸出成品的的位置和名稱。
封存輸出成品¶
建置系統目標的封存輸出成品可能是
由
add_library()
命令使用STATIC
選項建立的靜態庫目標的靜態庫文件(例如.lib
或.a
)。在 DLL 平台上:由
add_library()
命令使用SHARED
選項建立的共享庫目標的匯入庫文件(例如.lib
)。僅當庫匯出至少一個非託管符號時,才能保證此檔案存在。在 DLL 平台上:當
ENABLE_EXPORTS
目標屬性設定時,由add_executable()
命令建立的可執行目標的匯入庫文件(例如.lib
)。在 AIX 上:當
ENABLE_EXPORTS
目標屬性設定時,由add_executable()
命令建立的可執行目標的連結器匯入文件(例如.imp
)。在 macOS 上:當
ENABLE_EXPORTS
目標屬性設定時,由add_library()
命令使用SHARED
選項建立的共享庫目標的連結器匯入文件(例如.tbd
)。
可以使用 ARCHIVE_OUTPUT_DIRECTORY
和 ARCHIVE_OUTPUT_NAME
目標屬性來控制建置樹中封存輸出成品的的位置和名稱。
目錄範圍命令¶
target_include_directories()
、target_compile_definitions()
和 target_compile_options()
命令一次只對一個目標產生影響。add_compile_definitions()
、add_compile_options()
和 include_directories()
命令具有相似的功能,但為了方便起見,它們在目錄範圍而不是目標範圍內運作。
建置配置¶
配置決定了特定類型建置的規範,例如 Release
或 Debug
。指定方式取決於所使用的 產生器
類型。對於單一配置產生器,例如 Makefile 產生器 和 Ninja
,配置是在配置時通過 CMAKE_BUILD_TYPE
變數指定的。對於多配置產生器,例如 Visual Studio、Xcode
和 Ninja Multi-Config
,配置是由用戶在建置時選擇的,並且 CMAKE_BUILD_TYPE
會被忽略。在多配置情況下,可用配置的集合是在配置時通過 CMAKE_CONFIGURATION_TYPES
變數指定的,但是實際使用的配置在建置階段之前是未知的。這種差異經常被誤解,導致了如下的問題代碼
# WARNING: This is wrong for multi-config generators because they don't use
# and typically don't even set CMAKE_BUILD_TYPE
string(TOLOWER ${CMAKE_BUILD_TYPE} build_type)
if (build_type STREQUAL debug)
target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
endif()
產生器運算式
應該被用來正確地處理特定於配置的邏輯,而不管使用的是哪個產生器。例如
# Works correctly for both single and multi-config generators
target_compile_definitions(exe1 PRIVATE
$<$<CONFIG:Debug>:DEBUG_BUILD>
)
在存在 IMPORTED
目標的情況下,MAP_IMPORTED_CONFIG_DEBUG
的內容也會被上面的 $<CONFIG:Debug>
運算式考慮在內。
大小寫敏感性¶
CMAKE_BUILD_TYPE
和 CMAKE_CONFIGURATION_TYPES
與其他變數一樣,對其值進行的任何字串比較都將區分大小寫。$<CONFIG>
產生器運算式也保留了用戶或 CMake 預設設定的配置的大小寫。例如
# NOTE: Don't use these patterns, they are for illustration purposes only.
set(CMAKE_BUILD_TYPE Debug)
if(CMAKE_BUILD_TYPE STREQUAL DEBUG)
# ... will never get here, "Debug" != "DEBUG"
endif()
add_custom_target(print_config ALL
# Prints "Config is Debug" in this single-config case
COMMAND ${CMAKE_COMMAND} -E echo "Config is $<CONFIG>"
VERBATIM
)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
if(DEBUG IN_LIST CMAKE_CONFIGURATION_TYPES)
# ... will never get here, "Debug" != "DEBUG"
endif()
相反地,當 CMake 在內部使用配置類型來修改基於配置的行為時,CMake 會以不區分大小寫的方式處理配置類型。例如,$<CONFIG:Debug>
產生器運算式對於不僅是 Debug
,而且還有 DEBUG
、debug
甚至 DeBuG
的配置都將評估為 1。因此,您可以在 CMAKE_BUILD_TYPE
和 CMAKE_CONFIGURATION_TYPES
中使用任何大小寫混合來指定配置類型,儘管存在強烈的慣例(請參閱下一節)。如果您必須在字串比較中測試值,請始終先將值轉換為大寫或小寫,然後相應地調整測試。
預設和自訂配置¶
預設情況下,CMake 定義了許多標準配置
Debug
Release
RelWithDebInfo
MinSizeRel
在多配置產生器中,除非專案或用戶覆蓋,否則 CMAKE_CONFIGURATION_TYPES
變數預設將填充上述列表(可能是子集)。實際使用的配置由用戶在建置時選擇。
對於單配置產生器,配置是在配置時使用 CMAKE_BUILD_TYPE
變數指定的,並且無法在建置時更改。預設值通常不是上述任何標準配置,而是空字串。一個常見的誤解是這與 Debug
相同,但事實並非如此。用戶應始終明確指定建置類型,以避免這個常見問題。
上述標準配置類型在大多數平台上提供了合理的行為,但它們可以擴展以提供其他類型。每個配置都為使用的語言定義了一組編譯器和連結器標誌變數。這些變數遵循 CMAKE_<LANG>_FLAGS_<CONFIG>
慣例,其中 <CONFIG>
始終是大寫配置名稱。當定義自訂配置類型時,請確保正確設定這些變數,通常作為快取變數。
虛擬目標¶
某些目標類型不代表建置系統的輸出,而僅代表輸入,例如外部依賴項、別名或其他非建置成品。虛擬目標不表示在生成的建置系統中。
匯入目標¶
IMPORTED
目標表示預先存在的依賴項。通常,此類目標由上游套件定義,應視為不可變的。在宣告 IMPORTED
目標後,可以像任何其他常規目標一樣,使用常用的命令(例如 target_compile_definitions()
、target_include_directories()
、target_compile_options()
或 target_link_libraries()
)來調整其目標屬性。
IMPORTED
目標可能具有與二進制目標相同的已填充使用需求屬性,例如 INTERFACE_INCLUDE_DIRECTORIES
、INTERFACE_COMPILE_DEFINITIONS
、INTERFACE_COMPILE_OPTIONS
、INTERFACE_LINK_LIBRARIES
和 INTERFACE_POSITION_INDEPENDENT_CODE
。
的 LOCATION
也可從 IMPORTED 目標讀取,但很少有理由這樣做。像是 add_custom_command()
等命令可以透明地使用 IMPORTED
EXECUTABLE
目標作為 COMMAND
可執行檔。
定義 IMPORTED
目標的範圍是定義它的目錄。它可以從子目錄存取和使用,但不能從父目錄或同層目錄存取和使用。範圍類似於 cmake 變數的範圍。
也可以定義一個 GLOBAL
IMPORTED
目標,該目標在建置系統中是全域可存取的。
請參閱 cmake-packages(7)
手冊,以了解更多關於使用 IMPORTED
目標建立套件的資訊。
別名目標¶
ALIAS
目標是一個名稱,可以在唯讀環境中與二進制目標名稱互換使用。ALIAS
目標的主要用例是例如或單元測試可執行檔伴隨程式庫,這些可執行檔可能是同一個建置系統的一部分,或是根據使用者組態單獨建置的。
add_library(lib1 lib1.cpp)
install(TARGETS lib1 EXPORT lib1Export ${dest_args})
install(EXPORT lib1Export NAMESPACE Upstream:: ${other_args})
add_library(Upstream::lib1 ALIAS lib1)
在另一個目錄中,我們可以無條件地連結到 Upstream::lib1
目標,該目標可能是來自套件的 IMPORTED
目標,或者如果作為同一個建置系統的一部分建置,則為 ALIAS
目標。
if (NOT TARGET Upstream::lib1)
find_package(lib1 REQUIRED)
endif()
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Upstream::lib1)
ALIAS
目標是不可變的、不可安裝的或不可匯出的。它們完全是建置系統描述的本地目標。可以通過從中讀取 ALIASED_TARGET
屬性來測試名稱是否為 ALIAS
名稱
get_target_property(_aliased Upstream::lib1 ALIASED_TARGET)
if(_aliased)
message(STATUS "The name Upstream::lib1 is an ALIAS for ${_aliased}.")
endif()
介面程式庫¶
INTERFACE
程式庫目標不編譯來源程式碼,也不會在磁碟上產生程式庫成品,因此它沒有 LOCATION
。
它可以指定使用需求,例如 INTERFACE_INCLUDE_DIRECTORIES
、INTERFACE_COMPILE_DEFINITIONS
、INTERFACE_COMPILE_OPTIONS
、INTERFACE_LINK_LIBRARIES
、INTERFACE_SOURCES
和 INTERFACE_POSITION_INDEPENDENT_CODE
。只有 target_include_directories()
、target_compile_definitions()
、target_compile_options()
、target_sources()
和 target_link_libraries()
命令的 INTERFACE
模式可以與 INTERFACE
程式庫一起使用。
自 CMake 3.19 起,INTERFACE
程式庫目標可以選擇性地包含來源檔案。包含來源檔案的介面程式庫將作為建置目標包含在產生的建置系統中。它不編譯來源程式碼,但可能包含自訂命令以產生其他來源程式碼。此外,IDE 將顯示來源檔案作為目標的一部分,以進行互動式閱讀和編輯。
INTERFACE
程式庫的主要用例是僅標頭檔程式庫。自 CMake 3.23 起,標頭檔可以通過使用 target_sources()
命令將它們添加到標頭集合中,從而與程式庫關聯
add_library(Eigen INTERFACE)
target_sources(Eigen PUBLIC
FILE_SET HEADERS
BASE_DIRS src
FILES src/eigen.h src/vector.h src/matrix.h
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 Eigen)
當我們在這裡指定 FILE_SET
時,我們定義的 BASE_DIRS
會自動成為目標 Eigen
的使用需求中的 include 目錄。來自目標的使用需求在編譯時會被消耗和使用,但對連結沒有影響。
另一個用例是對於使用需求採用完全以目標為中心的設計
add_library(pic_on INTERFACE)
set_property(TARGET pic_on PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(pic_off INTERFACE)
set_property(TARGET pic_off PROPERTY INTERFACE_POSITION_INDEPENDENT_CODE OFF)
add_library(enable_rtti INTERFACE)
target_compile_options(enable_rtti INTERFACE
$<$<OR:$<COMPILER_ID:GNU>,$<COMPILER_ID:Clang>>:-rtti>
)
add_executable(exe1 exe1.cpp)
target_link_libraries(exe1 pic_on enable_rtti)
這樣一來,exe1
的建置規範完全表示為連結的目標,而編譯器特定旗標的複雜性被封裝在 INTERFACE
程式庫目標中。
INTERFACE
程式庫可以安裝和匯出。我們可以將預設標頭集合與目標一起安裝
add_library(Eigen INTERFACE)
target_sources(Eigen PUBLIC
FILE_SET HEADERS
BASE_DIRS src
FILES src/eigen.h src/vector.h src/matrix.h
)
install(TARGETS Eigen EXPORT eigenExport
FILE_SET HEADERS DESTINATION include/Eigen)
install(EXPORT eigenExport NAMESPACE Upstream::
DESTINATION lib/cmake/Eigen
)
在這裡,標頭集合中定義的標頭會安裝到 include/Eigen
。安裝目的地會自動成為 include 目錄,這是消費者的使用需求。