感谢支持
我们一直在努力

pkg-config指南

原文:


Guide for pkg-config


http://people.freedesktop.org/~dbn/pkg-config-guide.html 


Dan Nicholson



—————————————————–



概述


为什么?


一些概念


写pkg-config文件


使用pkg-config文件


常见问题


———————————————–


概述


这个文档的目的是从用户和开发者的角度给一个pkg-config工具的使用概述。本文复习一些pkg-config背后的概念,怎样写pkg-config文件来支持你的项目,以及怎样用pkg-config集成第三方项目。



关于pkg-config的更多信息可以在web站点和pkg-config的man手册中找到。



本文档假的pkg-config在类UNIX操作系统中使用,例如Linux。其他平台可能在一些细节上的存在差别。



为什么?


现代计算机系统使用了很多分层组件为用户提供应用。其中一个困难就是如何正确的整合这些组件。pkg-config会收集系统中安装的库的数据,然后提供给用户。



如果没有pkg-config这样的数据系统,定位计算机提供的服务和获取它们的细节会很困难。对于开发者,安装软件包的pkg-config文件极大的简化了对API的获取。



一些概念


使用pkg-config的初级阶段是为编译和链接程序时提供必要的细节。数据存储在pkg-config文件中。这些文件有一个.pc的后缀,放在一个特定的、pkg-config工具所知道的位置。我们会在后面描述更多的细节。




这个文件的格式包括预定义的关键字和自由形式的变量。例如:



[plain]


  1. prefix=/usr/local  

  2. exec_prefix=${prefix}  

  3. includedir=${prefix}/include  

  4. libdir=${exec_prefix}/lib  

  5. Name: foo  

  6. Description: The foo library  

  7. Version: 1.0.0  

  8. Cflags: -I${includedir}/foo  

  9. Libs: -L${libdir} -lfoo  

以预定义关键字Name:为例,以关键字开头,后面跟一个冒号和一个值。变量是一个字符串和一个值,例如prefix=,用等号分开。关键字是由pkg-config定义和输出的。变量不是必须的,但可以被关键字用来定位和存储pkg-config没有覆盖的数据。




这里只是简单的描述一下关键字。更深入的描述和怎样有效的使用它们将在“写pkg-config文件”段中给出。


Name:一个人们可读的链接库或软件包的名称,这不影响pkg-config的使用,它用的是.pc文件的名称。


Description:关于软件包的简单描述。


URL:一个URL,可以在那里获得更多的信息,并且下载这个软件包。


Version:软件包的版本。


Requires:这个软件包所需的包的列表。这些包的版本可能用一写运算符来指定:=、>、<、>=、<=。


Requires.private:这个软件包所需的私有包的列表,不会暴露给应用。版本的指定规则与Requires相同。


Conflicts:可选,描述了会与这个软件包产生冲突的包。版本的指定规则与Requires相同。这个域会提供同一个包的多个实例,例如:Conflicts: bar < 1.2.3, bar >= 1.3.0。


Cflags:为这个软件包指定编译器选项,以及pkg-config不支持的必要的库。如果所需的库支持pkg-config,应该将它们添加到Requires和Requires.private。


Libs:为这个软件包指定的链接选项,以及pkg-config不支持的必要的库。与Cflags的规则相同。


Libs.private:这个软件包所需的私有库的链接选项,不会暴露给应用。规则与Cflags相同。




写pkg-config文件


为一个软件包创建pkg-config时,首先要确定怎样描述它。一个文件最好只用于描述一个库,所以,每个软件包至少需要像它所需的链接库那么多的pkg-config文件。




软件包的名字是由pkg-config数据文件的名字确定的。就是文件名去掉.pc后缀的那一部分。通常都用库的名字命名.pc文件。例如,一个安装libfoo.so的包会有一个相应的libfoo.c文件来包含pkg-config数据。这不是必须的,.pc文件仅仅是一个对你的库的唯一标识符。所以,foo.pc或foolib.pc也能正常工作。




Name、Description和URL的值是纯粹的信息,容易填写。Version比较棘手,它要确保这个包可以被用户使用。pkg-config使用RPM算法来进行版本比较。Version最好是用点分开的十进制数字,例如1.2.3,因为字母可能引起意外的结果。数字应该是单调递增的,并且要竟可能具体的描述这个库。通常使用包的版本号即可,这样可以方便使用者跟踪。




在描述更多的有用的关键字之前,有必要展示一下变量的定义。最常见的用法是定义安装路径,这样就不会使其他字段显得杂乱。因为变量是扩大递归的,在结合autoconf派生路径时,这会很有用。


[plain]


  1. prefix=/usr/local  

  2. includedir=${prefix}/include  

  3. Cflags: -I${includedir}/foo  



最重要的pkg-config数据字段是Requires,Requires.private,Cflags,Libs 和 Libs.private。它们定义的数据被外部项目用来编译和链接库。




Requires和Requires.private定义了库所需的其他模块。通常首选Requires.private,以便避免程序链接到一些不必要的库。如果一个程序不使用所需库的符号,它就不应该直接链接到这个库。可以在overlinking的讨论中看到更多详细的解释。




由于pkg-config通常会公开Requires库的链接标识,这些模块会变成程序的直接依赖。另外,Requires.private中的库只有在静态链接是才会被包含。正因如此,pkg-config通常只会适当的从Requires中的同一个包中添加模块。




Libs包含了使用库是所必须的链接标识。此外,Libs和Libs.private还包含了pkg-config不支持的库的链接标识。与Requires类似,首选将外部库的链接标识添加到Libs.private,这样,程序就不会获得额外的直接依赖。




最后,Cflags包含了所用的库的编译标识。与Libs不同,Cflags没有私有变种。这是因为,数据类型和宏定义在任何链接情况下都是需要的。




使用pkg-config文件


假设系统中已经安装了.pc文件,pkg-config工具就被用来提取其中的数据。执行pkg-config –help命令可以看到一些关于命令选项的简单描述。深入的描述可以在pkg-config(1)的man手册页中找到。本地将对一些常见的用法进行简单的描述。




假设系统中已经有了两个模块:foo和bar。它们的.pc文件可能像下面这样:


[plain]


  1. foo.pc:  

  2. prefix=/usr  

  3. exec_prefix=${prefix}  

  4. includedir=${prefix}/include  

  5. libdir=${exec_prefix}/lib  

  6. Name: foo  

  7. Description: The foo library  

  8. Version: 1.0.0  

  9. Cflags: -I${includedir}/foo  

  10. Libs: -L${libdir} -lfoo  

  11. bar.pc:  

  12. prefix=/usr  

  13. exec_prefix=${prefix}  

  14. includedir=${prefix}/include  

  15. libdir=${exec_prefix}/lib  

  16. Name: bar  

  17. Description: The bar library  

  18. Version: 2.1.2  

  19. Requires.private: foo >= 0.7  

  20. Cflags: -I${includedir}  

  21. Libs: -L${libdir} -lbar  


模块的版本可以用–modversion选项获得。


[Python]


  1. $ pkg-config –modversion foo  

  2. 1.0.0  

  3. $ pkg-config –modversion bar  

  4. 2.1.2  



要打印模块的链接标识,就用–libs选项。


[python]


  1. $ pkg-config –libs foo  

  2. -lfoo  

  3. $ pkg-config –libs bar  

  4. -lbar  
请注意,pkg-config压缩了两个模块Libs字段。这是因为pkg-config对-L标识有特殊处理,它知道${libdir}目录/usr/lib是系统链接器搜素路径的一部分。也就是pkg-config受到了链接器选项的影响。



还有就是,虽然foo是bar所需要的,但是没有输出foo的链接标识。这是因为,只使用bar库的应用并不直接需要foo。对应静态链接bar的应用,我们需要两个链接标识:


[python]


  1. $ pkg-config –libs –static bar  

  2. -lbar -lfoo  



这种情况下,pkg-config就要输出两个链接标识,这样才能保证静态链接的应用可以找到所有必须的符号。另一方面,它会输出所有的Cflags字段。


[python]


  1. $ pkg-config –cflags bar  

  2. -I/usr/include/foo    

  3. $ pkg-config –cflags –static bar  

  4. -I/usr/include/foo  



还有一个有用的选项,–exists,可以用来测试模块的可用性。


[python]


  1. $ pkg-config –exists foo  

  2. $ echo $?  

  3. 0  


最值得注意的pkg-config特性是它所提供的版本检测,可以用来确定某个版本是否可用。


[python]


  1. $ pkg-config –exists foo  

  2. $ echo $?  

  3. 0  



有些命令在结合–print-errors选项使用时可以输出更详细的信息。


[python]


  1. $ pkg-config –exists —print-errors xoxo  

  2. Package xoxo was not found in the pkg-config search path.  

  3. Perhaps you should add the directory containing `xoxo.pc’  

  4. to the PKG_CONFIG_PATH environment variable  

  5. No package ‘xoxo’ found  



上面的信息出现了PKG_CONFIG_PATH环境变量。这个变量用来配置pkg-config的搜索路径。在类Unix操作系统中,会搜索/usr/lib/pkconfig和/usr/share/pkgconfig目录。这通常已经覆盖了系统已经安装的模块。但是,有些本地模块可能安装在了其他路径,例如/usr/local。这种情况下,需要指定搜索路径,以便pkg-config可以定位.pc文件。


[python]


  1. $ pkg-config –modversion hello  

  2. Package hello was not found in the pkg-config search path.  

  3. Perhaps you should add the directory containing `hello.pc’  

  4. to the PKG_CONFIG_PATH environment variable  

  5. No package ‘hello’ found  

  6. $ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig  

  7. $ pkg-config –modversion hello  

  8. 1.0.0  



autoconf也提供了一些宏,可以将pkg-config集成到项目中。


* PKG_PROG_PKG_CONFIG([MIN-VERSION]):定位系统中的pkg-config工具,并检测版本兼容性。


* PKG_CHECK_EXISTS(MODULES,[ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]):检测指定的模块是否存在。


* PKG_CHECK_MODULES(VARIABLE-PREFIX,MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])::检测指定的模块是否存在,如果存在,就根据 pkg-config –cflags和pkg-config –libs的输出设置<VARIABLE-PREFIX>_CFLAGS and <VARIABLE-PREFIX>_LIBS。




常见问题


1. 我的程序使用了x库,我该怎么做?


pkg-config的输出可以在编译命令中使用,假设x库已经有了一个叫做x.pc的pkg-config文件:


[python]


  1. cc `pkg-config –cflags –libs x` -o myapp myapp.c  



将pkg-config集成到autoconfautomake中使用会更强大。但是,用PKG_CONFIG_PATH宏可以很容易的在建立过程中访问元数据。


[python]


  1. configure.ac:  

  2. PKG_CHECK_MODULES([X], [x])  

  3. Makefile.am:  

  4. myapp_CFLAGS = $(X_CFLAGS)  

  5. myapp_LDADD = $(X_LIBS)  



如果找到了x模块,宏会填充和替代X_CFLAGS和X_LIBS变量。如果没有找到,会产生错误。配置PKG_CHECK_MODULES的第3、4个参数,可以控制没有找到模块时的动作。




2. 我的z库安装了保护libx头的头文件。我应该在z.pc中添加什么?


如果x库支持pkg-config,将它添加到Requires.private字段。如果不支持,就配置Cflags字段,添加一些使用libx头时所需的编译器标识。在这两种情况下,无论是否使用了–static,pkg-config都会输出编译器标识。




3. 我的z库内部使用了libx,但是不能再公开API中暴露libx的数据类型。我应该在z.pc中添加什么?


同样的,如果x支持pkg-config,就把它添加到Requires.private。这种情况下,就没必要发出编译器标识,但是在今天链接时要确保有链接器标识。如果libx不支持pkg-config,就将必要的链接器标识添加到Libs.private。




——————————————————————


Dan Nicholson <dbn.lists (at) gmail (dot) com>


Copyright (C) 2010 Dan Nicholson.
This document is licensed under the GNU General Public License, Version 2 or any later version.

赞(0) 打赏
转载请注明出处:服务器评测 » pkg-config指南
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏