/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d

文章目录

  • 0.前提描述
    • 0.1.环境
    • 0.2.使用的工程
  • 1.问题描述
  • 2.问题分析过程
  • 3. 破案了!破案了!破案了!
  • 4.再分析原因?
  • 5.使用OpenCV3.4.5还会链接到OpenCV3.2.0上的问题
  • 6.有使用OpenCV3的工程用到uav-target-get怎么办?

参考:https://zhuanlan.zhihu.com/p/400316912

0.前提描述

0.1.环境

Ubuntu18.04 + ros-melodic

ROS安装的OpenCV3.2.0,安装在根目录,比较分散,比如头文件在/usr/include,库文件在/usr/lib/x86_64-gnu下。

自己安装的OpenCV3.4.5, 路径是/usr/local/OpenCV3.4.5

自己安装的OpenCV4.5.1,路径是/usr/local/OpenCV4.5.1

0.2.使用的工程

  • uav-get-get:ROS功能包,使用OpenCV4
  • yn:ROS功能包,使用OpenCV3,并且用到 uav-get-get这个功能包。因此为了方便,一开始把uav-get-get这个功能包的source环境变量语句加到了~/.zshrc
  • r2live:港大Mars实验室的SLAM项目,也是ROS工程,使用OpenCV3

1.问题描述

编译港大r2live工程,编译的时候出现这个问题,虽然只是警告,但是运行的时候直接报错。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第1张图片因为链接库有问题,使用gdb调试发现如下结果,有一部分库链接到了OpenCV4.5的库上,而我cmake指定的是3.4.5的版本。/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第2张图片
这个确实是一个很隐晦的问题。问题就在于ROS工程中用到OpenCV的话,基本上都会用到ROS中的cv_bridge用来转换ros和opencv的图片格式。ros安装的opencv是3.2.0版本的,而如果我在cmakelist中指定使用其他版本的OpenCV,很可能就会造成cv_bridge依赖的库出错。从上面的警告来看,都是警告某些OpenCV3的库和Opencv4的库冲突。并且从最后一张图片来看我确实是链接到了OpenCV4的库上,导致程序编译的时候只是给了一个警告,在运行的时候就出错了。后来我把安装的opencv4删除了,然后就没有警告了。实际我的电脑上还有OpenCV3.4.5,和ros版本不同。我觉得此时程序可能仍然错误地连接到了opencv3.4.5的库上,只不过他和cv_bridge用的3.2.0的库主版本号一样,相差不大,所以没有报警告,应该运行也没问题。

不是!实际测试是全部连接到了OpenCV的3.2.0上,如下图所示。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第3张图片
根据参考博客中的讲解,我指定查找OpenCV 的路径是ros安装的Opencv路径,即3.2.0版本,编译之后报警告如下图。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第4张图片
然后对feature_tracker这个可执行文件依赖的库进行了查看,发现确实一部分链接到了OpenCV 4.5.1的库上,一部分链接到了OpenCV 3.2.0上。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第5张图片

最大的问题:为什么我在cmakelists.txt中制定了OpenCV的版本是3.4.5,还是会错误的连接到OpenCV4.5.1的库上呢?

2.问题分析过程

  1. 查看自己安装的OpencV 4.5.1的OpenCVConfig.cmake文件,它的位置也比较奇怪,竟然在lib文件夹下。
    /usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第6张图片
  2. 在实验室电脑上编译feature_tracker包,其中在cmakelists.txt中明确指明了要使用的OpenCV版本是3.2.0:
find_package(OpenCV 3.2.0 REQUIRED)

但是结果却没有优先找到系统的OpenCV版本上,而是找到了自己安装的3.4.5版本上(说明这台电脑上的ROS自带的OpenCV版本可能出了点问题):
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第7张图片

  1. 用EXACT关键字强制指定确定版本,而不是兼容版本:
find_package(OpenCV 3.2.0 EXACT  REQUIRED)

但是这么写结果还是找到了OpenCV3.4.5的版本,所以我感觉写法应该是有点问题,所以把REQUIRED参数去掉了,写成:

find_package(OpenCV 3.2.0 EXACT)

这次确实是找到了正确的版本,但是链接的问题仍然存在:
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第8张图片

  1. 把实验室电脑上的OpenCV4.5.1删除,这下一切正常了。feature_tracker正常链接到了3.2.0的库上,如下图。并且最后编译整个r2live成功,最后也成功运行起来没有任何错误。/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第9张图片
  2. 在宿舍电脑上再次实验,结果也是也可正常编译整个r2live成功,最后也成功运行起来没有任何错误。但是一个比较奇怪的问题是,如果到编译到feature_tracker为止的话,按理说已经编译出来了feature_tracker这个功能包,但是单独运行它报错,按理说ros的分布式的,编译出来这个功能包应该就可以运行了啊?。可能这个功能包有传入的参数?需要回头看看源码。初步猜测和launch文件中有参数有关:
    在这里插入图片描述
  3. 再次测试,宿舍的电脑上删除了OpenCV4.5.1版本,保留了自己安装的opencv3.4.5,然后在cmakelists.txt中指定链接的版本是3.4.5。
find_package(OpenCV 3.4.5 REQUIRED)

然后编译还是有警告。

/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第10张图片
有点明白了,感觉像是库会自动链接到比他高版本的库上。但是为什么上面指定OpenCV3.2.0的时候,没有链接到高版本上呢?

新的想法:并不是会自动链接到比他高版本的库上!

  1. 在6后面,继续编译r2live包,没有警告,而且程序可以正常跑,没有feature_tracker process has died的错误。

  2. 到实验室电脑上也改成3.4.5版本,结果和宿舍电脑上一样如下图所示。
    /usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第11张图片

  3. 再次安装opencv4.5.1,目录仍然是/usr/local/opencv4.5.1,但是此时再次编译r2live链接不会链接到4.5.1的库上。

  4. 编译另一个使用opencv4工程uav-target-get。然后再来编译r2live,这个时候就链接到4.5.1了,好像是使用了一次之后系统知道了这个目录。

3. 破案了!破案了!破案了!

问题恰恰出在我的另一个使用opencv4.5.1的工程uav-target-get上!因为这个ros包被其他包使用,为了方便使用,就把这个ros包的source环境变量语句加到了~/.zshrc中。也就是每次打开一个shell窗口,都会有这个包的环境变量,比如看echo $ROS_PACKAGE_PATH环境变量:

/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第12张图片
由于ros是用catkin_make编译的,底层也是cmake,所以ROS_PACKAGE_PATH的位置也会在cmake编译查找的变量CMAKE_PREFIX_PATH中。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第13张图片

如果我把source使用opencv4的工程uav-target-get环境变量语句在~/.zshrc中删掉之后,在重开窗口,这时候查看环境变量中就没有使用opencv4这个工程的路径了。然后再次编译r2live,就不会再链接到OpenCV4.5.1上了!如果把这个路径再加到环境变量中,再编译r2live,那么又会链接到OPenCV4.5.1上。 所以问题应该就出现在CMAKE_PREFIX_PATH环境变量这里!

4.再分析原因?

cmake简明使用指南 :这里面就讲了CMake的语法,好像也没有说上面的原因是什么。

加入uav-target-get的环境变量路径,在链接到opencv4.5.1的工程中打开catkin_make编译生成的build 和 devel两个文件夹,用vscode搜索其中的opencv版本4.5.1,结果发现果真有很多和4.5.1有关的内容:
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第14张图片把上面uav-target-get包的环境变量删掉,再来编译看看,这里结果显示链接不到OpenCV4.5.1了。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第15张图片这个时候再用vscode看,只有一个opencv4.5.1了,而且这个只是OpenCV4.5.1版本config.cake路径,说明此时确实没有链接到OpenCV4.5.1上。
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第16张图片
到底这样是为什么不清楚,因为cmake生成的文件太多太复杂了。

但是猜测一下应该是这样:

由于在CMAKE_PREFIX_PATHuav-target-get的路径,而这个工程使用了OpenCV4.5.1。那么这个工程的build文件夹下的文件中就会存在很多和OpenCV4.5.1有关的内容。而CMAKE_PREFIX_PATH是cmake查找的路径,这样cmake在查找库的时候就会把opencv4.5.1的库也链接到,从而造成了有些库的版本被错误链接的情况!

5.使用OpenCV3.4.5还会链接到OpenCV3.2.0上的问题

我现在删掉uav-target-get工程的环境变量路径,此时电脑上存在自己安装的OpenCV3.4.5和OpenCV4.5.1,CMakeLists.txt制定使用OpenCV3.4.5,然后此时编译结果:
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第17张图片看链接的库:
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第18张图片
根据上面的解释,很容易想到这个很有可能是因为用到了cv_bridge这个库,因为为了在ros中为了方便处理经常要把图像在ros格式和cv格式互转。查看cv_bridge这个库到底依赖了那些库:

cd /opt/ros/melodic/lib/
ldd libcv_bridge.so | grep opencv

结果如下,猜测正确!这三个库文件恰好就是上面的feature_tracker警告所链接到的OpenCV3.2.0版本的库文件!
/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第19张图片
这里虽然同样链接到库出问题了,但是最后编译完整个r2live运行并没有出错。原因应该是3.4.5版本和3.2.0版本的这几个库差别不大,毕竟他们版本号非常相近。但是如果后面使用的OpenCV版本很高,可能这几个库和3.2.0版本的库差别很大呢?此时就只能卸载原来的ros安装的cv_bridge,然后下载github下载源码编译自己匹配的版本的cv_bridge。具体再搜索即可。(但感觉应该不会有人写程序这么搞吧?这不是给自己找麻烦吗?)

6.有使用OpenCV3的工程用到uav-target-get怎么办?

因为uav-target-get是一个使用OpenCV 4.5.1的ROS工程包,然后另一个使用OpenCV 3的ROS功能包yn会使用uav-target-get这个功能包。那么为了在编译yn的时候能够找到uav-target-get功能包,就必须source它的环境变量。

这样就带来了上面的问题:现在的yn工程使用OpenCV3,同时还用了uav-target-get功能包又必须source它的功能包,由于source了环境变量之后会链接到OpenCV4.5的版本上,这样就会导致库链接冲突的问题。

之前看了编译后uav-target-get工程在build devel文件夹下有很多和OpenCV4.5.1版本相关的内容,这里想把build删掉,是不是尽管source了环境变量,也不会链接到OpenCV4.5.1上了?结果没用,还是会链接到上面。因为uav-target-get工程的devel文件夹下还是有和OpenCV4.5.1版本相关的内容。如下图

/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第20张图片
看CMakeCache.txt,结果像是大部分链接到了OpenCV4.5.1上,而少部分链接到OpenCV3.2.0上。迷惑?

/usr/bin/ld: warning libopencvxxx.so.a.b,needed by xxx, may conflict with libopencvxxx.so.c.d_第21张图片
但是这个程序运行好像问题不大。

现在想想如果要彻底解决这个链接库的问题,可以怎么做?

  • 现在唯一可以想到的就是自己在CMakeLists.txt中用find_library手动把OpenCV库写成一个自己的变量,比如MY_OpenCV_LIB,然后target_link_libraries的时候链接${MY_OpenCV_LIB,而不链接${OpenCV_LIBS}。但是面对别人的程序很难知道链接了哪些OpenCV库(或许什么都不改先链接,然后ldd看可执行文件就知道链接了哪些库了,但还是比较麻烦),这样就只能在Cmakellist中链接所有的库。OpenCV库又很多,这样导致每个cmakelists文件都要加一大堆内容,太麻烦。

  • 所以想到另一个解决办法就是写一个FindOpenCV-3.4.5.cmake这种文件放到ModulePath文件夹下,因为find_package会优先查找Module模式的库,即Findxxx.cmake这种。然后在FindOpenCV-3.4.5.cmake中定义头文件和库文件路径,同理这样我们的库名就变成了OpenCV-3.4.5而非OpenCV,链接库的时候也就成了${OpenCV-3.4.5_LIBS},甚至这个库的名称我们可以在FindOpenCV-3.4.5.cmake文件中任意定义,保证一定不会和${OpenCV_LIBS}重复。

你可能感兴趣的:(Linux工具操作笔记,linux)