Chapter 23. Finding Things
中等规模以上的项目除了本身的项目之外, 可能还依赖于其他东西. 比如 a particular library or tool, location of a specific configuration file or a header for a library. 甚至项目可能需要找一个 package, 其中定义了一系列内容, 包括 targets, functions, variables…
find_...
命令提供了搜索 file、library 或 progaram,甚至 package 的能力.
23.1 Finding Files and Paths
find_file(outVar
name | NAMES name1 [name2...]
[HINTS path1 [path2...] [ENV var]...]
[PATHS path1 [path2...] [ENV var]...]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[NO_DEFAULT_PATH]
[NO_PACKAGE_ROOT_PATH]
[NO_CMAKE_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_SYSTEM_PATH]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH]
[DOC "description"]
)
搜索顺序按如下表格:
Package root variables
packageName_ROOT
cmake 变量和同名环境变量.
Cache variables (CMake-specific)
CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH and CMAKE_FRAMEWORK_PATH.
CMAKE_PREFIX_PATH 会从<prefix>/include
中查找, 另外两个直接在变量路径中查找.
CMAKE_INCLUDE_PATH 适用于 find_file()和 find_path(). CMAKE_FRAMEWORK_PATH 适用于前两者+find_library().
Environment variables (CMake-specific)
CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH and CMAKE_FRAMEWORK_PATH 三个同名环境变量.
Environment variables (system-specific)
INCLUDE 和 PATH 两个环境变量.
Cache variables (platform-specific)
CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_INCLUDE_PATH and CMAKE_SYSTEM_FRAMEWORK_PATH.
会由 platform toolchain 自动设置.
HINTS and PATHS
上面讨论的变量都是由项目之外设置的,但是 HINTS 和 PATHS 选项是项目本身应该增加的额外搜索路径.
PATHS 一般是固定的路径, 而 HINTS 一般是由其他变量计算展开得到的. HINTS 的路径优先级比 PATHS 高.
图片中 skip option 中的选项可以用来跳过搜索某些路径. NO_DEFAULT_PATH 可以跳过除了 HINTS 和 PATHS 的所有搜索路径.
PATH_SUFFIXES: 会增加搜索每个搜索路径下的子文件夹.
find_file()还有一种缩略形式, 可以只搜索指定的路径:
find_file(outVar name [path1 [path2...]])
如果想更改 find_file 的搜索顺序, 先搜索我们想搜索的路径, 如果不存在, 再搜索默认的路径, 可以使用如下多次调用的方式:
find_file(FOO_HEADER foo.h PATHS /opt/foo/include NO_DEFAULT_PATH)
find_file(FOO_HEADER foo.h)
CMAKE_FIND_ROOT_PATH_BOTH, ONLY_CMAKE_FIND_ROOT_PATH, NO_CMAKE_FIND_ROOT_PATH 三个关键字可以改变搜索顺序:
23.1.2 Cross-compilation Controls
如果是交叉编译, 可以通过 CMAKE_FIND_ROOT_PATH 修改 find_xxx 命令的根目录.
或者 通过修改 CMAKE_SYSROOT 来改变编译器和链接器的搜索路径(只能在 toolchain file 中修改).
23.2 Finding Paths
搜索目录, find_path(), 和 find_file()其他参数一模一样.
23.3 Finding Programs
和 find_file()基本一致, 不同点有:
Cache variables (CMake-specific)
对于 CMAKE_PREFIX_PATH, find_file() append 的是 include/, 而 find_program() append 的是 bin/, sbin/
CMAKE_INCLUDE_PATH 由 CMAKE_PROGRAM_PATH 代替.
CMAKE_FRAMEWORK_PATH 由 CMAKE_APPBUNDLE_PATH 代替.
Environment variables (system-specific)
只搜索 PATH, 不搜索 INCLUDE.
General
多了一个 NAMES_PER_DIR 选项, 可以在 NAMES 列出来的文件中, 倒过来搜索.
23.4 Finding Libraries
和 find_file()基本一致, 不同点有:
Cache variables (CMake-specific)
对于 CMAKE_PREFIX_PATH, append 的是 lib/
Environment variables (system-specific)
PATH 和 LIB
General
多了和 find_program()一样的 NAMES_PER_DIR.
23.5 Finding Packages
find_package()有两种工作模式, module 模式和 config 模式.
module 模式
查找Find<PackageName>.cmake
配置文件.
只有两个查找路径: CMAKE_MODULE_PATH
和 cmake 内置的 module 路径.
如果找不到, 会退到 config 模式.
find_package(packageName
[version [EXACT]]
[QUIET] [REQUIRED]
[[COMPONENTS] component1 [component2...]]
[OPTIONAL_COMPONENTS component3 [component4...]]
[MODULE]
[NO_POLICY_SCOPE]
)
version: 指定最低的 package version. EXACT: 需要匹配准确的 version.
QUIET: 如果没找到 package, 不会打印错误信息.
REQUIRED: 如果没找到 package, 会打印错误信息.
COMPONENTS: 指定 package 中必须的组件.
OPTIONAL_COMPONENTS: 指定 package 中可选的组件.
MODULE: 只在 module 模式下查找, 默认行为是 Module 模式查找失败则会退到 Config 模式进行查找.
e.g.
例如,下面的调用需要找到 Qt 5.9 或更高版本,并且 Gui 组件是必选,DBus 模块是可选。
find_package(Qt5 5.9 REQUIRED
COMPONENTS Gui
OPTIONAL_COMPONENTS DBus
)
# 当出现 REQUIRED 选项时,可以省略 COMPONENTS 关键字,并将强制性组件放置在 REQUIRED 之后.
find_package(Qt5 5.9 REQUIRED Gui Widgets Network)
Config 模式
查找<PackageName>Config.cmake
或<lowercasePackageName>-config.cmake
配置文件.
还会查找<PackageName>ConfigVersion.cmake
或 <PackageName>Config-version.cmake
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) 会查找到 zephyr/share/zephyr-package/cmake/ZephrConfig.cmake 和 zephyr/share/zephyr-package/cmake/ZephrConfigVersion.cmake 文件
find_package(<PackageName> [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
[GLOBAL]
[NO_POLICY_SCOPE]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[NO_DEFAULT_PATH]
...
[NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH])
CONFIG/NO_MODULE: 这两个等价的选项表示采用 config 模式,不会再搜索 module.
搜索路径相比 module 多了很多:
Package root variables
<packagename>_ROOT
cmake 变量, 和同名的环境变量.
Cache variables (CMake-specific)
CMAKE_PREFIX_PATH
, CMAKE_FRAMEWORK_PATH
, CMAKE_APPBUNDLE_PATH
.
Environment variables (CMake-specific)
CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH and CMAKE_FRAMEWORK_PATH 三个同名环境变量.
Environment variables (system-specific)
只有 PATH
Cache variables (platform-specific)
CMAKE_SYSTEM_PREFIX_PATH, CMAKE_SYSTEM_FRAMEWORK_PATH and CMAKE_SYSTEM_APPBUNDLE_PATH.
HINTS and PATHS
和前面 find_file()一样.
Package registries
23.5.1 介绍, 方便查找 package.
23.5.1 Package Registries
CMake 支持一种 package 注册表形式,来处理 package 分散在不同的地方.
可以通过 export(PACKAGE packageName) 将 package 添加到 user registry:
~/.cmake/packages/<packageName>/
Chapter 24. Testing
CTest
// TODO:
Chapter 25. Installing
25.1 Directory Layout
25.1.1 Relative Layout
cmake 的 GNUInstallDirs 模块提供了非常方便的方法来使用标准目录布局。include(GNUInstallDirs)
命令会提供一系列目录CMAKE_INSTALL_<dir>
, dir 的取值有:
BINDIR: 直接运行的可执行文件、脚本和符号链接的位置。默认为 bin
SRINDIR: 与 BINDIR 相似,不过是针对有系统管理权限的情况。默认为 sbin
LIBDIR: 库和编译文件的路径。根据主机/目标平台,默认设置为 lib
LIBEXECDIR: 不直接由用户调用的可执行文件,但可以通过启动脚本或位于 BINDIR 中的符号链接的方式运行。默认为 libexec
INCLUDEDIR: 头文件目录。默认为 include
25.1.2 Base Install Location
CMAKE_INSTALL_PREFIX 变量控制 install 目录前缀, Unix 默认安装目录为/usr/local.
For add-on packages, 推荐安装到/opt/<package>
或/opt/<provider>/<package>
目录下.
CMAKE_STAGING_PREFIX 变量可以用于交叉编译的场景, 多一个 install 的备份目录.
DESTDIR 直接用于 build tool, 控制安装目录.
make DESTDIR=/home/me/staging install
env DESTDIR=/home/me/staging ninja install
25.2 Installing Targets
install(TARGETS targets...
[EXPORT exportName]
[CONFIGURATIONS configs...]
# One or more blocks of the following
[ [entityType]
DESTINATION dir
[PERMISSIONS permissions...]
[NAMELINK_ONLY | NAMELINK_SKIP]
[COMPONENT component]
[NAMELINK_COMPONENT component] # CMake 3.12 or later only
[EXCLUDE_FROM_ALL]
[OPTIONAL]
[CONFIGURATIONS configs...]
]...
# Special case
[INCLUDES DESTINATION incDirs...]
)
entityType 指定如何安装目标的不同部分, 选项有:
RUNTIME: install executable binaries.
LIBRARY: install shared library.
ARCHIVE: install static library.
OBJECTS: install objects in object library.
PUBLIC_HEADER: install files in PUBLIC_HEADER property.
PRIVATE_HEADER: install files in PRIVATE_HEADER property.
RESOURCE: install files in RESOURCE property.
e.g.
install(TARGETS mySharedLib myStaticLib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
如果只有一种类型, 可以省略 entityType.
# Targets are both executables, so specifying the entity type isn't needed
install(TARGETS exe1 exe2
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
PERMISSION 设置目标安装后的权限, 可选值有:
安装库文件时,
libmyShared.so.1.3.2
libmyShared.so.1 --> libmyShared.so.1.3.2
libmyShared.so --> libmyShared.so.1
NAMELINK_ONLY: 只安装 libmyShared.so
NAMELINK_SKIP: 安装 libmyShared.so.1.3.2 和 libmyShared.so.1
NAMELINK_ONLY 和 NAMELINK_SKIP 不能共存, 需要分开安装.
COMPONENT: 指定 install 的 components.
install(TARGETS myShared myStatic
RUNTIME
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT MyProj_Runtime
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
NAMELINK_SKIP
COMPONENT MyProj_Runtime
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT MyProj_Development
)
install(TARGETS myShared
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
NAMELINK_ONLY
COMPONENT MyProj_Development
)
CONFIGURATIONS: 根据 build type 来选择不同的安装方式
install(TARGETS myStatic
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}/Debug
CONFIGURATIONS Debug
)
install(TARGETS myStatic
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}/Release
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
)
CONFIGURATIONS 关键字还可以位于所有参数的前面,并作为没提供配置的默认值的默认值。下面的示例中,除了为 debug 和 release 安装的 ARCHIVE 外,所有的块都只为 release 安装。
install(TARGETS myShared myStatic
CONFIGURATIONS Release
RUNTIME
DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
CONFIGURATIONS Debug Release
)
25.2.1 Interface Properties
考虑如下 cmake code
add_library(foo STATIC ...)
target_include_directories(foo
INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/somewhere
${MyProject_BINARY_DIR}/anotherDir
)
install(TARGETS foo
DESTINATION ...
)
任何链接到 foo 的内容都会在头文件搜索路径中添加 somewhere 的搜索路径. 当 foo 安装时,可以打包并部署到完全不同的机器上。显然这时 somewhere 的路径将不再有意义.
这时,可以通过生成器表达式, 构建时使用头文件路径 xxx,安装时使用头文件路径 yyy:
include(GNUInstallDirs)
target_include_directories(foo
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/somewhere>
$<BUILD_INTERFACE:${MyProject_BINARY_DIR}/anotherDir>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
这样为每个 target 都指定 install 后的头文件路径比较麻烦, 因为安装后 target 共享相同的头文件搜索路径, 可以在 install 命令中统一指定.
add_library(myStatic STATIC ...)
add_library(myHeaderOnly INTERFACE ...)
target_include_directories(myStatic
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/static_exports>
)
target_include_directories(myHeaderOnly
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
)
install(TARGETS myStatic myHeaderOnly
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
25.2.2. RPATH
linux 平台使用LD_LIBRARY_PATH
环境变量来查找动态库.
可以使用 RPATH 在 cmake 中指定动态库查找路径, 这样就可以不依赖于环境变量.
RPATH 种类有, CMAKE_BUILD_RPATH, CMAKE_INSTALL_RPATH 等.
e.g.
set(CMAKE_INSTALL_RPATH $ORIGIN $ORIGIN/../lib)
25.3 Installing Exports
安装导出信息, xxx.cmake. 这样其他项目可以通过 find_package()来获取相关信息.
install(EXPORT exportName
DESTINATION dir
[FILE name.cmake]
[NAMESPACE namespace]
[PERMISSIONS permissions...]
[EXPORT_LINK_INTERFACE_LIBRARIES]
[COMPONENT component]
[EXCLUDE_FROM_ALL]
[CONFIGURATIONS configs...]
)
25.4 Installing Files And Directories
安装文件:
install(<FILES | PROGRAMS> files...
DESTINATION dir
[RENAME newName]
[PERMISSIONS permissions...]
[COMPONENT component]
[EXCLUDE_FROM_ALL]
[OPTIONAL]
[CONFIGURATIONS configs...]
)
install(FILES) 和 install(PROGRAMS) 的区别是,如果没有 PERMISSIONS ,后者会在默认情况下添加执行权限。
安装目录:
install(DIRECTORY dirs...
DESTINATION dir
[FILE_PERMISSIONS permissions... | USE_SOURCE_PERMISSIONS]
[DIRECTORY_PERMISSIONS permissions...]
[COMPONENT component]
[EXCLUDE_FROM_ALL]
[OPTIONAL]
[CONFIGURATIONS configs...]
[MESSAGE_NEVER]
[FILES_MATCHING]
# The following block can be repeated as many times as needed
[ [PATTERN pattern | REGEX regex]
[EXCLUDE]
[PERMISSIONS permissions...] ]
)
25.5 Custom Install Logic
install(SCRIPT fileName | CODE cmakeCode
[COMPONENT component]
[EXCLUDE_FROM_ALL]
)
e.g.
install(CODE [[ message("Starting custom script") ]]
SCRIPT myCustomLogic.cmake
CODE [[ message("Finished custom script") ]]
COMPONENT MyProj_Runtime
)
Chapter 26. Packaging
cpack 打包
// TODO:
Chapter 27. External Content
引入外部项目 ExternalProject, FetchContent 两个 module.
// TODO: