cmake的find_package命令对于构建软件提供了极大的便利,虽然知道怎么使用已经能解决大部分问题,但是cmake支持哪些包?为什么它能找到需要的包?如果是自己编写的库,需要如何做才能使用该命令进行构建等原理性问题却始终困扰着我。
这篇文章参考cmake官方文档和网上前辈的文章记录自己对find_package命令的学习和分析。
理论上只要符合find_package命令的查找条件,能让该命令识别的包或者库都会被支持。在实际使用时,我们可以把这些包分成三类:
官方预定义的包
为了方便我们查找依赖包,cmake为我们包含了许多默认的依赖包,这些包是cmake发行版的一部分,也就是说可以直接在CMakeLists.txt文件中引用,不需要额外安装。它们的位置通常是在cmake安装目录的Module目录下(比如我的系统内是:/usr/share/cmake-3.17/Modules),也可以通过变量CMAKE_MODULE_PATH来指定。
cmake预支持的包可以查看cmake-modules
非官方但支持cmake的包
这一类包虽然不在cmake的预支持列表中,但是自行下载使用cmake编译安装后也能被cmake识别,使用方法与官方预定义包一致。比如glog包。
默认不支持cmake的包
最典型的是我们自己编写的库文件,这类文件既不在cmake的官方列表,安装时默认也不会被cmake搜索到,此时就需要我们自行编写相应的配置文件并放到对应的路径中去。
cmake并不是直接去查找包本身,而是通过查找包对应的配置文件来查找包。这个文件说明了包相关的一些信息,比如版本、目录等。在不同模式下,配置文件可能不同,但是只要目标包有对应的.cmake文件且存在于cmake的查找路径中,就可以被find_package直接使用。
cmake定义了一系列的路径去查找包的配置文件,同时会定义
你可以通过
对于cmake的查找路径,不得不提到find_package的两种模式(MODULE模式和CONFIG模式),其中MODULE模式是基本用法,CONFIG模式是高级用法。除非用户显式设置,否则cmake会首先在MODULE模式下查找,在查找失败时会进入CONFIG模式继续查找。
使用上,MODULE模式比CONFIG模式要简单地多,通常情况下使用这一类命令就足够完成我们的要求,只有在MODULE模式找不到相应包的情况下,我们才可能会需要在CONFIG模式下使用。
find_package( [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[NO_POLICY_SCOPE])
注:上述参数中,除了PackageName必需,其他都是可选参数。
在MODULE模式下,cmake通过查找名为Find
用户代码通常使用MODULE模式命令就足够使用
find_package( [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
[NO_POLICY_SCOPE]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[PATH_SUFFIXES suffix1 [suffix2 ...]]
[NO_DEFAULT_PATH]
[NO_PACKAGE_ROOT_PATH]
[NO_CMAKE_PATH]
[NO_CMAKE_ENVIRONMENT_PATH]
[NO_SYSTEM_ENVIRONMENT_PATH]
[NO_CMAKE_PACKAGE_REGISTRY]
[NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
[NO_CMAKE_SYSTEM_PATH]
[NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH])
CONFIG模式命令几乎是MODULE模式命令的扩展,可以看到前面的参数(除MODULE)外都相同。
在CONFIG模式下,查找路径比MODULE多得多,而且查找的目标配置文件以
查找顺序为:
- 在cmake变量或者环境变量_ROOT指定的路径下查找,如果命令中设置了NO_CMAKE_FIND_ROOT_PATH或者CMAKE_FIND_USE_PACKAGE_ROOT_PATH变量设置为false则会跳过此路径;
- 在特定的cmake变量指定的位置查找:
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
(如果设置了NO_CMAKE_PATH参数或者将变量CMAKE_FIND_USE_CMAKE_PATH设置为False,那么会跳过这一步)
- cmake特定的环境变量
_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
可以通过NO_CMAKE_ENVIRONMENT_PATH来跳过。
- HINT字段指定的路径
- 搜索标准的系统环境变量PATH。
其中如果是以/bin或者/sbin结尾的,会自动转化为其父目录。
通过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。
- 存储在cmake的"User Package Registry"(用户包注册表)中的路径。
通过设定NO_CMAKE_PACKAGE_REGISTRY,或者:设定CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为true,
来避开。
- 设定为当前系统定义的cmake变量:
CMAKE_SYSTEM_PREFIX_PATH
CMAKE_SYSTEM_FRAMEWORK_PATH
CMAKE_SYSTEM_APPBUNDLE_PATH
通过设定NO_CMAKE_SYSTEM_PATH来跳过。
- 在cmake的"System Package Registry"(系统包注册表)中查找。
通过设定NO_CMAKE_SYSTEM_PACKAGE_REGISTRY跳过。
或者通过设定CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为true。
- 从PATHS字段指定的路径中查找。
以boost库为例
find_package(Boost REQUIRED)
if(Boost_FOUND)
message("Boost Package found")
endif()
glob包不在cmake预支持的包列表中,但是使用cmake编译安装后,也可以直接使用find_package来查找该包,用法与预支持的包没有不同。
安装glog
# clone该项目
git clone https://github.com/google/glog.git
# 切换到需要的版本
cd glog
git checkout v0.40
# 根据官网的指南进行安装
cmake -H. -Bbuild -G "Unix Makefiles"
cmake --build build
cmake --build build --target install
此时就可以和预定义包一样去查找了:
find_package(GLOG)
比如最典型的我们自己开发编译的动态库等,这里以之前代码中使用math库为例,说明如何在cmake系统中添加对我们的库的支持。
make
make install
我这里会将库.so和对应头文件分别安装到目录/usr/local/lib/math和/usr/local/include下。
find_path(MATH_INCLUDE_DIR add.h /usr/include/ /usr/local/include ${CMAKE_SOURCE_DIR}/ModuleMode)
find_library(MATH_LIBRARY NAMES math PATHS /usr/lib/math /usr/local/lib/math ${CMAKE_SOURCE_DIR}/ModuleMode)
if (MATH_INCLUDE_DIR AND MATH_LIBRARY)
set(MATH_FOUND TRUE)
endif (MATH_INCLUDE_DIR AND MATH_LIBRARY)
find_path指定了头文件的路径,find_library指定了查找库文件.so的路径。
set(CMAKE_MODULE_PATH "/home/xxx/experiment/cmake/math")
message(${CMAKE_MODULE_PATH})
find_package(math REQUIRED)
if(MATH_FOUND)
message("libmath found")
target_include_directories(${MODULE_MAIN} PRIVATE ${MATH_INCLUDE_DIR})
target_link_libraries(${MODULE_MAIN} ${MATH_LIBRARY})
else()
message("required libmath not found")
endif()
(base) xxx@xxx-desktop:~/experiment/cmake/build$ cmake ..
/home/xxx/experiment/cmake/math
libmath found
-- Configuring done
-- Generating done
-- Build files have been written to: /home/xxx/experiment/cmake/build
(base) xxx@xxx-desktop:~/experiment/cmake/build$ make
[ 50%] Built target utility
[100%] Built target demo
(base) xxx@xxx-desktop:~/experiment/cmake/build$ ./demo
Hello world!
1 + 2 is: 3
Hello, this is my first cmake sample 3
Cmake Document
深入理解CMake(3):find_package()的使用
Cmake之深入理解find_package()的用法