https://ukabuer.me/blog/manage-deps-with-cmake
https://zhuanlan.zhihu.com/p/102050750
- add_subdirectory(thirdParty/abc_path ${DEPS_PATH}/libs) #添加第三方被依赖项目Cmake及库的安装路径
# add_library(abc_static STATIC ${abc_SRCS}) #生成的第三方库
- add_executable(xxx_server ${SRC_LIST})
TARGET_LINK_LIBRARIES( abc_static ) #把第三方库连接进去
在 C/C++ 项目中使用第三方库有两种方式:
- 第三方库在项目外部单独构建:从库的官网或是系统包管理程序上下载预编译好的包,或者事先在项目外部的其他路径下使用库的源码进行编译
- 第三方库的构建集成到项目的构建过程里,从源码开始编译
第一种方式对外部环境编译的要求是不确定的,很可能会打击构建项目的积极性,毕竟并不是所有的平台/发行版/系统版本都能轻松完成各种库的编译和安装。但这种方式很适合编译时间久或者工具链复杂的第三方库,比如说 Qt、V8、OSPRay 等。
第二种方式对开发者比较友好,简单粗暴的实现方式是使用 Git Submodule 拉取依赖源码,或者编写一些脚本管理第三方库。 但如果是使用 CMake 作为构建系统的项目,我们可以利用 CMake 的 FetchContent 模块来管理依赖。 FetchCotent 是 CMake 3.11 版本开始引入的依赖管理模块,和其他方式相比主要有以下几个优点:
- 支持 Git Clone、下载源码压缩包等多种方式获取代码
- 可以处理依赖树中存在的重复依赖
- 在 CMake Configure 阶段拉取代码,build 阶段编译代码,符合 CMake 原有机制,减少了执行多个命令的麻烦
- 用 CMake 一套工具控制一切编译、安装任务
上面提到了两种使用第三方库的方式,在 CMake 项目中还可以分出两种子情况,即第三方库是否也使用 CMake 作为构建系统,下面就介绍如何处理这四种情况。
1、第三方库使用 CMake, 并集成到项目的构建过程里
这种情况可以使用FetchContent
模块获取第三方库的源码,核心函数只有两个:FetchContent_Declare
和FetchContent_Populate
,前者用于声明信息,后者用于下载代码。
下面的例子声明了两个依赖,AAA 和 bbb:
include(FetchContent) # 引入该CMake模块 FetchContent_Declare( # 声明依赖的相关信息 AAA GIT_REPOSITORY https://github.com/AAA/AAA.git GIT_TAG v1.0.0 GIT_SHALLOW TRUE # 不拉取完整历史,相当于`git clone --depth=1` ) FetchContent_Declare( bbb URL https://bbb.com/v2.0.0/bbb.tar.gz HASH qwerty # 可选,确保文件的正确性 )
但仅声明不会有代码被下载,还需要执行FetchContent_Populate
才能使代码能在 Configure 阶段被下载,下载前也可以设置一些变量对子 CMake 项目进行控制:
set(AAA_BUILD_TESTS OFF) # 设置好变量用于关掉AAA项目的测试 FetchContent_GetProperties(AAA) if(NOT AAA_POPULATED) # 确保只拉取一次 FetchContent_Populate(AAA) # 此函数执行后将设置AAA_POPULATED变量 # fetchContent通过AAA_SOURCE_DIR和AAA_BINARY_DIR就可以拿到源码所在目录的路径以及编译产物的目标路径 # 此外还有其他变量可以用,见CMake FetchContent文档 add_subdirectory(${AAA_SOURCE_DIR} ${AAA_BINARY_DIR}) endif () FetchContent_GetProperties(bbb) if(NOT bbb_POPULATED) FetchContent_Populate(bbb) add_subdirectory(${bbb_SOURCE_DIR} ${bbb_BINARY_DIR}) endif ()
add_subdirectory
后,AAA 项目的 target 都会进入到当前项目的作用域里,使用target_link_libraries
即可完成关联。 (如果不了解 target 的概念,可以看我的另一篇文章:现代 CMake 的设计理念和使用。)
AAA_POPULATED
这个变量会被FetchContent_Populate
设置,可以用于确保同名依赖只被拉取一次。 因此当依赖树中存在同名的重复依赖时,最先被拉取的将会覆盖其他的版本。 假设上面的 AAA 和 bbb 两个依赖,同时使用FetchContent_Declare
声明依赖了不同版本的 Ccc。如果 AAA 项目先执行FetchContent_Populate
,则最终 Ccc 项目会使用 AAA 项目中定义的版本。 除此之外,我们还可以在声明 AAA 和 bbb 两个依赖前,提前 populate 特定版本的 Ccc,就可以实现版本的覆盖。
顺带一提,所有使用 FetchContent 模块下载的源码相关目录都在 build 目录下的_deps
文件夹里。
2、第三方库未使用 CMake,将其集成到项目的构建过程里
使用的第三方库不一定使用了 CMake,或者使用不是现代 CMake。这些情况下利用FetchContent_GetProperties
可以拿到依赖库的各种目录,结合 CMake 的其他命令完成各种操作。
比如Eigen
这个 header-only 库,虽然使用了 CMake,但项目中测试相关的 target 过多,并且难以方便的禁用,我们可以在拿到源代码路径后自己创建一个简单的 target
include(FetchContent) FetchContent_Declare( eigen3 URL https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.tar.bz2 URL_MD5 b9e98a200d2455f06db9c661c5610496 ) FetchContent_GetProperties(eigen3) if (NOT eigen3_POPULATED) FetchContent_Populate(eigen3) endif () add_library(eigen INTERFACE) target_include_directories(eigen INTERFACE ${eigen3_SOURCE_DIR})
还有很多使用 make 作为编译工具的项目,我们可以通过拿到源码目录后,使用add_custom_command
和add_custom_target
原地编译,并创建一个简单的 imported target。 这里以uWebSockets为例,这个库本身是 header-only 的,但使用 Git Submodules 依赖了一个使用 make 的子项目 uSockets:
# 常规操作,declare后polulate
include(FetchContent) FetchContent_Declare( uWebSockets-git GIT_REPOSITORY https://github.com/uNetworking/uWebSockets.git GIT_TAG v18 ) FetchContent_GetProperties(uWebSockets-git) if (NOT uWebSockets-git_POPULATED) FetchContent_Populate(uWebSockets-git) endif () # 创建一个命令用于编译出uSockets的静态库,并且创建好头文件目录 add_custom_command( OUTPUT ${uWebSockets-git_SOURCE_DIR}/uSockets/uSockets.a COMMAND cp -r src uWebSockets && make WORKING_DIRECTORY ${uWebSockets-git_SOURCE_DIR} COMMENT "build uSockets" VERBATIM ) # 创建一个自定义target,依赖上面自定义命令的OUTPUT,但这样CMake还不会编译这个target,还需要一个真正的target依赖此target add_custom_target(uSockets DEPENDS ${uWebSockets-git_SOURCE_DIR}/uSockets/uSockets.a) # 创建一个imported target,依赖上面的自定义target,从而确保在使用这个imported target时,上面的编译命令能被执行 add_library(uWebSockets STATIC IMPORTED) set_property(TARGET uWebSockets PROPERTY IMPORTED_LOCATION ${uWebSockets-git_SOURCE_DIR}/uSockets/uSockets.a) set_target_properties( uWebSockets PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${uWebSockets-git_SOURCE_DIR};${uWebSockets-git_SOURCE_DIR}/uSockets/src" ) add_dependencies(uWebSockets uSockets) # 见上面add_custom_target的说明
总之当拿到源码目录后,可以结合 CMake 的其他命令完成各种操作,毕竟我们需要的可以只有头文件和链接库文件。
3、第三方库使用 CMake,在项目外部构建
一个靠谱的 CMake Library 项目应该在 install package 时提供xxx-config.cmake
或者XXXConfig.cmake
文件,其中包含项目相关的 imported target, 或者设置链接库路径等 CMake 变量。 (具体怎么做可以参考 CMake 官方的这个教程: Adding Export Configuration (Step 11),或者这篇更详细的指导:Tutorial: Easily supporting CMake install and find_package())
这种情况下可以使用find_package
命令来寻找依赖。假设库的名称为Aaa
,调用find_package(Aaa 1.0.0)
时,CMake 会尝试在/usr/lib/cmake
等默认路径下寻找Aaa-config.cmake
或者AaaConfig.cmake
,这个文件可以放在以Aaa*
为前缀的文件夹下来支持多版本并存。(Linux 用户可以执行ls /usr/lib/cmake
看看)
当然,这个第三方库不一定就安装在默认路径,那么用户可以设置Aaa_DIR
这个变量,用于提示 CMake 应该去哪里寻找 config 文件。 在找到该文件后,Aaa_FOUND
变量会被设置,同时 config 文件中包含的 target 以及 CMake 变量都会存在于fink_packge
之后的的作用域里,可以按需使用。
4、第三方库未使用 CMake,在项目外部构建
现实并不总是那么美好,第三方库安装时可能没有提供 config 文件,比如使用make
作为构建工具的项目。
我们可以直接使用find_path
, find_library
两个命令来寻找头文件以及链接库所在的路径,CMake 会尝试到默认路径下寻找, 但同样的,库不一定被安装在默认路径下,于是我们可以允许使用一个变量来提示位置:
# 可以设置POCO_INCLUDE_DIR这个变量进行路径的提示
find_path( POCO_INCLUDE_PATH NAMES Poco.h Poco/Poco.h HINTS ${POCO_INCLUDE_DIR} "${CMAKE_PREFIX_PATH}/include" ) # 可以设置POCO_LIB_DIR这个变量进行路径的提示 find_library( POCO_FOUNDATION_LIB NAMES PocoFoundation HINTS ${POCO_LIB_DIR} "${CMAKE_PREFIX_PATH}/lib" )
在找到头文件以及链接库后,我们可以直接用,或者创建个 imported target 使用。
add_library(Poco)STATIC IMPORTED) set_property(TARGET Poco PROPERTY IMPORTED_LOCATION ${POCO_FOUNDATION_LIB}) set_target_properties( Poco PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${POCO_INCLUDE_DIR} )
更优雅的方式是向 CMake 提供一个FindXxx.cmake
脚本,其中可以使用各种方法(比如find_path
和find_library
)找到库,并并导出库的信息。
上一节提到find_package(Aaa 1.0.0)
会去寻找 config 文件,这个描述实际上并不完整。 find_package
有MODULE
和CONFIG
两种模式,MODULE
模式寻找FindXxx.cmake
文件,CONFIG
模式寻找 config 文件。 如果像本文里没有指定模式,CMake 优先按MODULE
模式寻找库,没找到的话 fallback 到CONFIG
模式。(详见Basic Signature and Module Mode)。两者一个重要的区别在于,config 脚本由库的开发者提供,find 脚本由使用者提供。
很多基于 make 构建工具的第三方库都可以在网上可以找到 find 脚本,同时 CMake 官方也为我们写好了很多常用库的Find 脚本,比如 OpenGL, JPEG, ZLIB,对于这些库无需编写 find 脚本直接使用find_package
就可以了。
寻找 find 脚本时,CMake 会优先到CMAKE_MODULE_PATH
变量对应的路径找,随后是 CMake 自带的 find 脚本目录。 如果我们准备好了某个库的 find 脚本,可以把其所在的目录加到CMAKE_MODULE_PATH
里,这样find_package
就能找到他。
list(APPEND CMAKE_MODULE_PATH "./cmake/") find_package(MyLib) if (MyLib_FOUND) # ...
5、创建对下游友好的 CMake 项目
目前想到了下面这几点,对于最佳实践的追求总是没有尽头的,但希望大家可以一起建设更友好的 C/C++ 开发生态:
- 使用基于 target 的现代 CMake
- 作为库的开发者,在预编译的 package 里提供 config 脚本
- 代码仓库里不要放太多代码无关的大文件,避免下载时间过长
- 打好版本 tag,方便控制版本
https://zhuanlan.zhihu.com/p/60479441
CMake的find_package指令用于查找并载入一个外部包的设置。
基本调用形式和模块模式
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE])
查找并载入一个外部包的设置。查找是否成功可以通过<PackageName>_FOUND变量的值得到。成功查找到包后,我们就可以导入包中的目标。使用QUIET选项可以不显示查询过程中产生的信息,比如如果没有使用REQUIRED选项,即使包没有被找到,也不会显示任何信息。使用REQUIRED选项后,如果包没有被找到,就会产生一个错误信息,中断处理。
对于必须的包组件可以在COMPONENTS选项后列出(如果使用了REQUIRED选项,也可以在REQUIRED选项后列出)。对于可选的包组件可以在OPTIONAL_COMPONENTS选项后列出。包自身定义了可用的包组件和包被认为找到的条件。
[version]选项用于指定要查找的包的版本(版本格式为major[.minor[.patch[.tweak]]])。使用EXACT选项后,查找的包的版本需要和指定的版本准确匹配。如果没有使用[version]选项,没有给出需要的包组件列表,就会使用外部调用的相应参数(包括[version]的EXACT选项)。
find_package指令有两种查找包的模式:一种是模块(Module)模式,一种是配置(Config)模式。默认情况下,首先使用模块(Module)模式,如果没有找到对应的模块(Module),就会使用配置(Config)模式。如果使用了MODULE选项,使用模块模式失败后,不会继续使用配置(Config)模式。
模块(Module)模式下,CMake会搜索一个名为Find<PackageName>.cmake。首先使用CMAKE_MODULE_PATH中的路径搜索,然后搜索CMake自带的Find模块。找到Find<PackageName>.cmake后,CMake会读取处理这个文件。这个文件包含了要查找的包的信息。
完整调用形式和配置模式
一般而言,用户只需要使用find_package指令的基本调用形式即可。find_package指令的完整调用形式主要是用来对搜索过程进行更加准确详细的设置。
find_package指令的完整调用形式如下:
find_package(<PackageName> [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_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选项或它的同义词NO_MODULE选项,以及其它没有在find_package的基本调用形式中出现的选项都会强制find_package指令直接使用配置(Config)模式,跳过模块模式处理。
配置模式下,CMake会搜索一个由包提供的配置文件。
指令返回后,<PackageName>_DIR变量保存了包含这一配置文件的目录名。
默认情况下,find_package指令使用<PackageName>作为包名进行搜索。
如果使用了NAMES选项,就会使用NAMES选项后的列表中的名称作为包名搜索<PackageName>Config.cmake文件/<package-name>-config.cmake 文件。
可以使用CONFIGS选项替换使用的配置文件名称。找到配置文件后,配置文件会被CMake读取并执行。配置文件是由包自身提供,所以具备了包的所有信息。配置文件的完整路径会被存储在<PackageName>_CONFIG变量中。
在搜索过程中所有被查找过的可能被使用的包的配置文件会被存储在<PackageName>_CONSIDERED_CONFIGS变量中,包的版本会被存储在<PackageName>_CONSIDERED_VERSIONS变量中。
如果最后没有找到包的配置文件,并且没有使用QUIET选项,那么CMake就会产生一条描述这一错误的信息。
如果使用了REQUIRED选项,包没有被找到就会导致CMake产生一个致命错误,中断处理。如果<PackageName>_DIR变量已经被设置为一个不包含包配置文件的目录,CMake会忽略掉它,重新开始搜索。
包配置文件应该被安装在下面的搜索过程小节提到的搜索路径来方便查找。
包名大小写无关
message("gflags_CONFIG: ${gflags_CONFIG}")
message("GFLAGS_CONFIG: ${gflags_CONFIG}")
message("gflags_INCLUDE_DIR: ${GFLAGS_INCLUDE_DIR}")
message("GFLAGS_LIB: ${GFLAGS_LIB}")
版本选择
配置模式下,如果使用了[version]选项,将会搜索与指定的版本相匹配的包(版本格式为major[.minor[.patch[.tweak]]])。如果使用了EXACT选项,则会搜索与指定版本准确匹配的包。CMake没有对版本号有任何约定。包的版本号由包自身提供的版本文件得到。对于一个候选的包配置文件<config-file>.cmake,它对应的版本文件<config-file>-version.cmake/<config-file>Version.cmake在同一目录下。如果对应的版本文件不存在,包配置文件假设包与任何指定的版本都不匹配。可以使用CMakePackageConfigHelpers模块创建一个最基本的版本文件。版本文件找到后,会被读取用来检测包的版本号。版本文件会被在一个包含了以下变量的内部作用域下读取执行:
- PACKAGE_FIND_NAME:<PackageName>
- PACKAGE_FIND_VERSION:完整的版本字符串
- PACKAGE_FIND_VERSION_MAJOR:主版本号,如果没有指定则为0
- PACKAGE_FIND_VERSION_MINOR:次版本号,如果没有指定则为0
- PACKAGE_FIND_VERSION_PATCH:补丁版本号,如果没有指定则为0
- PACKAGE_FIND_VERSION_TWEAK:调整版本号,如果没有指定则为0
- PACKAGE_FIND_VERSION_COUNT:指定的版本号数量,值为0到4
版本文件使用上面这些变量检测包版本是否满足,然后设置下面这些变量的值:
- PACKAGE_VERSION:包的完整版本字符串
- PACKAGE_VERSION_EXACT:如果包版本准确匹配,这一变量被设置为true
- PACKAGE_VERSION_COMPATIBLE:如果包版本匹配,这一变量被设置为true
- PACKAGE_VERSION_UNSUITABLE:如果包不匹配任何版本,这一变量被设置为true
上面这些变量会被find_package指令检测用来确定包的版本是否可以被接收。它们在find_package指令返回后不可用。如果包的版本可以被接收,下面这些变量会被设置:
- <PackageName>_VERSION:包的完整版本字符串
- <PackageName>_VERSION_MAJOR:主版本号,如果没有提供则为0
- <PackageName>_VERSION_MINOR:次版本号,如果没有提供则为0
- <PackageName>_VERSION_PATCH:补丁版本号,如果没有提供则为0
- <PackageName>_VERSION_TWEAK:调整版本号,如果没有提供则为0
- <PackageName>_VERSION_COUNT:使用的版本号数量,值为0到4
当多个包配置文件匹配指定的包版本时,如果没有设置CMAKE_FIND_PACKAGE_SORT_ORDER变量进行排序,哪一个包被选定是未定义的,
使用CMAKE_FIND_PACKAGE_SORT_ORDER和CMAKE_FIND_PACKAGE_SORT_DIRECTION变量可以控制find_package指令的检测顺序。比如,为了选择版本最高的包,可以在调用find_package指令前按照下面这样设置:
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
搜索过程
CMake包含了一组前缀用于查找安装的包。这一组前缀之后连接了多个用于查找配置文件的目录。下面列出了这些目录。W代表在Windows下使用,U代表在UNIX下使用,A代表在Apple下使用的目录:
<prefix>/ (W) <prefix>/(cmake|CMake)/ (W) <prefix>/<name>*/ (W) <prefix>/<name>*/(cmake|CMake)/ (W) <prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/ (U) <prefix>/(lib/<arch>|lib*|share)/<name>*/ (U) <prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (U) <prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/ (W/U) <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/ (W/U) <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (W/U)
对于macOS系统,会使用下面的目录搜索配置文件:
<prefix>/<name>.framework/Resources/ (A) <prefix>/<name>.framework/Resources/CMake/ (A) <prefix>/<name>.framework/Versions/*/Resources/ (A) <prefix>/<name>.framework/Versions/*/Resources/CMake/ (A) <prefix>/<name>.app/Contents/Resources/ (A) <prefix>/<name>.app/Contents/Resources/CMake/ (A)
上面目录中的<name>(大小写无关)对应我们在find_package指令中指定的名称(<PackageName>和使用NAMES选项指定的名称)。
如果设置了CMAKE_LIBRARY_ARCHITECTURE变量,就会启用lib/<arch>这一搜索路径。lib*包含了类似lib64、lib32、libx32和lib(按这一顺序搜索)这些值:
- 如果FIND_LIBRARY_USE_LIB64_PATHS变量被设置为TRUE,会启用lib64搜索路径。
- 如果FIND_LIBRARY_USE_LIB32_PATHS变量被设置为TRUE,会启用lib32搜索路径。
- 如果FIND_LIBRARY_USE_LIBX32_PATHS变量被设置为TRUE,会启用libx32搜索路径。
- lib搜索路径总是被使用。
如果使用了PATH_SUFFIXES选项,指定的后缀会被加到W和U类型目录名称之后。
这一组目录主要用于和在安装树中提供了配置文件的程序进行协作。
上面标记了W的目录主要用于Windows系统,通常是程序的安装目录。标记了U的目录主要用于UNIX系统,通常是系统的默认包目录。
但实际上标记了W和U的目录会被所有平台搜索。标记了A的目录主要用于苹果平台。CMAKE_FIND_FRAMEWORK和CMAKE_FIND_APPBUNDLE变量决定了苹果平台的搜索顺序。
这一组目录的前缀由下面得到。
如果使用了NO_DEFAULT_PATH选项,那么所有NO_*选项都会被启用。
1.CMake变量<PackageName>_ROOT和环境变量<PackageName>_ROOT中指定的搜索路径(<PackageName>是要搜索的包名)。
包的root路径变量以栈的形式维护,如果没有使用NO_PACKAGE_ROOT_PATH选项,在一个搜索模块中调用find_package指令,来自搜索模块的root路径也会被搜索。
2.CMake的隐含变量指定的搜索路径。主要是在命令行上使用-DVAR=value来指定。指定的搜索路径以分号分割。可以使用NO_CMAKE_PATH选项跳过这一搜索路径:
CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH
3.使用CMake的环境变量指定的搜索路径。主要来自用户的终端配置,指定的搜索路径以原生的路径分隔符(在Windows上是;,在UNIX上是:)进行分割。可以使用NO_CMAKE_ENVIRONMENT_PATH跳过这一搜索路径:
<PackageName>_DIR CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH
4.使用HINTS选项指定的搜索路径。这一搜索路径通常由计算得到。比如使用一个已有信息作为搜索路径的提示。
如果要使用硬编码的路径信息应该使用PATHS选项。
5.系统的环境变量指定的搜索路径。使用NO_SYSTEM_ENVIRONMENT_PATH选项可以跳过这一搜索路径。路径中以/bin和/sbin结尾的目录会被自动转换为它们的父目录:
PATH
6.CMake的用户包注册表指定的搜索路径。使用NO_CMAKE_PACKAGE_REGISTRY选项或设置CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY变量为TRUE可以跳过这一搜索路径。
7.当前系统平台的CMake变量指定的搜索路径。使用NO_CMAKE_SYSTEM_PATH可以跳过这一搜索路径:
CMAKE_SYSTEM_PREFIX_PATH CMAKE_SYSTEM_FRAMEWORK_PATH CMAKE_SYSTEM_APPBUNDLE_PATH
8.CMake的系统包注册表指定的搜索路径。使用NO_CMAKE_SYSTEM_PACKAGE_REGISTRY选项或设置CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY变量为TRUE可以跳过这一搜索路径。
9.使用PATHS选项指定的搜索路径。也就是硬编码的搜索路径。
使用CMake变量CMAKE_FIND_ROOT_PATH可以指定一个或多个目录作为其它搜索路径的前缀。使用它我们可以重定位整个搜索路径。使用CMAKE_STAGING_PREFIX变量衍生的搜索路径是一个系统平台特定路径,不会被重定位。默认情况下,CMAKE_FIND_ROOT_PATH变量的值为空。
使用CMAKE_SYSROOT变量也可以指定一个搜索路径前缀。
这些变量对于跨平台编译有很大的作用。默认情况下,CMake首先使用CMAKE_FIND_ROOT_PATH中的路径,然后使用CMAKE_SYSROOT中的路径,接着使用非根路径。可以通过设置CMAKE_FIND_ROOT_PATH_MODE_PACKAGE变量调整这一默认搜索方式。可以使用下面这些选项针对单个find_package指令进行控制:
- CMAKE_FIND_ROOT_PATH_BOTH:按照上面的描述进行搜索。
- NO_CMAKE_FIND_ROOT_PATH:不使用CMAKE_FIND_ROOT_PATH变量进行搜索。
- ONLY_CMAKE_FIND_ROOT_PATH:只搜索重定位的目录和 CMAKE_STAGING_PREFIX变量衍生的目录。
默认情况下的搜索顺序满足大多数使用场景。一般情况下,我们只需要使用NO_*类选项多次调用find_package指令就可以按照我们的要求来搜索包:
find_package (<PackageName> PATHS paths... NO_DEFAULT_PATH) find_package (<PackageName>)
上面的两次find_package指令只要有一次成功找到了包,就不会再次搜索这个包。
默认情况下,存储在结果变量中的值是最后找到的文件路径。在使用find_package指令前设置CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS变量的值为TRUE,可以将找到的符号链接解析为真实的文件路径。
通过设置CMAKE_DISABLE_FIND_PACKAGE_<PackageName>变量为TRUE可以让非REQUIRED的find_package指令调用失效。
包文件接口变量
载入一个搜索模块或包配置文件时,find_package指令设置了一些有关调用参数的变量(find_package指令调用返回后,这些变量会被还原):
- CMAKE_FIND_PACKAGE_NAME:搜索的包名
- <PackageName>_FIND_REQUIRED:如果使用了REQUIRED选项,值为TRUE
- <PackageName>_FIND_QUIETLY:如果使用了QUIET选项,值为TRUE
- <PackageName>_FIND_VERSION:搜索的包的完整版本字符串
- <PackageName>_FIND_VERSION_MAJOR:包的主版本号,如果没有指定则为0
- <PackageName>_FIND_VERSION_MINOR:包的次版本号,如果没有指定则为0
- <PackageName>_FIND_VERSION_PATCH:包的补丁版本号,如果没有指定则为0
- <PackageName>_FIND_VERSION_TWEAK:包的调整版本号,如果没有指定则为0
- <PackageName>_FIND_VERSION_COUNT:包使用的版本数量,值为0到4
- <PackageName>_FIND_VERSION_EXACT:如果使用了EXACT选项,值为TRUE
- <PackageName>_FIND_COMPONENTS:搜索的包的组件
- <PackageName>_FIND_REQUIRED_<c>:如果组件<c>被指定为必须,值为TRUE。如果组件<c>是可选的,值为FALSE
在模块(Module)模式下,被加载的搜索模块负责查找满足这些信息的包。在配置(Config)模式下,find_package指令会自动处理REQUIRED、QUIET和[version]选项,然后让包配置文件自己处理组件需求。包配置文件通过设置<PackageName>_FOUND变量为FALSE表明组件需求不能够被满足。
if(MINGW) option(WITH_GFLAGS "build with GFlags" OFF) else() option(WITH_GFLAGS "build with GFlags" ON) endif() set(GFLAGS_LIB) if(WITH_GFLAGS) # set(gflags_ROOT /home/gitSrc/google/gflags-2.2.0) set(gflags_DIR /home/gitSrc/google/gflags-2.2.0/build) # Config with namespace available since gflags 2.2.2 option(GFLAGS_USE_TARGET_NAMESPACE "Use gflags import target with namespace." ON) find_package(gflags CONFIG PATHS /home/gitSrc/google/gflags-2.2.0 NO_DEFAULT_PATH) if(gflags_FOUND) message("=======gflags found========") if(TARGET ${GFLAGS_TARGET}) # Config with GFLAGS_TARGET available since gflags 2.2.0 set(GFLAGS_LIB ${GFLAGS_TARGET}) else() # Config with GFLAGS_LIBRARIES available since gflags 2.1.0 set(GFLAGS_LIB ${gflags_LIBRARIES}) endif() else() message("=======gflags not found========") find_package(gflags REQUIRED PATHS /home/gitSrc/google/gflags-2.2.0 NO_DEFAULT_PATH) set(GFLAGS_LIB gflags::gflags) endif() include_directories(${GFLAGS_INCLUDE_DIR}) list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIB}) add_definitions(-DGFLAGS=1) message("gflags_CONFIG: ${gflags_CONFIG}") message("GFLAGS_CONFIG: ${gflags_CONFIG}") message("gflags_INCLUDE_DIR: ${GFLAGS_INCLUDE_DIR}") message("GFLAGS_LIB: ${GFLAGS_LIB}") endif() CMake Error at CMakeLists.txt:142 (find_package): Could not find a package configuration file provided by "gflags" with any of the following names: gflagsConfig.cmake gflags-config.cmake Add the installation prefix of "gflags" to CMAKE_PREFIX_PATH or set "gflags_DIR" to a directory containing one of the above files. If "gflags" provides a separate development package or SDK, be sure it has been installed. set(gflags_DIR /home/gitSrc/google/gflags-2.2.0/build) #有gflagsconfig.cmake的直接路径
find_package(gflags REQUIRED
PATHS /home/gitSrc/google/gflags-2.2.0
COMPONENTS static #使用静态库
NO_DEFAULT_PATH)