find_package()有Module模式(基本用法)和Config模式(彻底用法),其中Module模式是基础,Config模式则提供复杂高级功能。
find_package是否使用Config模式能够经过下列条件判断:
(1)find_package()中指定CONFIG关键字
(2)find_package()中指定NO_MODULE关键字
(3)find_package()中使用了再也不Module模式下全部支持配置的关键字缓存
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...] [NO_POLICY_SCOPE])
version和EXACT,可选,version指定版本,若是指定就必须检查找到的包的版本是否和version兼容。若是指定EXACT则表示必须彻底匹配的版本而不是兼容版本就能够。
QUIET,可选字段,表示若是查找失败,不会在屏幕进行输出(但若是指定了REQUIRED字段,则QUIET无效,仍然会输出查找失败提示语)。
MODULE,可选字段。前面提到说“若是Module模式查找失败则回退到Config模式进行查找”,可是假如设定了MODULE选项,那么就只在Module模式查找,若是Module模式下查找失败并不回落到Config模式查找。
REQUIRED可选字段。表示必定要找到包,找不到的话就当即停掉整个cmake。而若是不指定REQUIRED则cmake会继续执行。
COMPONENTS,components:可选字段,表示查找的包中必需要找到的组件(components),若是有任何一个找不到就算失败,相似于REQUIRED,致使cmake中止执行。
Module模式下须要查找到名为FindPackageName.cmake的文件。
先在CMAKE_MODULE_PATH变量对应的路径中查找。若是路径为空,或者路径中查找失败,则在cmake module directory(cmake安装时的Modules目录,好比/usr/local/share/cmake/Modules)查找。ide
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模式下的查找顺序,比Module模式下要多得多,新版本的CMake比老版本的有更多的查找顺序(新增的在最优先的查找顺序)。Config模式下须要查找到名为lower-case-package-name-config.cmake或PackageNameConfig.cmake文件。查找顺序以下:
(1)名为PackageName_ROOT的cmake变量或环境变量。CMake3.12新增。设定CMP0074 Policy来关闭。若是定义了PackageName_DIR cmake变量,那么PackageName_ROOT 不起做用。工具
cmake_minimum_required(VERSION 3.13) project(OpenCVDemo) set(OpenCV_ROOT "/usr/local/lib/opencv_249/build") set(OpenCV_DIR "/usr/local/lib/opencv_300/build") find_package(OpenCV QUIET NO_MODULE NO_DEFAULT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_PACKAGE_REGISTRY NO_CMAKE_BUILDS_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_SYSTEM_PACKAGE_REGISTRY ) message(STATUS "OpenCV library status:") message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
上述代码会找到opencv300,OpenCV_DIR变量的值有效OpenCV_ROOT变量无效。
(2)cmake特定的缓存变量开发工具
CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH
能够经过设定NO_CMAKE_PATH来关闭特定缓存变量的查找顺序
(3)CMake特定的环境变量ui
PackageName_DIR CMAKE_PREFIX_PATH CMAKE_FRAMEWORK_PATH CMAKE_APPBUNDLE_PATH
能够经过NO_CMAKE_ENVIRONMENT_PATH来跳过。
(4)HINT字段指定的路径
(5)搜索标准的系统环境变量PATH。
其中若是是以/bin或者/sbin结尾的,会自动转化为其父目录。
经过指定NO_SYSTEM_ENVIRONMENT_PATH来跳过。
(6)存储在CMake的"User Package Registry"(用户包注册表)中的路径。
经过设定NO_CMAKE_PACKAGE_REGISTRY,或设定CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY为true,来避开。
(7)设定为当前系统定义的cmake变量:
CMAKE_SYSTEM_PREFIX_PATH
CMAKE_SYSTEM_FRAMEWORK_PATH
CMAKE_SYSTEM_APPBUNDLE_PATH
经过设定NO_CMAKE_SYSTEM_PATH来跳过。
(8)在cmake的"System Package Registry"(系统包注册表)中查找。
经过设定NO_CMAKE_SYSTEM_PACKAGE_REGISTRY跳过,或者经过设定CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY为true避开。
(9)从PATHS字段指定的路径中查找。code
FIND_FILE(VAR name path1 path2 …)
VAR变量表明找到的文件全路径,包含文件名。
FIND_LIBRARY(VAR name path1 path2 …)
VAR变量表明找到的库全路径,包含库文件名。
FIND_PATH(VAR name path1 path2 …)
VAR变量表明包含文件的路径
FIND_PROGRAM(VAR name path1 path2 …)
VAR变量表明包含程序的全路径FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED | COMPONENTS] [componets …]])
用来调用预约义在CMAKE_MODULE_PATH下的Findname.cmake模块,能够本身定义Findname模块,经过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录供工程使用。component
FIND_LIBRARY(libX X11 /usr/lib) IF (NOT libx) MESSAGE(FATAL_ERROR "libX not found") ENDIF(NOT libX)
系统预约义了各类模块,一般须要使用INCLUDE指令显式的调用, 但FIND_PACKAGE指令是一个特例,能够直接调用预约义的模块。
每个模块都会定义如下3个变量:blog
<name>_FOUND <name>_INCLUDE_DIR or <name>_INCLUDES <name>_LIBRARY or <name>_LIBRARIES
所以,能够经过name_FOUND来判断模块是否被找到,若是没有找到,按照工程的须要关闭某些特性、给出提醒或者停止构建。
对于系统预约义的Findname.cmake模块,使用方法以下:开发
FIND_PACKAGE(NAME) IF(NAME_FOUND) INCLUDE_DIRECTORIES(${NAME_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(targetname ${NAME_LIBRARY}) ELSE(NAME_FOUND) MESSAGE(FATAL_ERROR ”NAME library not found”) ENDIF(NAME_FOUND)
工程实践中使用示例以下:get
#经过<name>_FOUND来控制工程特性: SET(SOURCES Viewer.cpp) SET(OptionalSources) SET(OptionalLibs) FIND_PACKAGE(JPEG) IF(JPEG_FOUND) SET(OptionalSources ${OptionalSources} JPEGView.cpp) INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} ) SET(optionalLibs ${OptionalLibs} ${JPEG_LIBRARIES} ) ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT) ENDIF(JPEG_FOUND) IF(PNG_FOUND) SET(OptionalSources ${OptionalSources} PNGView.cpp) INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIR} ) SET(OptionalLibs ${OptionalLibs} ${PNG_LIBRARIES} ) ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT) ENDIF(PNG_FOUND) ADD_EXECUTABLE(viewer ${SOURCES} ${OptionalSources} ) TARGET_LINK_LIBRARIES(Viewer ${OptionalLibs}) #经过判断系统是否提供了JPEG、PNG模块来决定程序是否支持JPEG、PNG功能。
本文基于libHello动态库,编写一个FindHelo.cmake模块。
建立一个FindHello项目目录,进入FindHello。
建立一个Hello目录,进入Hello目录,建立FindHelo.cmake文件,FindHelo.cmake文件以下:
#查找hello库头文件的安装路径 FIND_PATH(HELLO_INCLUDE_DIR Hello.h /usr/local/include/hello) #查找hello库的安装路径 FIND_LIBRARY(HELLO_LIBRARY NAMES hello /usr/local/lib/libhello.so) IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) SET(HELLO_FOUND TRUE) ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) IF (HELLO_FOUND) IF (NOT HELLO_FIND_QUIETLY) MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}") ENDIF (NOT HELLO_FIND_QUIETLY) ELSE (HELLO_FOUND) IF (HELLO_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find hello library") ENDIF (HELLO_FIND_REQUIRED) ENDIF (HELLO_FOUND)
返回FindHello目录,建立main.cpp文件:
#include <Hello.h> int main(int argc, char* argv[]) { Hello hello; hello.Print(); return 0; }
建立工程CMakeLists.txt文件以下:
cmake_minimum_required(VERSION 2.8) PROJECT(FindHello) # 设置自定义模块路径 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/Hello/") MESSAGE(STATUS "CMAKE_MODULE_PATH " ${PROJECT_SOURCE_DIR}/Hello) #查找自定义Hello模块 FIND_PACKAGE(Hello) IF(HELLO_FOUND) ADD_EXECUTABLE(hello main.cpp) INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR}) MESSAGE(STATUS "LINK " ${HELLO_LIBRARY}) TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY}) ELSE(HELLO_FOUND) MESSAGE(STATUS "Could not find hello library") ENDIF(HELLO_FOUND)
建立build目录,进行build目录进行构建。cmake ../make./hello结果打印出Hello world.