查找已有库
在我们实际开发过程中,经常不可避免会使用到第三方开源库,这些开源库可能是通过apt-get install
命令自动安装到系统目录中,也可能是由我们自己下载库的源码然后通过编译安装到指令目录下的,CMake提供了一个find_package
命令用于查找系统中的库。
查找自动安装库:boost
在代码中调用了boost库来产生随机sample,在Ubuntu下可以通过sudo apt-get install libboost-all-dev
来安装它。通过apt-get install
安装的库系统通常会将其.cmake
配置或库文件安装到/usr/lib
或/usr/local/lib
目录下,这也通常是系统默认的搜索路径,所以开发者可以在不指明库调用的情况下使用它们,但这不是一种推荐的方式,正确的CMakeLists.txt
应该如下所示:
当库被找到之后,find_package
命令会自动初始化一些列变量:
<NAME>_FOUND
: 是否找到的标志<NAME>_INCLUDE_DIRS
or<NAME>_INCLUDES
: 头文件目录<NAME>_LIBRARIES
or<NAME>_LIBRARIES
or<NAME>_LIBS
: 库文件<NAME>_DEFINITIONS
在找到需要的库之后,需要包含其头文件和库文件。
find_package
命令可以得到库文件的绝对路径,所以通常不会生成所谓的<NAME>_LIBRARIES_DIRS
再手动添加到link_directories
中
虽然由于许可证和法律问题等原因,开源并不容易,对于闭源库来说你应该把它的动态库包含金源码树。但对于使用开源库的项目来说,上述并不是一种安全的调用方式,因为你无法确保你的编译主机上具备完整的编译环境(换句话说就是程序的可移植性变差),更推荐的方式为使用源码编译的方式引入第三方库。
查找源码编译库:OpenCV
根据上述规则先给出一个查找并调用OpenCV
库的CMakeLists.txt
:
不加验证的,这样执行通常情况下都无法完成构建,除非你的OpenCV
在CMake默认搜索路径之下,要想正确查找并调用到OpenCV
需要理解find_package
的工作原理。
find_package
CMake本身不提供任何搜索库的便捷方法,所有搜索库并给变量赋值的操作必须由CMake代码完成,也即<package_name>Config.cmake
以及Find<package_name>.cmake
配置文件。
Module模式
find_package
命令基础工作模式(Basic Signature),也是默认工作模式。Config模式
find_package
命令高级工作模式(Full Signature)。 只有在find_package()中指定CONFIG、NO_MODULE等关键字,或者Module模式查找失败后才会进入到Config模式。
find_package的Mode模式
Module模式的参数为:
find_package(<package> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE]
参数解释: package
:必填参数。需要查找的包名,注意大小写。
version
和EXACT
:可选参数,version指定的是版本,如果指定就必须检查找到的包的版本是否和version兼容。如果指定EXACT则表示必须完全匹配的版本而不是兼容版本就可以。
QUIET
:可选参数,表示如果查找失败,不会在屏幕进行输出(但是如果指定了REQUIRED字段,则QUIET无效,仍然会输出查找失败提示语)。
MODULE
:可选字段。前面提到说“如果Module模式查找失败则回退到Config模式进行查找”,但是假如加入了MODULE选项,那么就只在Module模式查找,如果Module模式下查找失败并不切换到Config模式查找。
REQUIRED
:可选字段。表示一定要找到包,找不到的话就立即停掉整个CMake。而如果不指定REQUIRED则CMake会继续执行。
optional_COMPONENTS components
:可选字段,表示查找的包中必须要找到的组件(components),如果有任何一个找不到就算失败,类似于REQUIRED,导致CMake停止执行。
Module模式下是要查找到名为
Find<PackageName>.cmake
的配置文件。Module模式只有两个查找路径:
CMAKE_MODULE_PATH
和CMake安装路径下的Modules目录,先在CMAKE_MODULE_PATH
变量对应的路径中查找。如果路径为空,或者路径中查找失败,则在CMake安装目录(即CMAKE_ROOT
变量)下的Modules目录下查找。这两个变量可以在CMakeLists.txt文件中打印查看具体内容:
find_package的Config模式
Config模式的完整命令参数为:
find_package(<package> [version] [EXACT] [QUIET] [REQUIRED] [[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_CMAKE_ENVIRONMENT_PATH] [NO_CMAKE_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])
相比于Module模式,Config模式的参数更多,也更复杂,但实际在使用过程中我们并不会用到所有参数,大部分参数都是可选的,我们只需要掌握基本的参数用法即可。
其中具体查找库并给<package_name>_INCLUDE_DIRS
和<package_name>_LIBRARIES
两个变量赋值的操作由<package_name>Config.cmake
模块完成。
两种模式看起来似乎差不多,不过CMake默认采取Module模式,如果Module模式未找到库,才会采取Config模式。如果XXX_DIR路径下找不到<package_name>Config.cmake
文件,则会找/usr/local/lib/cmake/${package_name}/
中的<package_name>Config.cmake
文件。总之,Config模式是一个备选策略。通常,库安装时会拷贝一份<package_name>Config.cmake
到系统目录中,因此在没有显式指定搜索路径时也可以顺利找到。
Config模式查找顺序:Config模式下是要查找名为<package_name>Config.cmake
或<lower-case-package-name>-config.cmake
的模块文件。
搜包路径依次为:
名为
<PackageName>_DIR
的CMake变量或环境变量路径,默认为空。 这个路径是非根目录路径,需要指定到<PackageName>Config.cmake
或<lower-case-package-name>-config.cmake
文件所在目录才能找到。名为
CMAKE_PREFIX_PATH
、CMAKE_FRAMEWORK_PATH
、CMAKE_APPBUNDLE_PATH
的CMake变量或环境变量路径,根目录路径,默认都为空。PATH
环境变量路径,根目录路径,默认为系统环境PATH环境变量值。 这个路径是Config模式大部分情况下能够查找到安装到系统中各种库的原因。 这个路径的查找规则为: 遍历PATH环境变量中的各路径,如果该路径如果以bin或sbin结尾,则自动回退到上一级目录得到根目录。
在上述指明的是根目录路径时,CMake会首先检查这些根目录路径下是否有名为<PackageName>Config.cmake
或<lower-case-package-name>-config.cmake
的模块文件,如果没有,CMake会继续检查或匹配这些根目录下的以下路径(<PackageName>_DIR
路径不是根目录路径):
其中<arch>
为系统架构名,如Ubuntu下一般为:/usr/lib/x86_64-linux-gnu
,整个lib/<arch>|lib|share
为可选路径,例如OpenCV库而言会检查或匹配<prefix>/OpenCV/
、<prefix>/lib/x86_64-linux-gnu/OpenCV/
、<prefix>/lib/share/OpenCV/
、<prefix>/share/OpenCV/
等路径;name为包名,不区分大小写<name>*
意思是包名后接一些版本后等字符也是合法的,如pcl-1.9也会被找到。
这里不过多赘述如何编写自己的.cmake
模块配置文件,创建自己的CMake Package是一个非常复杂的工作,后续将单独写一篇文章来讲解。
查找指定库的建议
虽然通常来说第三方开源库都会提供安装选项,并且安装到CMake(系统)默认的搜索位置,但假如我们希望我们的项目能够跨平台运行,那么就需要使用源码发布的方式,将所有开源的第三方库源码包含在你的源码树中。在这种情况下我们可以参考find_package
的搜索路径设置对应的变量即可:
在明确要调用库的
<package_name>Config.cmake
或<lower-case-package-name>-config.cmake
路径的情况下,直接设置<package_name>_DIR
路径即可:假如需要查找多个库,那么建议将所有的
.cmake
配置文件都放在一个共同的目录下,然后设置CMAKE_PREFIX_PATH
变量指向放置这些配置文件的目录,需要注意根据上述的匹配规则,此时每个包的配置文件需要单独放置在命名为包名的文件夹下(文件夹名不区分大小写),否则会提示找不到。
Last updated