CMake中的命令find_package用于查找指定的package。
find_package支持两种主要的搜索方法:注意:
(1).Config mode(配置模式):使用该方法,find_package会查找通常由package本身提供的文件。在这种模式下,CMake会搜索名为
config-file packages:config file是package附带的文本文件,用于定义CMake targets, variables, commands等等。config file是一个普通的CMake脚本,由find_package命令读入。config files通常可以在lib/cmake/
config file必须命名为
如果找到
如果package的位置在CMake已知的目录中,则find_package调用应该成功。CMake已知的目录是特定于平台(platform-specific)的。例如,在Linux上使用标准系统包管理器(standard system package manager)安装的package将自动在/usr前缀中找到。在Windows上安装在Program Files中的package同样会被自动找到。
如果package位于CMake不知道的位置,例如/opt/lib,则不会在没有帮助的情况下自动找到它们。这是正常情况,CMake为用户提供了几种方法指定在哪里可以找到此类库。
CMAKE_PREFIX_PATH变量可以在调用CMake时设置。它被视为搜索config file的基本路径列表。安装在/opt/lib/somepackage中的package通常会安装config file,例如/opt/lib/somepackage/lib/cmake/SomePackageConfig.cmake。在这种情况下,应将/opt/lib/somepackage添加到CMAKE_PREFIX_PATH。
环境变量CMAKE_PREFIX_PATH也可以填充前缀以搜索package。与PATH环境变量一样,这是一个列表,但它需要使用特定于平台的环境变量列表项分隔符,如在Linux上为":",在Windows上为";"。
CMAKE_PREFIX_PATH变量在需要指定多个前缀的情况下提供了便利,或者当多个package在同一前缀下可用时。也可以通过设置匹配
(2).Module mode(模块模式):并非所有packages都支持CMake,许多不提供支持config modes所需的文件。对于这种情况,可以由项目(project)或CMake单独提供Find module文件。Find module通常是一种启发式实现(heuristic implementation),它知道package通常提供什么以及如何将该package呈现给项目(project)。由于Find module通常与package分开发布(distribute),因此它们不那么可靠。它们通常是单独维护的,并且可能遵循不同的发布时间表,因此它们很容易过时。在这种模式下,CMake搜索名为Find
根据使用的参数,find_package可以使用上述方法之一或两者。
通过将选项限制为仅基本签名(basic signature),可以使用config mode和module mode来满足依赖关系。其它选项的存在可能会将调用限制为仅用两种方法中的一种,从而可能降低find_package查找依赖项的能力。
Find Module Files:如果FindSomePackage.cmake文件可用,仍然可以使用find_package命令找到不提供config file的package。这些Find module files与config files的不同之处在于:
A.Find module files不应由package本身提供。
B.Find
C.CMake不会在CMAKE_PREFIX_PATH变量中指定的位置搜索Find
D.CMake为某些第三方packages提供Find
(3).FetchContent redirection mode:3.24版本中引入。对find_package的调用可以在内部重定向到FetchContent module提供的package。对调用者而言,此行为类似于Config mode,只是绕过了搜索逻辑并且不使用组件信息。
当未重定向到FetchContent提供的package时,由find_package参数决定使用Module mode还是Config mode。
当使用基本签名时,find_package首先在Module mode下搜索。如果未找到package,则搜索回退到Config mode。用户可以将CMAKE_FIND_PACKAGE_PREFER_CONFIG变量设置为true以反转优先级并指示CMake在返回到Module mode之前首先使用Config mode进行搜索。也可以强制基本签名仅使用带有MODULE关键字的Module mode。如果使用完整签名,则find_package仅在Config mode下搜索。
在可能的情况下,用户代码通常应该使用基本签名来查找package,因为这样可以在任何mode下找到package。
Imported Targets:表示一个预先存在的依赖性。通常这样的targets是由上游package定义的,应该被视为不可变的。在声明一个Imported Target之后,可以像使用任何其它常规target一样,使用诸如target_compile_definitions, target_include_directories, target_compile_options或target_link_libraries之类的命令来调整其target属性。
config files和Find module files都可以定义Imported Targets。这些通常具有SomePrefix::ThingName形式的名称。
find_package的基本签名(Basic Signature)如下:
find_package( [version] [EXACT] [QUIET] [MODULE]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER]
)
Module和Config modes都支持基本签名。选项包括:无论使用何种模式,都会设置一个
(1).MODULE:意味着只能使用Module mode来查找package,而不能回退到Config mode。示例代码段如下:
find_package(OpenCV) # Found OpenCV: /usr (found version "4.5.4")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /usr/include/opencv4
find_package(OpenCV MODULE) # CMake Warning at test_find_package.cmake:10 (find_package): No "FindOpenCV.cmake" found in CMAKE_MODULE_PATH
message("CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") # CMAKE_MODULE_PATH:
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
(2).QUIET:禁用消息性消息(disables informational messages),包括那些指示如果package不是REQUIRED则无法找到的消息。示例代码段如下:
find_package(OpenCV QUIET) # 不会再显示: Found OpenCV: /usr (found version "4.5.4")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
find_package(OpenCV MODULE QUIET) # 不会再显示: CMake Warning at test_find_package.cmake:17 (find_package): No "FindOpenCV.cmake" found in CMAKE_MODULE_PATH
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
(3).REQUIRED:如果找不到package,则REQUIRED选项会停止处理并触发错误消息。示例代码段如下:
find_package(OpenCV MODULE REQUIRED) # CMake Error at test_find_package.cmake:20 (find_package): No "FindOpenCV.cmake" found in CMAKE_MODULE_PATH
(4).COMPONENTS:此关键字之后可能会列出所需组件的特定于package(package-specific)列表。如果不能满足这些组件中的任何一个,则认为未找到整个package。如果还存在REQUIRED选项,则将其视为fatal error,否则仍会继续执行。作为一种简写模式,如果存在REQUIRED选项,则可以省略COMPONENTS关键字,并且可以在REQUIRED之后直接列出所需的组件。示例代码段如下:
find_package(OpenCV COMPONENTS opencv_core) # Found OpenCV: /usr (found version "4.5.4") found components: opencv_core
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
find_package(OpenCV COMPONENTS opencv_xxxx) # Could NOT find OpenCV (missing: opencv_xxxx) (found version "4.5.4"))
# CMake Warning at test_find_package.cmake:25 (find_package): Found package configuration file:
# /usr/lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake
# but it set OpenCV_FOUND to FALSE so package "OpenCV" is considered to be NOT FOUND.
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
(5).OPTIONAL_COMPONENTS:其它可选组件可能会在OPTIONAL_COMPONENTS之后列出。如果这些都不能满足,只要满足所有必需的组件,仍然可以认为整个package都找到了。
可用组件的集合及其含义由target package定义。示例代码段如下:
find_package(OpenCV COMPONENTS opencv_core opencv_highgui) # Found OpenCV: /usr (found version "4.5.4") found components: opencv_core opencv_highgui
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
find_package(OpenCV COMPONENTS opencv_core opencv_highgui OPTIONAL_COMPONENTS opencv_xxxx opencv_yyyy) # Found OpenCV: /usr (found version "4.5.4") found components: opencv_core opencv_highgui missing components: opencv_xxxx opencv_yyyy
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
(6).REGISTRY_VIEW:3.24版本中引入。指定应要查询的注册表视图(registry views)。此选项仅在Windows平台上有意义,在其它平台上将被忽略。
(7).GLOBAL:3.24版本中引入。指定此关键字会将所有imported targets提升到导入项目(improting project)的全局范围。或者可以通过设置CMAKE_FIND_PACKAGE_TARGETS_GLOBAL变量来启用此功能。
(8).version:此参数请求找到的package应该兼容的版本。有两种可能的形式可以指定它:
单个版本(single version):格式为:major[.minor[.patch[.tweak]]],其中每个组件都是一个数值。
版本范围(version range):3.19版本中引入。格式为:versionMin...[<]versionMax,其中versionMin和versionMax具有与单个版本相同的格式和约束。默认情况下,两个端点都包括在内。通过指定<,将排除上端点(the upper end point will be excluded)。
find_package(OpenCV 3.4.2) # Found OpenCV: /opt/opencv3.4.2 (found suitable version "3.4.2", minimum required is "3.4.2")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /opt/opencv3.4.2/include;/opt/opencv3.4.2/include/opencv
find_package(OpenCV 2.4.13.7) # CMake Warning at test_find_package.cmake:41 (find_package):
# Could not find a configuration file for package "OpenCV" that is compatible with requested version "2.4.13.7".
# The following configuration files were considered but not accepted:
# /opt/opencv3.4.2/share/OpenCV/OpenCVConfig.cmake, version: 3.4.2
# /usr/lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake, version: 4.5.4
# /lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake, version: 4.5.4
# /opt/opencv3.1/share/OpenCV/OpenCVConfig.cmake, version: 3.1.0
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
(9).EXACT:此选项要求version完全匹配。此选项与version range的规范不兼容。
find_package(OpenCV 3.4.2) # Found OpenCV: /opt/opencv3.4.2 (found suitable version "3.4.2", minimum required is "3.4.2")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /opt/opencv3.4.2/include;/opt/opencv3.4.2/include/opencv
find_package(OpenCV 3.0.0) # Found OpenCV: /opt/opencv3.4.2 (found suitable version "3.4.2", minimum required is "3.0.0")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
find_package(OpenCV 3.0.0 EXACT) # CMake Warning at test_find_package.cmake:57 (find_package):
# Could not find a configuration file for package "OpenCV" that exactly matches requested version "3.0.0".
# The following configuration files were considered but not accepted:
# /opt/opencv3.4.2/share/OpenCV/OpenCVConfig.cmake, version: 3.4.2
# /usr/lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake, version: 4.5.4
# /lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake, version: 4.5.4
# /opt/opencv3.1/share/OpenCV/OpenCVConfig.cmake, version: 3.1.0
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
find_package的完整签名(Full Signature)如下:
find_package( [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
[GLOBAL]
[NO_POLICY_SCOPE]
[BYPASS_PROVIDER]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
[PATHS path1 [path2 ... ]]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
[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_INSTALL_PREFIX]
[NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
[CMAKE_FIND_ROOT_PATH_BOTH |
ONLY_CMAKE_FIND_ROOT_PATH |
NO_CMAKE_FIND_ROOT_PATH]
)
(1).CONFIG|NO_MODULE:CONFIG选项、同义的NO_MODULE选项或使用基本签名中未指定的选项都强制执行纯Config mode。在纯Config mode下,该命令会跳过Module mode搜索并立即进行Config mode搜索。
find_package(OpenCV 3.1.0 EXACT CONFIG)
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /opt/opencv3.1/include/opencv;/opt/opencv3.1/include
find_package(OpenCV 4.5.4 EXACT NO_MODULE) # Found OpenCV: /usr (found suitable exact version "4.5.4")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /usr/include/opencv4
(2).NAMES:Config mode搜索试图定位要找到的package提供的配置文件。创建一个名为
find_package(opencv) # CMake Warning at test_find_package.cmake:74 (find_package):
# By not providing "Findopencv.cmake" in CMAKE_MODULE_PATH this project has
# asked CMake to find a package configuration file provided by "opencv", but CMake did not find one.
# Could not find a package configuration file provided by "opencv" with any of the following names:
# opencvConfig.cmake
# opencv-config.cmake
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND:
find_package(opencv NAMES OpenCV) # Found OpenCV: /usr (found version "4.5.4")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: TRUE
(3).CONFIGS:find_package为每个指定的名称搜索名为
# 手动copy一份/opt/opencv3.1到/opt/opencv,并将/opt/opencv/share/OpenCV/OpenCVConfig.cmake调整为/opt/opencv/share/opencv/cv_config.cmake
set(OpenCV_DIR "/opt/opencv/share/opencv/")
find_package(OpenCV NO_DEFAULT_PATH) # Could NOT find OpenCV (missing: OpenCV_DIR)
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 0
set(OpenCV_DIR "/opt/opencv/share/opencv/")
find_package(OpenCV CONFIGS cv_config.cmake NO_DEFAULT_PATH)
message("OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") # OpenCV_INCLUDE_DIRS: /opt/opencv/include/opencv;/opt/opencv/include
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_CONFIG: ${OpenCV_CONFIG}") # OpenCV_CONFIG: /opt/opencv/share/opencv/cv_config.cmake
message("OpenCV_CONSIDERED_CONFIGS: ${OpenCV_CONSIDERED_CONFIGS}") # OpenCV_CONSIDERED_CONFIGS: /opt/opencv/share/opencv/cv_config.cmake
message("OpenCV_CONSIDERED_VERSIONS: ${OpenCV_CONSIDERED_VERSIONS}") # OpenCV_CONSIDERED_VERSIONS: unknown
CMake在搜索具有适当版本的package时考虑的所有配置文件都存储在
如果找不到package配置文件,除非指定QUIET参数,否则CMake将生成描述问题的错误。如果指定了REQUIRED并且未找到package,则会生成致命错误(fatal error)并且配置步骤停止执行(configure step stops executing)。如果
find_package(OpenCV) # Found OpenCV: /usr (found version "4.5.4")
message("OpenCV_FOUND: ${OpenCV_FOUND}") # OpenCV_FOUND: 1
message("OpenCV_CONSIDERED_CONFIGS: ${OpenCV_CONSIDERED_CONFIGS}") # OpenCV_CONSIDERED_CONFIGS: /usr/lib/x86_64-linux-gnu/cmake/opencv4/OpenCVConfig.cmake
message("OpenCV_CONSIDERED_VERSIONS: ${OpenCV_CONSIDERED_VERSIONS}") # OpenCV_CONSIDERED_VERSIONS: 4.5.4
message("CMAKE_LIBRARY_ARCHITECTURE: ${CMAKE_LIBRARY_ARCHITECTURE}") # CMAKE_LIBRARY_ARCHITECTURE: x86_64-linux-gnu
message("FIND_LIBRARY_USE_LIB64_PATHS: ${FIND_LIBRARY_USE_LIB64_PATHS}") # FIND_LIBRARY_USE_LIB64_PATHS:
Config mode搜索过程:无论是完整签名还是基本签名,当使用Config mode时都会应用此搜索过程
(1).3.24版本中引入:所有对find_package的调用(即使在Module mode下)首先在CMAKE_FIND_PACKAGE_REDIRECTS_DIR目录中查找config package file。
FetchContent module,甚至project本身,可能会将文件写入该位置,以将find_package调用重定向到project已提供的内容。如果在该位置未找到config package file,则搜索将按照下面描述的逻辑进行:
(2).CMake为package构造了一组可能的安装前缀。在每个前缀下搜索几个目录以查找配置文件。下表显示了搜索的目录:每个条目(each entry)都适用于遵循Windows(W), UNIX(U), 或Apple(A)约定的安装树(installation trees)
/ (W)
/(cmake|CMake)/ (W)
/*/ (W)
/*/(cmake|CMake)/ (W)
/*/(cmake|CMake)/*/ (W)
/(lib/|lib*|share)/cmake/*/ (U)
/(lib/|lib*|share)/*/ (U)
/(lib/|lib*|share)/*/(cmake|CMake)/ (U)
/*/(lib/|lib*|share)/cmake/*/ (W/U)
/*/(lib/|lib*|share)/*/ (W/U)
/*/(lib/|lib*|share)/*/(cmake|CMake)/ (W/U)
/.framework/Resources/ (A)
/.framework/Resources/CMake/ (A)
/.framework/Versions/*/Resources/ (A)
/.framework/Versions/*/Resources/CMake/ (A)
/.app/Contents/Resources/ (A)
/.app/Contents/Resources/CMake/ (A)
在所有情况下,
(3).如果设置了CMAKE_LIBRARY_ARCHITECTURE变量,则启用带有lib/
A.如果FIND_LIBRARY_USE_LIB64_PATHS属性设置为TRUE,则在64位平台上搜索具有lib64的路径。
B.如果FIND_LIBRARY_USE_LIB32_PATHS属性设置为TRUE,则在32位平台上搜索具有lib32的路径。
C.如果FIND_LIBRARY_USE_LIBX32_PATHS属性设置为TRUE,则在x32 ABI平台上搜索具有libx32的路径。
D.总是搜索lib路径。
(4).3.24版本中更改:在Windows平台上,可以使用专用语法将注册表查询作为通过HINTS和PATHS关键字指定的目录的一部分。在所有其它平台上,此类规范将被忽略。
(5).3.24版本中引入:可以指定REGISTRY_VIEW管理Windows注册表查询作为PATHS和HINTS的一部分。
指定必须要查询的注册表视图(registry views)。此选项仅在Windows平台上有意义,在其它平台上将被忽略。如果未指定,则在CMP0134策略为NEW时使用TARGET view。
(6).如果指定了PATH_SUFFIXES,则后缀将一个接一个地附加到每个(W)或(U)目录条目(directory entry)。
(7).如果指定了NO_DEFAULT_PATH,则启用所有NO_*选项。
以上测试代码搜索本机中的OpenCV头文件,本机中有多个版本的OpenCV库:
(1).4.5.4:头文件存放路径,系统默认的安装路径,通过命令sudo apt install libopencv-dev安装:/usr/include/opencv4/; OpenCVConfig.cmake存放路径:/usr/lib/x86_64-linux-gnu/cmake/opencv4/
(2).3.1:头文件存放路径:/opt/opencv3.1/include/; OpenCVConfig.cmake存放路径:/opt/opencv3.1/share/OpenCV/
(3).3.4.2:头文件存放路径:/opt/opencv3.4.2/include/; OpenCVConfig.cmake存放路径:/opt/opencv3.4.2/share/OpenCV/
执行上述测试代码需要3个文件:build.sh, CMakeLists.txt, test_find_package.cmake
build.sh内容如下:
#! /bin/bash
# supported input parameters(cmake commands)
params=(function macro cmake_parse_arguments \
find_library find_path find_file find_program find_package \
cmake_policy)
usage()
{
echo "Error: $0 needs to have an input parameter"
echo "supported input parameters:"
for param in ${params[@]}; do
echo " $0 ${param}"
done
exit -1
}
if [ $# != 1 ]; then
usage
fi
flag=0
for param in ${params[@]}; do
if [ $1 == ${param} ]; then
flag=1
break
fi
done
if [ ${flag} == 0 ]; then
echo "Error: parameter \"$1\" is not supported"
usage
exit -1
fi
if [[ ! -d "build" ]]; then
mkdir build
cd build
else
cd build
fi
echo "==== test $1 ===="
cmake -DTEST_CMAKE_FEATURE=$1 ..
CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.22)
project(cmake_feature_usage)
message("#### current cmake version: ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}")
include(test_${TEST_CMAKE_FEATURE}.cmake)
message("==== test finish ====")
test_find_package.cmake内容:为上面所有的示例代码
执行可能的结果如下图所示:
GitHub: https://github.com/fengbingchun/Linux_Code_Test