关于CMakeLists.txt中find_packge()和动态库查找的遇到的大部分问题总结
前段时间,一直在编译代码,遇到一些关于cmake中编译的相关问题,想着经下心来总结以下相关问题,发现深挖后,真是理解到了学无止境,还有很多东西需要了解,不过在其中也总结了一些东西想要分享给大家,有什么问题可以及时沟通。
下面主要从两个方面介绍cmake中的查找默认路径和方式,以及我们能够做的工作,下面介绍的这是其中的一小部分,让我用简短的一段文字完全讲述其中的相关知识背景,我实在无能为力,此外由于知识水平有限,其中会有我的知识瓶颈,如有不同观点,欢迎沟通交流,互相成长!
常见报错形式:
以下3个报错,为常见错误。
- Could not find a package configuration file provided by “XXX” with any of the following names:
原因:没有找到你的功能包,其实是没有找到.cmake文件,可以采用1.1.1中的最后一个针对配置模式的解决方案,就可以解决了。 - error: ld returned 1 exit status。
原因:ld是链接器的简称,所以是链接是没有找到这个库文件,一般为编译时,没有找到库的地址,可以通过添加下面代码,让ld找到库文件,括号内的地址是你的库文件存放的地址,这个问题解决后,可能会出现3中出现的问题,找到了链接不上,我还没有解决此问题,欢迎讨论。不过还是可以通过1.1.2.1中的其中一个方法解决此问题的。
link_directories(${PROJECT_SOURCE_DIR}/bin/)
- error while loading shared libraries: libSatp.so.1: cannot open shared object file: No such file or directory。
原因:这种情况一般是编译通过后,运行时报的错误,编译没有报错误,说明这个包存在,并且找到了,就是没有链接上,采用1.1.2.1中的任意一个一般可以解决,没有解决可以沟通交流。但是我困惑的是想要通过1.1.2.2中提出的方式解决,一直没有成功,报这个错误,我很困惑一直没有解决。
1 不同方式引入的库
在我目前知识范围内,自己编译环境存在的库的形式大致可以分为两大类,一种是自己编译环境下通过cmake/make由源码编译生成的库文件,另外一种是在别人电脑编译生成库文件后,拷贝到自己环境中的,此时特指只拷贝了库文件,没有拷贝源码,如果拷贝了源码,在自己环境中进行再次编译生成动态库就和第一中情况一样了。以上库文件,一般指动态链接库和静态链接库,其中动态链接库(有的称为共享库)unbuntu下常以.so结尾,静态链接库以.a结尾。
以上两种方式引入的动态库,在我们编写CmakeLists.txt时的引用方式不同,具体引用方式如何书写,此文章暂时不进行分享,以后了解更多了再写。此文章重点书写在链接过程中出现链接失败,如何使编译通过。
1.1 通过编译引入的库
通过编译引入的动态库,一般make都会生成一个Config.cmake文件,如果提示查找不到某个包(can not find package),你就可以在根目录下搜索一下有没有这个文件,有的话,直接把这个地址给find_pkg 就行了,此方式也可以指定你想要选择的包版本。具体是如何查找的,以及查找路径可以看下面的讲解。
1.1.1 findpackage查找路径
-
模块模式
cmake的模块模式,我理解的就是cmake编译时查找你想要引入的库的Find.cmake文件。
1) 首先查找当前cmake 路径下的所有目录中是否存在你想引入库的Find.cmake,此文件一般为用户自定义的。
2)如果没有查找到,cmake一般会在/usr/share/cmake-x.y/Modules/中查找,此文件夹中的Find.cmake为cmake 官方给出专门查找功能包的文件,其中的一个Find.cmake可以找到一个对应的功能包,其中只是一部分常用功能包。
如果官方给出了某个功能包的Find.cmake文件,你就可以在cmakelist.txt中通过find_package(XXX)引入此功能包。
-
配置模式
cmake在模块模式中如果没有查找到对应的库,将会进入配置模式,我目前理解的配置模式就是查找
<包名>Config.cmake。1) 首先会在~/.cmake/packages中查找是否有你引入的 <包名>Config.cmake,我查看的我的电脑这个目录中没有什么东西。
2) 然后会在/usr/local/share/中查找是否存在你想要引入的 <包名>Config.cmake。
-
根据模块模式和配置模式我们可以做什么?
知道了模块模式和配置模式的查找方式后,我们就可以使用这些规则制定我们自己的Find.cmake和 <包名>Config.cmake了。
1)针对模块模式,我们可以在自己当前cmake目录下,编写Find.cmake文件,指定引入的库的路径。
下面是编写的find_qt.cmake文件示例。
if (WIN32) # set stuff for windows # SET(Qt5_PATH /path/to/your/qt/lib) else() # set stuff for other systems SET(Qt5_PATH /home/wsb/Qt5.12.2/5.12.2/gcc_64/lib/cmake/Qt5/) #此处被更改,更改为本机QtConfig.cmake的地址 endif() SET(CMAKE_PREFIX_PATH ${Qt5_PATH}) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) if(CMAKE_VERSION VERSION_LESS "3.7.0") set(CMAKE_INCLUDE_CURRENT_DIR ON) endif() #set(Qt5 /home/wsb/Qt5.12.2/5.12.2/gcc_64/lib/cmake/Qt5/) #此处为添加内容,为本机QtConfig.cmake的地址,与上处可替换 find_package(Qt5 COMPONENTS Core Network RemoteObjects REQUIRED) include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS})
2)针对配置模式,如果我们想要指定某一个库的版本,例如有多个OpenCVd的版本,我们需要先在本机找到需要版本的 <包名>Config.cmake,
然后在CMakeLists.txt中添加以下代码,"/usr/local/share/OpenCV/"路径为我需要的OpenCV的OpenCVConfig.cmake的地址。set(OpenCV_DIR "/usr/local/share/OpenCV/") #指定自己需要的OpenCV的版本 find_package(OpenCV REQUIRED)
1.1.2 通过单独复制引入的库
其中有一些库并不是通过make源码安装的,我遇到的情况是,我购买了摄像头,供应商给我提供的一些 动态库(.so文件),这些动态库是零散的,
无法通过上面提到的模块模式和配置模式完成引入库的功能,cmake 还提供了一些单独查找库的方法。
1.1.2.1 几种从系统中角度引入单个动态库的方法
-
export LD_LIBRARY_PATH=/usr/lib64/(/usr/lib64/为 .so所在路径),只对当前用户的终端生效,其他用户没用。
-
在 /etc/ld.so.conf文件下,找一个文件将动态库的路径加进来,然后 执行sudo /sbin/ldconfig 进行更新。
-
无论什么动态库, Linux操作系统,都是默认去该目录下 /usr/lib/查找使用的动态库,所以有两种办法。
1)创建动态库的软连接至该目录;sudo ln -s (动态库的源文件) /usr/lib。这个方法的好处是动态库进行更新时,会把目录下的一起更新,不需要额外操作。
2)sudo cp (动态库的源文件) /usr/lib;(不推荐把动态库拷贝至该目录下 ,因为更新时需要把该目录下的都重新拷贝)
-
修改该用户下的 .bashrc 配置文件;将动态库的路径添加进来;重新执行配置文件:source .bashrc。(弊端:只 在当前用户下有用)
1.1.2.2 如何通过CMakeLists.txt引入单个动态库?
最近一直被在各个问题困惑,还没有搞清楚怎么回事,前面的几种方式都是在系统中添加自己的库文件,怎么有一种方式,不需要通过系统,直接在CMakeLists.txt中引入单个库,这样就不用每次对系统都进行更改某些内容了,有知道的朋友,可以与我分享一下。我尝试以下代码引入,单个动态库,同一个文件中的动态库,有的被成功链接到,有的就是链接不会上,不知道怎么回事?
#添加头文件地址
include_directories(
${catkin_INCLUDE_DIRS}
${OpenCV_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/include/
)
#添加库文件地址
link_directories(${PROJECT_SOURCE_DIR}/bin/)
#多个库赋值到STECAM_LIBS变量中
set(STECAM_LIBS StereoCamera Satp icudata icui18n icuuc ImageUtils QAsioSocket Qt5Core Qt5Gui Qt5Network Qt5RemoteObjects SatpExt)
#创建可执行文件
add_executable(stereo_publisher
src/stereo_publisher.cpp
src/framemonitor.cpp
src/pointcloudgenerator.cpp
)
#添加链接库
target_link_libraries(stereo_publisher
${catkin_LIBRARIES}
${STECAM_LIBS}
)