1 find_package 使用简介
1.1 Module 模式
1.2 Config 模式
2 问题分析
3 解决方案
3.1 方案(一)
3.2 方案(二)
3.2.1 方式(1)
3.2.2 方式(2)
3.2.3 方式(3)
3.2.4 方式(4)
3.3 总结
4 参考资料
我在安装完 OpenCV 库之后,本来打算测试一下 OpenCV 库是否成功安装。结果出现了莫名的关于 find_package() 的错误,接下来,我会按照事情的发生以及最终的解决一步步探究 find_package() 的原理。
下面用的例子对应的 .cpp 代码以及对应的 CMakeLists.txt 代码分别如下:
.cpp:
#include
#include
using namespace cv;
int main(int argc, char** argv ) {
if ( argc != 2 ) {
printf("usage: DisplayImage.out \n");
return -1;
}
Mat image;
image = imread( argv[1], 1 );
if ( !image.data ) {
printf("No image data \n");
return -1;
}
namedWindow("Display Image", WINDOW_AUTOSIZE );
imshow("Display Image", image);
waitKey(0);
return 0;
}
CMakeLists.txt 文件:
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
find_package( Opencv REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
add_executable( Display test.cpp)
arget_link_libraries( Display ${OpenCV_LIBS} )
在工程目录下新建 build 文件夹,并在终端中进入 build 目录,然后输入命令: cmake ..,之后出现如下错误信息:
CMake Error at CMakeLists.txt:5 (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
Add the installation prefix of "Opencv" to CMAKE_PREFIX_PATH or set
"Opencv_DIR" to a directory containing one of the above files. If "Opencv"
provides a separate development package or SDK, be sure it has been
installed.
从上面的提示,发现CMake 没有找到FindOpencv.cmake文件,之后 CMake 尝试去找OpencvConfig.cmake, 结果也没有找到。这个就是我们解决问题的突破口。
首先我在系统根目录下尝试搜索 FindOpencv.cmake,也尝试寻找 OpencvCongfig.cmake 结果都没有找到这个文件。
实际上,库的作者一般都会提供这两个文件,但是我按照 Opencv 官网上的安装说明编译安装的 Opencv 库。CMake 却无法找到,其中具体原因还不太清楚。
于是我在编译好的 Opecnv 文件的 build 的目录里面尝试并找到了OpenCVConfig.cmake ,然后在 /usr/local/share/OpenCV 这个目录下同样找到了 OpenCVConfig.cmake 文件。
虽然找到了类似的文件,仍然没有实质性的解决问题——为什么明明有这两个文件,但是 CMake 仍然找不到?
于是我在网上搜索了关于 find_package() 命令是如何查找库的。下面简单介绍 CMake 如何使用 find_package 命令对外部库进行查找。最后针对上面 cmake .. 后出现的问题,提出了不同的解决方案。
首先明确一点,CMake 本身不提供任何关于搜索库的便捷方法,也不会对库本身的环境变量进行设置。
它仅仅是按照优先级顺序在指定的搜索路径进行查找 Findxxx.cmake 文件和xxxConfig.cmake文件(其中xxx代表库的名字,特别注意的是有大小写之分),这两个文件大体上是没有区别的,CMake 能够找到这两个文件中的任何一个,我们都能成功使用该库,
也就是我们可以用库的内置好了 CMake 变量。包含了库的头文件和库文件的路径信息,虽然库的作者一般会提供这两个文件,但是也会遇到安装完毕后找不到的情况。
当我们在终端键入 cmake.. 命令之后,CMake 会读取并执行 CMakeLists.txt 中的代码,当执行 find_package() 这条命令后,CMake 就会从某些路径中找这 Findxxx.cmake 文件或者xxxConfig.cmake 文件,
CMake 找到任意一个之后就会执行这个文件,然后这个文件执行后就会设置好一些 CMake 变量。比如下面的变量(NAME 表示库的名字 比如可以用 Opencv 代表 Opencv库):
_FOUND
_INCLUDE_DIRS or _INCLUDES
_LIBRARIES or _LIBRARIES or _LIBS
_DEFINITIONS
一般常用的就是 xxx_FOUND 、xxx_INCLUDE_DIRS、xxx_LIBS,分别代表是否找到库的标志、库的头文件路径、库文件路径。
find_package() 有两种模式: Module 模式和 Config 模式,分别对应上面的 Findxxx.cmake 和xxxConfig.cmake 两个文件。CMake 默认优先 Module 模式,而 Config 模式是备选项。
1.1 Module模式(仅仅查找 Findxxx.cmake 文件)
CMake 会优先搜索 CMAKE_MODULE_PATH 指定的路径,如果在 CMakeLists.txt 中没有设置 CMAKE_MODULE_PATH 为存储 Findxxx.cmake 的路径,也就是说没有下面的指令: set(CMAKE_MODULE_PATH "Findxxx.cmake文件所在的路径")
那么 CMake 不会搜索 CMAKE_MODULE_PATH 指定的路径,此时 CMake 会搜索第二优先级的路径,也就是 /share/cmake-x.y/Mdodules(注意:x.y 表示版本号,我的是 3.10)。
其中 CMAKE_ROOT 是你在安装 CMake 时候的系统路径,因为我并没有指定安装路径,所以是系统默认的路径,在我的系统中(ubuntu16.04)系统的默认路径是 /usr/loacl。
如果你在安装的过程中使用了 cmake -DCMAKE_INSTALL_PREFIX=自己dir路径 ,那么此时CMAKE_ROOT 就代表那个你写入的路径 。
刚刚说道第一优先级的路径搜索没有找到Findxxx.cmake 文件,就会到第二优先级的路径下搜索。如果 CMake 在两个路径下都没有找到 Findxxx.cmake 文件。那么 CMake 就会进入 Config 模式。
1.2 Config模式(仅仅查找 xxxConfig.cmake 文件)
CMake 会优先搜索 xxx_DIR 指定的路径。如果在 CMakeLists.txt 中没有设置这个 CMake变量。也就是说没有下面的指令: set(xxx_DIR "xxxConfig.cmkae文件所在的路径")
那么 CMake 就不会搜索 xxx_DIR 指定的路径,此时 CMake 就会自动到第二优先级的路径下搜索,也就是 /usr/local/lib/cmake/xxx/ 中的 xxxConfig.cmake 文件。
上面主要讲了 CMake 的搜索模式。如果 CMake 在两种模式提供的路径中没有找到对应的Findxxx.cmake 和 xxxConfig.cmake 文件,此时系统就会提示最上面的那些错误信息。
回顾一下上面的错误信息。根据提示以及上面的两种模式的说明,可以发现我的CMakeLists.txt 文件中没有加入设置 CMKAE_MODULE_PATH 以及 Opencv_DIR 的命令。
也就是说 CMake 执行 find_package 后,会自动到两种默认的第二优先级的搜索路径下搜索Findxxx.cmake 和 xxxConfig.cmake 文件。虽然我在安装 Opencv 的时候,是默认安装系统目录的,
但是我在 Moudule 模式中的第二优先级搜索路径 /share/cmake-x.y/Mdodules 里面没有发现 FindOpencv.cmake 文件。
然后我在 Config 模式中的第二优先级搜索路径 /usr/loacl/lib/cmake/ 中也没有发现 Opencv 文件夹,更没有找到OpencvConfig.cmake 文件了。而是仅仅看到了我之前在系统上安装的 Pangolin 文件夹,里面确实有对应的 PangolinConfig.cmake 文件。
但是我在 /usr/local/share/OpenCV/ 文件夹中找到了 OpenCV.cmake 文件。但不是最上面错误信息中提到的 OpencvConfig.cmake 文件。仅仅是大小写不同。
3.1 方案(一)
我们可以忽略上面的错误信息,因为我们用 find_package() 查找库的目的是为了用include_directories() 包含想要用的头文件。用 link_directories() 包含库文件。
最后用target_link_libraries (可执行文件 库)链接动态或者动态的库,所以,最简单的方法就是自己在Opencv 包的目录下找到头文件路径和库文件路径。可以在 CMakeLists.txt 中设置如下指令:
set(Opencv_INCLUDE_DIRS "Opencv库的头文件目录")
set(Opencv_LIBRARIES_DIRS "Opencv库的库文件目录")
set(Opencv_LIBs "具体的链接库文件.a .so")
include_directories(${Opencv_INCLUDE_DIRS})
link_directories(${Opencv_LIBRARIES_DIRS})
target_link_libraries( <执行文件名字> "${Opencv_LIBs} )
这里有一点不好的地方就是指令的引号 "" 部分。前两条指令还可以,我们只要写下具体的文件路径就行了。对于第三条指令。具体链接库的文件这个就很多了,而且很有可能我们写入的不全,好的情况是我们没有用那么多的库,所以简单的程序可能正常编译运行,
但是当程序复杂的时候,所需要的库就不是我们能够考虑到的,我们就必须要把所有可能的库都加入到这里来,只要漏掉了工程需要的库文件或者头文件,都会导致程序编译失败。
这是一个致命的缺点。当然对于我自己的情况,我的安装包默认安装到了系统目录,也就是说我可以不添加头文件和库文件路径,仅仅设置下一具体的链接库文件即以 .so .a 结尾的文件集。如果你安装到了其他的地方,那么就需要设置包含库文件和头文件的路径。
Opencv库的头文件目录 ,这个路径的选择有两个,一个是安装包的下的 include 目录,一个是 /usr/local/include
Opencv库的库文件目录 ,这个路径的选择有两个,一个是安装包的build/bin,一个是 /usr/local/lib
具体的链接库文件,这个路径的选择有两个,一个是安装包的 build/bin/ 所有库文件名字,一个是 /usr/local/lib 下的所有库文件名字(注意:这个「具体的链接库文件」怎么能够简单的写出来我还不太清楚,仅仅知道把所有文件写出来,如果有人知道简化写,可以给我留言)
3.2 方案(二)
第一种方法虽然回避了上面错误信息的发生,但是在后面也需要做很多的工作。每一步的工作不到位,都可能会导致编译失败。接下来的方法,
按照上面提示的错误信息寻找需要的 FindOpencv.cmake 和 OpencvConfig.cmake 文件。在问题分析部分讨论了,在我的 CMake 默认的搜索路径中没有包含上面的任何一个文件。仅仅在Opencv 安装包和 /usr/local/share/OpenCV 这两个路径下找到了 OpenCV.cmake 文件。
接下来的操作有四种选择(需要注意的是我的 Opencv 库默认安装到了系统目录,所以在CMakeLists.txt 没有加入包含头文件和库文件的两条指令。):
3.2.1 方式(1)
让系统按照 Module 模式进行查找,也就是想办法弄出一个 FindOpencv.cmake 文件,之后设置一下 CMAKE_MODULE_PATH 变量。下面是具体操作:
因为我们在所有目录中没有找到 FindOpencv.cmake 文件。只能找到 OpenCVConfig.cmake文件。我们可以将这个文件名字更改为 FindOpencv.cmake(更改方法有很多,如果没有权限可以鼠标右键更改,如果有权限那么必须在命令行中 sudo 进行修改)。
然后在 CMakeLists.txt 中加入 CMAKE_MODULE_PATH 的路径信息。
因为 OpenCVConfig.cmake 文件出现的位置有两个,一个是安转包的 build/ 下,一个是 /usr/local/share/OpenCV/ 下。
我选择的是在安转包的build/ 下面直接鼠标右键更改。更改完毕后在 CMakeLists.txt 中find_package 命令前面加入下面命令: set(CMAKE_MODULE_PATH /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
最后的 CMakeLists.txt 中的命令如下:
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
set(CMAKE_MODULE_PATH /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
find_package( Opencv REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
add_executable( Display test.cpp)
target_link_libraries( Display ${OpenCV_LIBS} )
3.2.2 方式(2)
让系统按照 Config 模式进行查找,想办法弄出一个 OpencvConfig.cmake 文件,之后设置一下 Opencv_DIR 变量(这里一定是Opencv_DIR 不能是OpenCV_DIR)。下面是具体操作:
因为我们在所有目录中没有找到 OpencvConfig.cmake 文件。只能找到OpenCVConfig.cmake 文件。我们可以将这个文件名字更改为OpencvConfig.cmake(具体的更改方法参照上面)。
然后在 CMakeLists.txt 中加入 Opencv_DIR 的路径信息。因为 OpenCVConfig.cmake 文件出现的位置有两个,一个是安转包的 build/ 下,一个是 /usr/local/share/OpenCV/ 下。
我选择的是在安转包的build/下面直接鼠标右键更改。更改完毕后在 CMakeLists.txt 中find_package 命令前面加入下面命令: set(Opencv_DIR /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
最后的CMakeLists.txt中的命令如下:
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
set(Opencv_DIR /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
find_package( Opencv REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
add_executable( Display test.cpp)
target_link_libraries( Display ${OpenCV_LIBS} )
3.2.3 方式(3)
让系统按照Module模式进行查找,将OpenCVConfig.cmake文件更改为FindOpenCV.cmake。
设置一下CMAKE_MODULE_PATH 路径信息,之后在 find_package 的时候指定一个名字,比如:find_package(Opencv NAMES OpenCV REQUIRED )。
此时 find_package 就会查找 FindOpenCV.cmake 文件,下面是具体操作:
因为我们在所有目录中没有找到 FindOpencv.cmake 文件。只能找到 OpenCVConfig.cmake 文件。我们可以将这个文件名字更改为 FindOpenCV.cmake。
然后在CMakeLists.txt中加入CMAKE_MODULE_PATH的路径信息。
因为 OpenCVConfig.cmake 文件出现的位置有两个,一个是安转包的 build/ 下,一个是 /usr/local/share/OpenCV/ 下。我选择的是在安转包的 build/ 下面直接鼠标右键更改。更改完毕后在 CMakeLists.txt 中 find_package 命令前面加入下面命令: set(CMAKE_MODULE_PATH /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
并且修改find_package内容,具体命令如下: find_package( Opencv NAMES OpenCV REQUIRED)
最后的CMakeLists.txt中的命令如下:
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
set(CMAKE_MODULE_PATH /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
find_package( Opencv NAMES OpenCV REQUIRED)#或者用find_package(OpenCV REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
add_executable( Display test.cpp)
target_link_libraries( Display ${OpenCV_LIBS} )
3.2.4 方式(4)
让系统按照 Config 模式进行查找,设置一下 Opecv_DIR 路径,之后在 find_package 的时候指定一个名字,比如:find_package(Opencv NAMES OpenCV REQUIRED )。或者显示的指出用Config模式即
find_package(Opencv CONFIGS NAMES OpenCV REQUIRED),
因为OpenCVConfig.cmake文件出现的位置有两个,一个是安转包的 build/ 下,一个是 /usr/local/share/OpenCV/ 下。我选择的是在安转包的 build/ 路径对 Opencv_DIR 设置。更改完毕后在 CMakeLists.txt 中 find_package 命令前面加入下面命令:
set(Opencv_DIR /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
并且修改 find_package 内容,具体命令如下: find_package( Opencv NAMES OpenCV REQUIRED)
最后的 CMakeLists.txt 中的命令如下:
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
set(Opencv_DIR /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
find_package( Opencv NAMES OpenCV REQUIRED)#或者用find_package(OpenCV REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
add_executable( Display test.cpp)
target_link_libraries( Display ${OpenCV_LIBS} )
3.3 总结
你可以灵活的选择上面的两种方法中的任何一种,或者是第二中方法中的任何一种方式。任何一种写法。实际上上面的过程中会有很多的细节,可以自己一一尝试,看看输出的内容到底是什么。
find_package 具体用法可以参考 CMake 官方手册。下面将上面的四种方式写的CMakeLists.txt,结合在一起进行对比(默认对应上面四种方式相应的更改了OpenCVCongfig.cmake文件):
cmake_minimum_required(VERSION 2.8)
project(DisplayImage)
#设置Module模式路径(想要使用的话前提要求有FindOpencv.cmake或者FindOpenCV.cmake,之后选择对应的find_package模式,例如FindOpencv.cmake 对应find_package(Opencv REQUIRED),FindOpenCV.cmake对应find_package(Opencv NAMES OpenCV REQUIRED) )
#set(CMAKE_MODULE_PATH /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
#设置Config模式路径(想要使用的话,前提要有OpencvConfig.cmake或者OpenCVConfig.cmake,之后选择对应的find_package模式,对应关系与Moudule模式同理,需要注意的一点是,我们也可直接显示的使用Config模式,即Cmake不会按照Module模式进行查找。需要调用find_package(Opencv CONFIGS NAMES OpenCV REQUIRED)或者find_package(Opencv CONFIGS REQUIRED))
set(Opencv_DIR /home/gcj/Slam_Start/slam_directory/slam_packages/opencv-3.4.0/build)
#对应FindOpenCV.cmake和OpenCVConfig.cmake
find_package( Opencv NAMES OpenCV REQUIRED)#或者用find_package(OpenCV REQUIRED)
#对应FindOpencv.camke和OpencvConfig.cmake
find_package( Opencv REQUIRED)
if(Opencv_FOUND)
message(STATUS "The Opecv lib is found!")
endif()
#下面两个包含头文件路径的命令,可以选择性的添加。因为我默认安装到了系统目录中,所以即使不添加下面的命令,系统也能够默认搜索到头文件。如果你安装这个库到了其他地方,那么下面这句话就是必须的。
#添加包含头文件的路径
#include_directories(${OpenCV_INCLUCE_DIRS})
#增加可执行文件
add_executable( Display test.cpp)
#链接库文件 这个命令是必须的。尤其是${OpenCV_LIBS}是在cmake找到对应的xxxConfig.cmake或者Findxxx.cmake后执行.cmake文件后定义的Cmake变量。注意OpenCV是大写的,因为.cmake文件中仅仅定义了大写的OpenCV_LIBS变量。所以在具体的写其他库的时候,要注意是大写还是小写,可以到相应的.cmake文件中查看。
target_link_libraries( Display ${OpenCV_LIBS} )
1.http://blog.csdn.net/bytxl/article/details/50637277#t0 2.https://cmake.org/cmake/help/v3.10/command/find_package.html 3.https://stackoverflow.com/questions/8711109/could-not-find-module-findopencv-cmake-error-in-configuration-process