CMake 工程中经常会调用 find_package 来使用外部包,通常为了让 CMake 能够直接找到一些常用的包,我们会将它们安装到系统路径,比如 /usr/local,但这么做往往在卸载的时候很难清理。如果我们不想将包安装到系统路径,又要在别的工程中调用这些包,该怎么办呢?
(以下假设包名为 MyPackage,其配置文件 MyPackageConfig.cmake 所在路径为 /path/to/MyPackage,使用该包的工程为 MyProject)
最直接同时也很常见的方法是:在 MyProject 中手动设置 MyPackage 查找路径。要么直接在 CMakeLists.txt 中加入 set(MyPackage_DIR "/path/to/MyPackage")
,要么在执行 cmake 时加入选项 -DMyPackage_DIR=/path/to/MyPackage
。前者在 CMake 代码中加入了本地路径,非常不推荐,而后者又使得编译流程更加繁琐。
有没有办法能够让一个包在不安装到系统路径的情况下,又能够直接被 CMake 系统找到呢?用户包注册 (User Package Registry) 就是这样一种方法。
什么是用户包注册
根据 CMake Packages: Package Registry:
The registries are especially useful to help projects find packages in non-standard install locations or directly in their own build trees. A project may populate either the user or system registry (using its own means, see below) to refer to its location. In either case the package should store at the registered location a Package Configuration File (Config.cmake) and optionally a Package Version File (ConfigVersion.cmake).
CMake 提供了一种包注册机制,帮助 CMake 系统找到那些存放在非标准安装路径下的包。简而言之,就是将包所在路径 (/path/to/MyPackage) 通过注册的方式直接告诉 CMake 系统。
包注册有用户包注册 (User Package Registry) 和系统包注册 (System Package Registry) 两种,由于系统包注册只存在于 Windows 系统,这里不作介绍。
对于 Windows 系统,用户包注册保存在注册表中:
HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\MyPackage
该键下用一个 REG_SZ
类型变量 (名称任意) 保存 /path/to/MyPackage。
对于 UNIX 系统,用户包注册保存在 home 目录:
~/.cmake/packages/MyPackage
该目录下用一个文本文件 (名称任意) 存放 /path/to/MyPackage。
注册用户包的方法
手动编译包的情况
在 MyPackage 的 CMakeLists.txt 中添加:
export(PACKAGE MyPackage)
这样在编译 MyPackage 时,CMake 会自动注册用户包。上面提到用于存放 /path/to/MyPackage 的文件名/变量名虽然是任意的,但 CMake 自动注册时为了避免冲突 (同一个包可以注册多个版本),会使用哈希值作为名称,比如:
> reg query HKCU\Software\Kitware\CMake\Packages\MyPackage
HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\MyPackage
45e7d55f13b87179bb12f907c8de6fc4 REG_SZ c:/Users/Me/Work/lib/cmake/MyPackage
7b4a9844f681c80ce93190d4e3185db9 REG_SZ c:/Users/Me/Work/MyPackage-build
或者
$ cat ~/.cmake/packages/MyPackage/7d1fb77e07ce59a81bed093bbee945bd
/home/me/work/lib/cmake/MyPackage
$ cat ~/.cmake/packages/MyPackage/f92c1db873a1937f3100706657c63e07
/home/me/work/MyPackage-build
需要注意的是,根据 CMake: export,CMake 3.15 之后,export(PACKAGE)
命令默认情况下是无效的 (因为显然该命令会修改源码路径和构建路径之外的文件),需要设置 CMAKE_EXPORT_PACKAGE_REGISTRY
变量来启用该功能。
只有预编译包的情况
手动在注册表或者 ~/.cmake/packages
路径下建立相应记录即可:
> reg query HKCU\Software\Kitware\CMake\Packages\MyPackage
HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\MyPackage
custom REG_SZ c:/path/to/MyPackage
或者
$ cat ~/.cmake/packages/MyPackage/custom
/path/to/MyPackage
如何禁用已注册的用户包
有时某个包已经在系统路径下安装了一个版本 (系统包管理安装的预编译包),同时又注册了一个用户包 (自行源码编译的修改版)。可以在 MyProject 中通过以下方法禁用用户包:
- 调用 find_package 时加入 NO_CMAKE_PACKAGE_REGISTRY 选项,即
find_package(MyPackage REQUIRED NO_CMAKE_PACKAGE_REGISTRY)
- 设置 CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY 为 TRUE (该方法已弃用)
- 设置 CMAKE_FIND_USE_PACKAGE_REGISTRY 为 FALSE