install(TARGETS)
install(EXPORT)
今天會介紹 CMake 的 install()
指令, install()
根據安裝的目標不同, 會有不同的寫法, 本系列只會介紹 install(TARGETS)
和 install(EXPORT)
, 其他用法先在這簡單介紹
install(IMPORTED_RUNTIME_ARTIFACTS)
find_package()
找到的 imported target 會是 UNKNOWN
type 而非 SHARED
或 STATIC
, 這時就沒辦法用此指令安裝該 targetinstall({FILES | PROGRAMS})
target_sources(FILE_SET)
安裝install(DIRECTORY)
install(FILE_SET)
會很麻煩, 就可以用這個指令install(SCRIPT)
install(CODE)
install(RUNTIME_DEPENDENCY_SET)
install(TARGETS)
與 install(IMPORTED_RUNTIME_ARTIFACTS)
搭配的指令RUNTIME_DEPENDENCY_SET
, 執行這個指令就可以將該 targets 的 runtime dependencies 都一併安裝install(TARGETS)
會將 targets 根據 FHS 的規則加入對應的 directory
install(TARGETS targets... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
[[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[NAMELINK_COMPONENT <component>]
[OPTIONAL] [EXCLUDE_FROM_ALL]
[NAMELINK_ONLY|NAMELINK_SKIP]
] [...]
[INCLUDES DESTINATION [<dir> ...]]
)
TARGETS
bin
, libraries to lib
)EXPORT
install(EXPORT)
使用, 下面會介紹RUNTIME_DEPENDENCIES
install(RUNTIME_DEPENDENCY_SET)
, 不過這邊會會在安裝此 target 的時候一併安裝指定的 dependenciesRUNTIME_DEPENDENCIES_SET
RUNTIME_DEPENDENCIES
會和 target 一起安裝不同, 這裡僅先設定 dependency set 名稱, 等到 install(RUNTIME_DEPENDENCY_SET)
才安裝ARCHIVE
LIBRARY
RUNTIME
OBJECTS
FRAMEWORK
BUNDLE
PRIVATE_HEADER
PUBLIC_HEADER
RESOURCE
FILE_SET
CXX_MODULES_BMI
DESTINATION
GNUInstallDirs
提供的路徑, 根據 target type 去安裝, 可以參考官網的表GNUInstallDirs
提供的為主, 比如可以將 headers 裝到 ${CMAKE_INSTALL_INCLUDEDIR}/<project-name>
而非 lib
底下PERMISSIONS
OWNER_READ
OWNER_WRITE
OWNER_EXECUTE
GROUP_READ
GROUP_WRITE
GROUP_EXECUTE
WORLD_READ
WORLD_WRITE
WORLD_EXECUTE
SETUID
SETGID
CONFIGURATIONS
COMPONENT
cmake -DCOMPONENT=<component>
單獨安裝該 component 的 targetsNAMELINK_COMPONENT
, NAMELINK_ONLY
, NAMELINK_SKIP
INCLUDES DESTINATION
INTERFACE_INCLUDE_DIRECTORIES
property, 讓 import 該 target 的 target 能找到需要的 headersinstall(EXPORT)
該指令會幫我們產生 可以讓別人 import 我們 targets 的 cmake file, 預設是 <export-name>.cmake
, 如果要讓別人使用此專案, 這就是個很方便的指令
install(EXPORT <export-name> DESTINATION <dir>
[NAMESPACE <namespace>] [FILE <name>.cmake]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]
[CXX_MODULES_DIRECTORY <directory>]
[EXPORT_LINK_INTERFACE_LIBRARIES]
[COMPONENT <component>]
[EXCLUDE_FROM_ALL]
)
EXPORT
install(TARGETS <target-name> EXPORT <export-name>)
使用DESTINATION
NAMESPACE
fmt
進來時, fmt
提供的 imported targets 是 fmt::fmt-header-only
和 fmt::fmt-core
, 前面的 fmt::
就是 namespace::
當作 namespace <project-name>::
FILE
<export-name>.cmake
.cmake
extensionPERMISSIONS
install(TARGETS)
, 這邊不多贅述CONFIGURATIONS
COMPONENT
install(TARGETS)
時指定的 component 中的所有 targets記得 Day 13 提過的幾種引進外部 libraries 的方法嗎? 現在我們要自己寫一份讓其他使用者用 find_package()
來引進我們專案
所以我們專案 targets 的版本也很重要
我們可以用 CMakePackageConfigHelpers
module 提供的兩個 functions: write_basic_package_version_file()
和 configure_package_config_file()
write_basic_package_version_file()
會產生 <export-name>ConfigVersion.cmake
標示當前使用的 library 版本
write_basic_package_version_file(<filename>
[VERSION <major.minor.patch>]
COMPATIBILITY <AnyNewerVersion|SameMajorVersion|SameMinorVersion|ExactVersion>
[ARCH_INDEPENDENT]
)
ARCH_INDEPENDENT
configure_package_config_file()
幫我們產生 <package-name>Config.cmake
設定檔
然而, 只用該指令產生的 config 檔會在 build-time 就決定所有的安裝路徑了
但無論是在 Windows 還是 Apple 系列平台, 我們安裝程式都是在 install stage 才安裝!
如果僅用該指令產生 config 檔的話, 就會綁死使用者能夠安裝我們專案的路徑
所以, 如果要避免這種情況, 讓我們的專案能夠 relocate, 可以裝在任意路徑, 我們就需要先自己寫一個 <package-name>Config.cmake.in
該檔案開頭必須包含 @PACKAGE_INIT@
, 且內容的路徑都要使用 @PACKAGE_<path-var>@
, PACKAGE_<path-var>
系列的變數會在 configure_package_config_file
被處理並轉換成相對於安裝路徑的字串
有了這個概念後, 來看看他的指令
configure_package_config_file(<input> <output>
INSTALL_DESTINATION <path>
[PATH_VARS <var1> <var2> ... <varN>]
[NO_SET_AND_CHECK_MACRO]
[NO_CHECK_REQUIRED_COMPONENTS_MACRO]
[INSTALL_PREFIX <path>]
)
INSTALL_DESTINATION
INSTALL_PREFIX
PATH_VARS
PACKAGE_<path-var>
系列變數, 讓我們寫的 <package-name>Config.cmake.in
中的 PACKAGE_<path-var>
install-time 的路徑能被 parse 成真正的安裝路徑INSTALL_PREFIX
了解了複雜的安裝指令後, 下一篇我們就來實際操作看看吧!