众所周知 linux 下库文件编译三部曲 config make makeinstall
configure过程中可能会遇到无法找到某些头文件和动态库;原因有两个:
(1)系统没有这些头文件和动态库。(locate XXXX.h/so 未找到)
(2)已经安装相关的头文件和动态库。但未将头文件和动态库拷贝到标准路径下。
config 时如果使用第三方库如何呢
接下来我们以一个很简单的使用opencv库读取并显示图片的程序做示例。
//文件路径
.
├── cat.jpeg
└── test.cpp
2 directories, 8 files
//test.cpp 读取图片
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
if ( argc != 2 )
{
printf("usage: DisplayImage.out <Image_Path>\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;
}
Linux下gcc 链接第三方库
如果使用gcc 编译器 那么使用第三方链接库是这样子的
#!/bin/bash
g++ -o opencvtest test.cpp \
-I /home/ywz/WorkSpace/install/include/opencv \
-I /home/ywz/WorkSpace/install/include \
-L /home/ywz/WorkSpace/install/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs
编译后生成可执行文件opencvtest
如果 一个大型的工程 有很多的第三方链接库 那么我们使用gcc 链接库要一个一个的设置链接路径 是很繁琐且容易出错的
有没有更简单的方法呢
这里就可以使用pkg-config工具来管理已安装的链接库了
linux下工具 pkg-config 链接第三方库
pkg-config 简介
这部分介绍是机器翻译的 可以对比原文看。
名称
PKG - 配置 -返回的元信息有关安装的库
描述
该PKG - 配置程序是用来检索有关系统安装的库信息。它通常用于编译和链接一个或多个库。这是Makefile中的典型使用场景:
Here is a typical usage scenario in a Makefile:
program: program.c
···
cc program.c 'pkg-config --cflags --libs gnomeui'
···
pkg-config retrieves information about packages from special metadata files. These files are named after the package, with the extension .pc. By default, pkg-config looks in the directory prefix/lib/pkgconfig for these files; it will also look in the colon-separated (on Windows, semicolon-separated) list of directories specified by the PKG_CONFIG_PATH environment variable.
The package name specified on the pkg-config command line is defined to be the name of the metadata file, minus the .pc extension. If a library can install multiple versions simultaneously, it must give each version its own name (for example, GTK 1.2 might have the package name “gtk+” while GTK 2.0 has “gtk±2.0”).
pkg - config从特殊的元数据文件中检索有关软件包的信息。这些文件以软件包名称命名,扩展名为 .pc。默认情况下, pkg - config在目录prefix / lib / pkgconfig中查找这些文件;它还将在由PKG_CONFIG_PATH环境变量指定的目录的冒号分隔(在Windows中,用分号分隔)列表中查找。
在pkg - config命令行上指定的软件包名称定义为元数据文件的名称,减去.pc扩展名。如果一个库可以同时安装多个版本,则它必须为每个版本指定自己的名称(例如,GTK 1.2的程序包名称为“ gtk +”,而GTK 2.0的程序包名称为“ gtk + -2.0”)。
…
环境变量
PKG_CONFIG_PATH
用冒号分隔(在Windows中,用分号分隔)的目录列表,以搜索.pc文件。搜索路径后,将始终搜索默认目录。默认值为libdir / pkgconfig:datadir / pkgconfig其中libdir是libdir,其中pkg - config和 datadir是安装pkg - config的datadir 。
PKG_CONFIG_DEBUG_SPEW
如果设置,将导致pkg - config打印各种调试信息并报告所有错误。
PKG_CONFIG_TOP_BUILD_DIR
为魔术变量pc_top_builddir设置的值,该值可能出现在.pc文件中。如果未设置环境变量,则将使用默认值’$(top_builddir)’。此变量应引用Makefile的最高builddir,将在其中使用pkg - config报告的编译/链接标志。这仅在针对尚未安装的软件包进行编译/链接时才重要。
pkg-config 的作用讲解推荐看这个博客pkg-config
比较重要的部分内容是
pkg-config给出了opencv的头文件和库的所有信息!
这有什么好处?
所有用opencv的其他程序,在编译时,只需要写“”,而不需要自己去找opencv的头文件在哪里,要链接的库在哪里!省时省力!
如果你写了一个库,不管是静态的还是动态的,要提供给第三方使用,那除了给人家库/头文件,最好也写一个pc文件,这样别人使用就方便很多,不用自己再手动写依赖了你哪些库,只需要敲一个”pkg-config [YOUR_LIB] –libs –cflags”。
pkg-config的信息从哪里来?
很简单,有2种路径:
第一种:取系统的/usr/lib下的所有*.pc文件。
第二种:PKG_CONFIG_PATH环境变量所指向的路径下的所有*.pc文件。
这些pc文件什么时候有的?都是在你安装某个库/模块的时候,添加的。比如你往系统安装opencv时,就会在/usr/lib/目录下,放一个opencv.pc。
如何添加自己的pc文件
如上文所说,有2种方式.:
第一种:把你的pc文件,直接放到/usr/lib/…默认路径下。
第二种: 把你的pc文件的路径写到PKG_CONFIG_PATH环境变量里。
如何将pc文件的路径添加到PKG_CONFIG_PATH环境变量里呢。
linux下 环境变量
linux下 环境变量讲解推荐看博客:环境变量的区别
主要内容是:
登入系统时读入一些环境变量的配置方案,读取步骤:
当登入系统时候获得一个shell进程时,其读取环境设定档有三步 :
1.首先读入的是全局环境变量设定档/etc/profile,然后根据其内容读取额外的设定的文档,如 /etc/profile.d和/etc/inputrc
2.然后根据不同使用者帐号,去其家目录读取~/.bash_profile,
如果这读取不了就读取~/.bash_login,
这个也读取不了才会读取~/.profile(可以设定本用户专有的路径,环境变量等,它只在登入的时候执行一次),这三个文档设定基本上是一样的,读取有优先关系
3.然后在根据用户帐号读取~/.bashrc (也是某用户专有设定文档,可以设定路径,命令别名,每次shell script的执行都会使用它一次)
若该变量为扩增变量内容时,则可用 “$变量名称”+
变
量
累
加
内
容
,
如
下
所
示
:
『
P
A
T
H
=
"
{变量} 累加内容,如下所示: 『PATH="
变量累加内容,如下所示:『PATH="PATH":/home/bin』
若该变量需要在其他子程序执行,则需要以 export 命令使变量发成环境变量: 『export PATH』
利用 source 或小数点 (.) 都可以将配置文件癿内容读取到目前的 shell 环境中!
配置pkg-config
根据上面的信息
修改bash.bashrc文件
sudo vim ~/.bashrc
//(或者修改其他几个环境配置文件)
//在最后加入:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/OPENCV_DIR_PATH/lib/pkgconfig
export PKG_CONFIG_PATH
//保存后使用source命令
source /etc/bash.bashrc
//查看变量是否生效
echo $PKG_CONFIG_PATH //可以看见添加成功
pkg-config opencv --libs --cflags //可以看见正确查找opencv
好了,经过上述设置后,就可以使用pkg-config顺利链接opencv了。
//pkg-config 编译链接opencv
g++ test.cpp -o opencvtest1 `pkg-config opencv --cflags --libs`
Linux 下工具 cmake 链接第三方库
findpackage搜索路径
这个问题在CMake官方文档中有非常详细的解答。详细可以查看官方文档。
首先是查找路径的根目录。把几个重要的默认查找目录总结如下:
<package>_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH
其中,PATH
中的路径如果以bin
或sbin
结尾,则自动回退到上一级目录。
找到根目录后,cmake会检查这些目录下的
<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/ (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/ (U)
cmake找到这些目录后,会开始依次找<package>Config.cmake
或Find<package>.cmake
文件。找到后即可执行该文件并生成相关链接信息。
查找路径的根目录中比较重要的一个是PATH
。由于/usr/bin/
在PATH
中,cmake会自动去/usr/(lib/<arch>|lib|share)/cmake/<name>*/
寻找模块,这使得绝大部分我们直接通过apt-get
安装的库可以被找到。
另外一个比较重要的是<package>_DIR
。我们可以在调用cmake时将这个目录传给cmake。由于其优先级最高,因此cmake会优先从该目录中寻找,这样我们就可以随心所欲的配置cmake使其找到我们希望它要找到的包。而且除上述指定路径外,cmake还会直接进入<package>_DIR
下寻找。
这样做以后,cmake会优先从该目录寻找OpenCV
。
模块模式中
findpackage指令默认使用模块模式
第一种方式 拷贝或编写find*.cmake文件到搜索路径中
模块模式的搜索路径是什么呢? 我并没有找到文档说明,猜测应该是某个环境变量。应该包括 /usr/share/cmake/Modules,这其中还有很多cmake自带的模块文件
第二种方式 把*.cmake文件所在的路径添加到CMAKE_MODULE_PATH 变量中
CMake searches for a file called Find.cmake in the CMAKE_MODULE_PATH followed by the CMake installation.
CMAKE_MODULE_PATH变量默认为空
用户可以使用set指令指定
常用方式为
//set指令设置CMAKE_MODULE_PATH
set()
如果模块模式没有找到 会使用配置模式继续查找
If no module is found and the MODULE option is not given the command proceeds to Config mode.
这里不做出模块模式的示例了。
配置模式
Config mode attempts to locate a configuration file provided by the package to be found. A cache entry called <package>_DIR is created to hold the directory containing the file
如果模块模式没有找到 那么就在搜索路径中查找
*config.cmake 文件
找到后即可执行该文件并生成相关链接信息。
也可以直接设定_DIR 来指定文件目录
opencv示例-
//CMakeLists.txtt
cmake_minimum_required(VERSION 2.8)
project( testopencv )
//设置搜索路径的CMAKE_PREFIX_PATH变量
set(CMAKE_PREFIX_PATH /home/ywz/WorkSpace/install/share/OpenCV)
//或者直接设定<package>_DIR ,二者选一
set(OpenCV_DIR /home/ywz/WorkSpace/install/share/OpenCV)
find_package( OpenCV REQUIRED )
add_executable( testopencv test.cpp )
target_link_libraries( testopencv ${OpenCV_LIBS} )
但是上面这两种设置方法需要每一个项目都设定,如果是从别的地方下载的工程直接编译是找不到opencv的,每个工程都需要修改cmakelists.txt才能编译成功,不是很方便。还有另一个方法即在~/.bashr 中设置环境变量,这样每个工程都可以找到opencv’库。
sudo gedit ~/.bashrc
添加
CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/home/ywz/WorkSpace/install/share/OpenCV
export CMAKE_PREFIX_PATH
使其生效
source ~/.bashrc
//这时使用env 查看环境变量
CMAKE_PREFIX_PATH=:/home/ywz/WorkSpace/install/share/OpenCV
这样每个工程都可以使用opencv库而不用显试修改。
Linux 下可执行文件运行时加载第三方库
我们仅仅完成了程序的编译和链接,还有最后一步,程序运行;程序运行需要加载动态链接库并运行,也需要找寻动态链接库的路径,pkg-config仅负责编译链接时头文件和动态链接库的查找。对于程序运行时加载动态链接库,pkg-config就无能为力了,此时需要用到ldconfig命令,该命令为程序运行时提供动态链接库的运行时绑定,通过加载/etc/ld.so.conf下的路径信息,生成动态库的缓存/etc/ld.so.cache,为程序运行时提供动态库的链接;通过ldconfig -p查看动态链接库缓存信息。因此需要将我们的动态链接库路径添加到/etc/ld.so.conf中,运行ldconfig更新cache,就大功告成了。
ldconfig是一个动态链接库管理命令,其目的为了让动态链接库为系统所共享。
ldconfig的主要用途:
默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。
搜索出可共享的动态链接库,库文件的格式为:lib***.so.**,进而创建出动态装入程序(ld.so)所需的连接和缓存文件。
缓存文件默认为/etc/ld.so.cache,该文件保存已排好序的动态链接库名字列表。
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。
ldconfig需要注意的地方:
1、往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。
2、如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装MySQL的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下:
echo “/usr/local/mysql/lib” >> /etc/ld.so.conf
#ldconfig -v | grep mysql
3、如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。一般来讲这只是一种临时的解决方案,在没有权限或临时需要的时候使用。
可执行文件如何寻找动态库
在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
中,可执行文件就能正确找到该目录。
相信经过上述的讲解,不管是多版本的opencv库,还是ROS中的opencv库 ,在应用中都可以熟练解决。
本文的涉及到到的文件完整路径为
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── Makefile
│ └── testopencv
├── cat.jpeg
├── CMakeLists.txt
├── gccbuild.sh
├── opencvtest
├── opencvtest1
├── pkgbuild.sh
└── test.cpp
2 directories, 12 files
相关文件后续可以在我的github获得。
参考:
Linux man page 手册
Linux 下C++文件 编译 静态/动态 链接库
CMake packages 手册
cmake findpackage 手册
CMake findapackage 博客
opencv环境配置
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)