【转】理解 pkg-config 工具

原文网址:http://www.chenjunlu.com/2011/03/understanding-pkg-config-tool/

你在 Unix 或 Linux 下开发过软件吗?写完一个程序,编译运行完全正常,在你本机上工作得好好的,你放到源代码管理系统中。然后,告诉你的同事说,你可以取下来用了。这时,你长长的出了一口气,几天的工作没有白费,多么清新的空气啊,你开始飘飘然了。

“Hi,怎么编译不过去?”你还沉浸在那种美妙的感觉之中,双臂充满着力量,似乎没有什么问题能难倒你的。正在此时,那个笨蛋已经冲着你嚷开了。

“不会吧,我这边好好的!”表面上你说得很客气,其实,你心里已经骂开了,真笨,不知道脑子干嘛用的。也许,你想的没错,上次,他犯了一个简单的错误,不是你一去就解决了吗。

他喊三次之后,你不得不放下你手上的工作,刚才那种美妙的感觉已经消失得无影无踪了,要不是你把情绪控制得很好,一肚子气就要撒在他身上了。你走到他的电脑前,键入 make,优雅的按下回车。怎么可能出错呢?你信心十足。然而,屏幕上的结果多少有点让人脸红,该死的,libxxx.so 怎么会让不到呢?

你在/usr目录中查找 libxxx.so,一切都逃不过你的眼睛。奇怪,libxxx.so 怎么在 /usr/local/lib 下,不是应该在 /usr/lib 下的吗?这你可不能怪别人,别人想安装在哪里都行,下次还可能安装到 /lib 目录下呢。

以上的场景并非虚构,我都经历过好几次,明明在本机上好好的,在别人的机器上连编译都过不去。可能两人的操作系统一模一样,需要的库都安装上,只是由于个人 喜好不同,安装在不同的目录而已。遇到这种情况,每次都技巧性的绕过去了,用的补丁型的方法,心里老惦记其它地方能不能工作。

今天我们要介绍的 pkg-config,为解决以上问题提供了一个优美方案。从此,你再也不为此担忧了。pkg-config提供了下面几个功能:

  1. 检查库的版本号。如果所需要的库的版本不满足要求,它会打印出错误信息,避免链接错误版本的库文件。
  2. 获得编译预处理参数,如宏定义,头文件的位置。
  3. 获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数。
  4. 自动加入所依赖的其它库的设置。

这一切都自动的,库文件安装在哪里都没关系!

在使用前,我们说说 pkg-config 的原理,pkg-config 并非精灵,可以凭空得到以上信息。事实上,为了让pkg-config可以得到这些信息,要求库的提供者,提供一个.pc文件。比如gtk+-2.0的pc文件内容如下:

prefix=/usr
exec_prefix=/usr
libdir=/usr/lib
includedir=/usr/include
target=x11

gtk_binary_version=2.4.0
gtk_host=i386-redhat-linux-gnu

Name: GTK+
Description: GIMP Tool Kit (${target} target)
Version: 2.6.7
Requires: gdk-${target}-2.0 atk
Libs: -L${libdir} -lgtk-${target}-2.0
Cflags: -I${includedir}/gtk-2.0

这个文件一般放在 /usr/lib/pkgconfig/ 或者 /usr/local/lib/pkgconfig/ 里,当然也可以放在其它任何地方,如像 X11 相关的pc文件是放在 /usr/X11R6/lib/pkgconfig 下的。为了让pkgconfig可以找到你的pc文件,你要把pc文件所在的路径,设置在环境变量 PKG_CONFIG_PATH 里。

使用 pkg-config 的 –cflags 参数可以给出在编译时所需要的选项,而 –libs 参数可以给出连接时的选项。例如,假设一个 sample.c 的程序用到了 Glib 库,就可以这样编译:

$ gcc -c `pkg-config –cflags glib-2.0` sample.c

然后这样连接:

$ gcc sample.o -o sample `pkg-config –libs glib-2.0`

或者上面两步也可以合并为以下一步:

$ gcc sample.c -o sample `pkg-config –cflags –libs glib-2.0`

可以看到:由于使用了 pkg-config 工具来获得库的选项,所以不论库安装在什么目录下,都可以使用相同的编译和连接命令,带来了编译和连接界面的统一。

使用 pkg-config 工具提取库的编译和连接参数有两个基本的前提:

  • 库本身在安装的时候必须提供一个相应的 .pc 文件(不这样做的库说明不支持 pkg-config 工具的使用)。
  • pkg-config 必须知道要到哪里去寻找此 .pc 文件。

GTK+ 及其依赖库支持使用 pkg-config 工具,所以剩下的问题就是如何告诉 pkg-config 到哪里去寻找库对应的 .pc 文件,这也是通过设置搜索路径来解决的。

对于支持 pkg-config 工具的 GTK+ 及其依赖库来说,库的头文件的搜索路径的设置变成了对 .pc 文件搜索路径的设置。.pc 文件的搜索路径是通过环境变量 PKG_CONFIG_PATH 来设置的,pkg-config 将按照设置路径的先后顺序进行搜索,直到找到指定的 .pc 文件为止。

安装完 Glib 后,在 bash 中应该进行如下设置:

$ export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH

可以执行下面的命令检查是否 /opt/gtk/lib/pkgconfig 路径已经设置在 PKG_CONFIG_PATH 环境变量中:

$ echo $PKG_CONFIG_PATH

这样设置之后,使用 glib 库的其它程序或库在编译的时候 pkg-config 就知道首先要到 /opt/gtk/lib/pkgconfig 这个目录中去寻找 glib-2.0.pc 了(GTK+ 和其它的依赖库的 .pc 文件也将拷贝到这里,也会首先到这里搜索它们对应的 .pc 文件)。之后,通过 pkg-config 就可以把其中库的编译和连接参数提取出来供程序在编译和连接时使用。

另外还需要注意的是:环境变量的设置只对当前的终端窗口有效。如果到了没有进行上述设置的终端窗口中,pkg-config 将找不到新安装的 glib-2.0.pc 文件、从而可能使后面进行的安装(如 glib 之后的 Atk 的安装)无法进行。

在我们采用的安装方案中,由于是使用环境变量对 GTK+ 及其依赖库进行的设置,所以当系统重新启动、或者新开一个终端窗口之后,如果想使用新安装的 GTK+ 库,需要如上面那样重新设置 PKG_CONFIG_PATH 和 LD_LIBRARY_PATH 环境变量。

这种使用 GTK+ 的方法,在使用之前多了一个对库进行设置的过程。虽然显得稍微繁琐了一些,但却是一种最安全的使用 GTK+ 库的方式,不会对系统上已经存在的使用了 GTK+ 库的程序(比如 GNOME 桌面)带来任何冲击。

为了使库的设置变得简单一些,可以把下面的这两句设置保存到一个文件中(比如 set_gtk-2.10 文件):

export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

之后,就可以用下面的方法进行库的设置了(其中的 source 命令也可以用 . 代替):

$ source set_gtk-2.10

只有在用新版的 GTK+ 库开发应用程序、或者运行使用了新版 GTK+ 库的程序的时候,才有必要进行上述设置。

如果想避免使用 GTK+ 库之前上述设置的麻烦,可以把上面两个环境变量的设置在系统的配置文件中(如 /etc/profile)或者自己的用户配置文件中(如 ~/.bash_profile) ;库的搜索路径也可以设置在 /etc/ld.so.conf 文件中,等等。这种设置在系统启动时会生效,从而会导致使用 GTK+ 的程序使用新版的 GTK+ 运行库,这有可能会带来一些问题。当然,如果你发现用新版的 GTK+ 代替旧版没有什么问题的话,使用这种设置方式是比较方便的。

库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:

  1. 在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。
  2. 在  /etc/ld.so.conf  文件中添加库的搜索路径。

将自己可能存放库文件的路径都加入到 /etc/ld.so.conf 中是明智的选择。( ^_^)

添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:

/usr/X11R6/lib
/usr/local/lib
/opt/lib

需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是 因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件 /etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache 是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf 中设置的搜索路径由 /sbin/ldconfig 命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig 命令要以 root 权限执行)。因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf 中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新 /etc/ld.so.cache 文件之后才可以。ldconfig ,简单的说,它的作用就是将 /etc/ld.so.conf 列出的路径下的库文件缓存到/etc/ld.so.cache以供使用。因此当安装完一些库文件(例如刚安装好 glib 或者修改 ld.so.conf 增加新的库路径)后,需要运行一下 /sbin/ldconfig 使所有的库文件都被缓存到 ld.so.cache 中,如果没做,即使库文件明明就在 /usr/lib 下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂 computer 蠢猪一个。 (^_^)

在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过 -L 参数显式指定。因为用 -L 设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。

前面已经说明过了,库搜索路径的设置有两种方式:在环境变量 LD_LIBRARY_PATH 中设置以及在 /etc/ld.so.conf 文件中设置。其中,第二种设置方式需要 root 权限,以改变 /etc/ld.so.conf 文件并执行 /sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于 GTK2 的程序在运行时都将使用新安装的 GTK+ 库。不幸的是,由于 GTK+ 版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。为了避免出现上面的这些情况,在 GTK+ 及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要 root 权限,设置也简单:

$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:

$ echo $LD_LIBRARY_PATH

最后,我来总结一下,PKG_CONFIG_PATH 主要指明.pc文件的所在路径,这样 pkg-config 工具就可以根据.pc文件的内容动态生成编译和连接选项,比如 Cflags (编译用)和 Libs (连接用),如果使用的是动态链接库,那么程序在连接和运行时,一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,对于处于默认库搜索路径之外的库,系统管理员可以设置 LD_LIBRARY_PATH 环境变量或在 /etc/ld.so.conf 文件中添加库的搜索路径。值得说明的是,使用 gcc 连接时的选项,如果不用 pkg-config 工具,需要显示的声明连接的动态链接库名。使用 gcc 的同学可以查看下面的注意事项。

Linux 系统中,为了让动态链接库能被系统中其它程序共享,其名字应符合 lib*.so.* 这种格式。如果某个动态链接库不符合此格式,则 Linux 的动态链接库自动装入程序(ld)将搜索不到此链接库,其它程序也无法共享之。格式中,第一个*通常表示为简写的库名,第二个*通常表示为该库的版本号。如在我的系统中,基本C动态链接库的名字为 libc.so.6,线程 pthread 动态链接库的名字为 libpthread.so.0 等等。如果没有指定版本号,比如 libmy.so ,这也是符合要求的格式。

gcc 命令几个重要选项:

  • -shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号,不用该标志外部程序无法连接。相当于一个可执行文件)。
  • -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
  • -L.:表示要连接的库在当前目录中。
  • -lmy:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称(libmy.so)。

当然如果有 root 权限的话,可以修改 /etc/ld.so.conf 文件,然后调用 /sbin/ldconfig 来达到同样的目的,不过如果没有 root 权限,那么只能采用输出 LD_LIBRARY_PATH 的方法了。

相关日志:

时间: 2024-10-08 14:56:29

【转】理解 pkg-config 工具的相关文章

从源码上理解Netty并发工具-Promise

前提 最近一直在看Netty相关的内容,也在编写一个轻量级的RPC框架来练手,途中发现了Netty的源码有很多亮点,某些实现甚至可以用苛刻来形容.另外,Netty提供的工具类也是相当优秀,可以开箱即用.这里分析一下个人比较喜欢的领域,并发方面的一个Netty工具模块 - Promise. 环境版本: Netty:4.1.44.Final JDK1.8 Promise简介 Promise,中文翻译为承诺或者许诺,含义是人与人之间,一个人对另一个人所说的具有一定憧憬的话,一般是可以实现的. io.n

[深入理解Android卷二 全文-第五章]深入理解PowerManagerService

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第5章  深入理解PowerManagerService 本章主要内容: ·  深入分析PowerManagerService ·  深入分析BatteryService和BatteryStatsService 本章所涉及的源代码文件名及位置: ·  PowerManagerService.java frame

vue-cli的webpack模版,相关配置文件dev-server.js与webpack.config.js配置解析

1.下载vue-cli [html] view plain copy npm install vue-cli -g vue-cli的使用与详细介绍,可以到github上获取https://github.com/vuejs/vue-cli 2.安装webpack项目模版 [html] view plain copy vue init <template-name> <project-name> 比如: [html] view plain copy vue init webpack m

隐马可夫模型:探索看不到的世界的数学工具

这篇要讨论的可不是哲学议题,而是希望以一个“数学工具”的角度来看隐马可夫模型(Hidden Markov Model, HMM)是什么,它的背后假设.长处与限制,以理解这样的工具可以拿来做什么用,而不是只与特定的应用绑在一起. 隐马可夫模型?隐藏的马可夫模型? 隐马可夫模型是机器学习(Machine Learning)领域中常常用到的理论模型,从语音识别(Speech Recognition).手势辨识(gesture recognition),到生物信息学(Bioinformatics)里的种

第一次编写简单的中间件测试工具(1) - 记一次新员工训练营

去年11月,我加入了N记,紧接着进入新员工训练营. 开始一次简单的中间件测试工具编写任务. 这次训练营体验给我的感觉就是:大公司不愧是大公司,这回我终于可以安心学点核心技术了. 任务: 这个训练营有两个任务,一是熟悉这边的敏捷开发流程:二是在训练营里做一定的编码,用python编写一个测试工具(桩,stub). 我们要做的这个工具,是用来测试我们一种通信设备(B)上运行的程序(某种中间件),这个工具模拟另一种通信设备(A),发送一些按特定协议编码的消息给另一种通信设备B,并能反编码设备B返回的消

DevOps开源工具的三种分类整理

原文地址:http://www.360doc.com/content/16/0322/07/31263000_544210096.shtml 随着开发运维一体化的DevOps运动在国内外蓬勃发展,DevOps相关工具也呈现热闹趋势,在这个言必谈如何实施落地引入工具.建设平台的大环境下,我们今天也来盘点一下DevOps相关工具. 先来看一下业界对DevOps工具的各种分类介绍. 一.DevOps应用交付工具链   ElasticBox是国外一个云应用管理工具,主要用于实现云应用生命周期的可视化管理

地理信息系统 - ArcGIS - 高/低聚类分析工具(High/Low Clustering ---Getis-Ord General G)

前段时间在学习空间统计相关的知识,于是把ArcGIS里Spatial Statistics工具箱里的工具好好研究了一遍,同时也整理了一些笔记上传分享.这一篇先聊一些基础概念,工具介绍篇随后上传. 空间统计研究起步于上个世纪70年代,空间统计其核心就是认识与地理位置相关的数据间的空间依赖.空间关联等关系,通过空间位置建立数据间的统 计关系.空间统计学依赖于tablor地理学第一定律,即空间上越临近的事物拥有越强的相似程度:和空间异质性,即空间位置差异造成的行为不确定现象.例 如要度量犯罪率与教育程

mysql监控、性能调优及三范式理解

原文:mysql监控.性能调优及三范式理解 1监控 工具:sp on mysql     sp系列可监控各种数据库 2调优 2.1 DB层操作与调优 2.1.1.开启慢查询 在My.cnf文件中添加如下内容(如果不知道my.cnf的路径可使用find / -name my.cnf进行查找): 在mysqld下添加 Log_slow_queries = ON  作用:开启慢查询服务 Log-slow-queries = /var/log/slowqueries.log 作用:慢查询日志存储路径.

从工程的角度上理解coding

#从工程上理解coding---我对软件工程的理解 ##语言只是工具对于刚刚开始学习计算机程序的我.多少还是有些迷茫.曾经一度认为代码是神圣的不可侵犯的,或者说,是一个我所不能够理解的范畴,是那些大牛们才能够理解的才能够明白的,初入大一,我学习了c和c++,虽然没有挂科,但对于到底什么是程序仍然是一头雾水,我似乎只会在那个黑框框里做一些简单的加减乘除的预算,除此之外,别无他技.但如果从工程的角度来看,我所学到的却是神奇的与计算机交流的方式,途径.有了这些我就能够给计算机表达我想让他做的事情,那这

关于cgi,fastcgi,php-fpm的一些个人理解

个人将cgi理解为翻译工具,举个例子,webserver如apache是中国人,收到的请求访问php文件是法国人,两者语言不通,无法沟通.这时cgi作为一个接口工具,为两者做翻译的服务.cgi程序会解析php的内容,以双方约定的形式返回web服务器. cgi全称是"公共网关接口"(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行"交谈"的一种工具,其程序须运行在网络服务器上.当web server(nginx)收到/in