Motivation

经常在Linux下面写C++程序,尤其是需要集成各种第三方库的工程,肯定对find_package指令不陌生。

这是条很强大的指令。可以直接帮我们解决整个工程的依赖问题,自动把头文件和动态链接文件配置好。比如说,在Linux下面工程依赖了OpenCV,只需要下面几行就可以完全配置好:

add_executable(my_bin src/my_bin.cpp)

find_package(OpenCV REQUIRED)

include_directories(${OpenCV_INCLUDE_DIRS})

target_link_libraries(my_bin, ${OpenCV_LIBS})

工作流程如下:find_package在一些目录中查找OpenCV的配置文件。

找到后,find_package会将头文件目录设置到${OpenCV_INCLUDE_DIRS}中,将链接库设置到${OpenCV_LIBS}中。

设置可执行文件的链接库和头文件目录,编译文件。

到现在为止出现了第一个问题。那就是:

find_package会在哪些目录下面寻找OpenCV的配置文件?

find_package目录

为什么我们要知道这个问题呢?因为很多库,我们都是自己编译安装的。比如说,电脑中同时编译了OpenCV2和OpenCV3,我该如何让cmake知道到底找哪个呢?

其实这个问题在CMake官方文档中有非常详细的解答。

首先是查找路径的根目录。我把几个重要的默认查找目录总结如下:

_DIR

CMAKE_PREFIX_PATH

CMAKE_FRAMEWORK_PATH

CMAKE_APPBUNDLE_PATH

PATH

其中,PATH中的路径如果以bin或sbin结尾,则自动回退到上一级目录。

找到根目录后,cmake会检查这些目录下的

/(lib/|lib|share)/cmake/*/ (U)

/(lib/|lib|share)/*/ (U)

/(lib/|lib|share)/*/(cmake|CMake)/ (U)

cmake找到这些目录后,会开始依次找Config.cmake或Find.cmake文件。找到后即可执行该文件并生成相关链接信息。

现在回过头来看查找路径的根目录。我认为最重要的一个是PATH。由于/usr/bin/在PATH中,cmake会自动去/usr/(lib/|lib|share)/cmake/*/寻找模块,这使得绝大部分我们直接通过apt-get安装的库可以被找到。

另外一个比较重要的是_DIR。我们可以在调用cmake时将这个目录传给cmake。由于其优先级最高,因此cmake会优先从该目录中寻找,这样我们就可以随心所欲的配置cmake使其找到我们希望它要找到的包。而且除上述指定路径外,cmake还会直接进入_DIR下寻找。如我在3rd_parties目录下编译了一个OpenCV,那么执行cmake时可以使用

OpenCV_DIR=../../3rd-party/opencv-3.3.4/build/ cmake ..

这样做以后,cmake会优先从该目录寻找OpenCV。

配置好编译好了以后,我感兴趣的是另一个问题:

我现在编译出了可执行文件,并且这个可执行文件依赖于opencv里的动态库。这个动态库是在cmake时显式给出的。那么,该执行文件在运行时是如何找到这个动态库的?

如果我把可执行文件移动了,如何让这个可执行文件依然能找到动态库?

如果我把该动态库位置移动了,如何让这个可执行文件依然能找到动态库?

如果我把可执行文件复制到别的电脑上使用,我该把其链接的动态库放到新电脑的什么位置?

可执行文件如何寻找动态库

在ld的官方文档中,对这个问题有详尽的描述。The linker uses the following search paths to locate required

shared libraries:

1. Any directories specified by -rpath-link options.

2. Any directories specified by -rpath options. The difference

between -rpath and -rpath-link is that directories specified by

-rpath options are included in the executable and used at

runtime, whereas the -rpath-link option is only effective at

link time. Searching -rpath in this way is only supported by

native linkers and cross linkers which have been configured

with the --with-sysroot option.

3. On an ELF system, for native linkers, if the -rpath and

-rpath-link options were not used, search the contents of the

environment variable "LD_RUN_PATH".

4. On SunOS, if the -rpath option was not used, search any

directories specified using -L options.

5. For a native linker, the search the contents of the environment

variable "LD_LIBRARY_PATH".

6. For a native ELF linker, the directories in "DT_RUNPATH" or

"DT_RPATH" of a shared library are searched for shared

libraries needed by it. The "DT_RPATH" entries are ignored if

"DT_RUNPATH" entries exist.

7. The default directories, normally /lib and /usr/lib.

8. For a native linker on an ELF system, if the file

/etc/ld.so.conf exists, the list of directories found in that

file.

If the required shared library is not found, the linker will issue

a warning and continue with the link.

最重要的是第一条,即rpath。这个rpath会在编译时将动态库绝对路径或者相对路径(取决于该动态库的cmake)写到可执行文件中。chrpath工具可以查看这些路径。

>>> chrpath extract_gpu

extract_gpu: RPATH=/usr/local/cuda/lib64:/home/dechao_meng/data/github/temporal-segment-networks/3rd-party/opencv-3.4.4/build/lib

可以看到,OpenCV的动态库的绝对路径被写到了可执行文件中。因此即使可执行文件的位置发生移动,依然可以准确找到编译时的rpath。

接下来的问题:如果我把可执行文件复制到了别人的电脑上,或者我的动态库文件的目录发生了改变,怎样让可执行文件继续找到这个动态库呢?其实是在第五条:LD_LIBRARY_PATH。只要将存储动态库的目录加入到LD_LIBRARY_PATH中,可执行文件就能正确找到该目录。

这种做法十分常见,比如我们在安装CUDA时,最后一步是在.bashrc中配置

export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH

这样做之后,依赖cuda的可执行文件就能够正常运行了。

总结

写这篇文章是因为从我第一次使用cmake以来,经常因为动态链接的问题而耽误很长时间。清楚理解find_package的运行机制在Linux的C++开发中是非常重要的,而相关的资料网上又比较稀少。其实官网上解释的非常清楚,不过之前一直没有认真查。做事情还是应该一步一个脚印,将原理搞清楚再放心使用。

Reference

cmake添加查找目录_cmake find_package路径详解相关推荐

  1. cmake添加查找目录_CMake如何查找库路径(一)

    CMake如何查找库路径(一) 如果你的代码使用了外部库(external libraries),并且你事先不知道这些库的头文件和库文件在当前平台的位置.那么适当的文件夹路径和库的搜索路径就应该被添加 ...

  2. cmake find_package路径详解

    cmake find_package路径详解 转自:https://zhuanlan.zhihu.com/p/50829542 经常在Linux下面写C++程序,尤其是需要集成各种第三方库的工程,肯定 ...

  3. linux 查找目录或文件 (详解)

    linux 查找目录或文件  (详解) 查找目录:find /(查找范围) -name '查找关键字' -type d 查找文件:find /(查找范围) -name 查找关键字 -print 如果需 ...

  4. cmake添加查找目录_CMakeLists.txt文件写法(7):添加查找头文件的路径

    [1.List转换成为数组.(这里的List是实体是ArrayList) 调用ArrayList的toArray方法. toArray public T[] toArray(T[] a)返回一个按照正 ...

  5. linux文件目录:Linux中各目录(文件夹)作用详解(持续更新)

    进入Linux系统,我们就是畅游在各种目录文件中,毕竟Linux是"文件系统",文件的存放就是在目录下面,那我们了解下"Linux中各目录(文件夹)作用详解"还 ...

  6. SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解

    SpringBoot默认包扫描机制及@ComponentScan指定扫描路径详解 SpringBoot默认包扫描机制 标注了@Component和@Component的衍生注解如@Controller ...

  7. Nginx反向代理、动静分离、负载均衡及rewrite隐藏路径详解(Nginx Apache MySQL Redis)–第二部分...

    Nginx反向代理.动静分离.负载均衡及rewrite隐藏路径详解 (Nginx Apache MySQL Redis) 楓城浪子原创,转载请标明出处! 更多技术博文请见个人博客:https://fe ...

  8. linux如何切换到光盘,怎么刻录cd光盘-Linux切换目录之cd命令详解

    请关注本头条号,每天坚持更新原创干货技术文章. 如需学习视频,请在微信搜索公众号"智传网优"直接开始自助视频学习 1. 前言 在Linux cd(切换目录)命令是最重要和最广泛使用 ...

  9. linux 软链接 相对路径,Linux入门之ln命令创建软链接的绝对路径和相对路径详解(Ubuntu)...

    ln命令创建软链接的绝对路径和相对路径详解 简介 ln命令 总结 简介 Linux链接,可以分为硬链接与软链接:本文主要介绍软链接.(默认情况下,ln命令产生硬链接) 软链接文件类似于Windows的 ...

最新文章

  1. kotlin条件表达式
  2. 理解Canvas的save()和restore()方法
  3. JQuery图表插件之Flot
  4. java线程泄露_面试官:小伙子先来说一下可能引起Java内存泄露的场景吧
  5. 网站前端设计,从960框架开始
  6. 普罗米修斯使用es数据库_用普罗米修斯和格拉法纳仪法来豪猪
  7. (转载)ubuntu开启SSH服务
  8. catkin_make和cmake
  9. 安装Ubuntu后必须要做的几件事(一)--基础应用篇
  10. [密码学]利用docker安装与使用sagemath
  11. mapinfo二次开发之:MapX和MapXtreme区别
  12. dismiss和remove_为什么不会在onDestroy或onPause中解雇Dialog,removeDialog或dialog.dismiss工作?...
  13. 概述、 BGP AS 、BGP 邻居、 BGP 更新源 、BGP TTL 、BGP路由表、 BGP 同步
  14. 编写一个静态方法lg(),接收一个整型参数N,返回不大于log2N(以2为底)的最大整数。不要使用Math库。
  15. Windows 系统维护
  16. 【IEEE_Verilog-4.4】Verilog中的充电强度charge strength和驱动强度drive strength
  17. Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed.
  18. 从事网络安全,可以考取什么证书?
  19. 你有什么道理后悔没早点知道
  20. 有关面试八股文的一些难点

热门文章

  1. Reporting Services Internal Error(诡异的问题)-【转载】
  2. php建一个表按删除就删除,mysql表的清空、删除和修改操作详解
  3. Android ScrollView用法实例汇总
  4. 网页内容若使用gzip压缩--获得页面源码
  5. My first project
  6. HDU 5536 字典树
  7. 开发者自述:我是如何从 0 到 1 走进 Kaggle 的
  8. Office365下部署SharePoint站点集
  9. Linux Bash命令关于程序调试详解
  10. C++ 基础 - woaidongmao - C++博客 good 量产