本节介绍如何制作自己的函数库、安装这个函数库,以及在一个应用工程中如何使用这个函数库。其中用到了工具pkg-config.
一、 pkg-config
1. 为何要用pkg-config
你在 Unix 或 Linux 下开发过软件吗?写完一个程序,编译运行完全正常,在你本机上工作得好好的,你放到源代码管理系统中。然后,告诉你的同事说,你可以取下来用了。这时,你长长的出了一口气,几天的工作没有白费,多么清新的空气啊,你开始飘飘然了。
“Hi,怎么编译不过去?”你还沉浸在那种美妙的感觉之中,双臂充满着力量,似乎没有什么问题能难倒你的。正在此时,那个笨蛋已经冲着你嚷开了。
“不会吧,我这边好好的!”表面上你说得很客气,其实,你心里已经骂开了,真笨,不知道脑子干嘛用的。也许,你想的没错,上次,他犯了一个简单的错误,不是你一去就解决了吗。
他喊三次之后,你不得不放下你手上的工作,刚才那种美妙的感觉已经消失得无影无踪了,要不是你把情绪控制得很好,一肚子气就要撒在他身上了。你走到他的电脑前,键入 make,优雅的按下回车。怎么可能出错呢?你信心十足。然而,屏幕上的结果多少有点让人脸红,该死的,libxxx.so 怎么会让不到呢?
你在/usr目录中查找 libxxx.so,一切都逃不过你的眼睛。奇怪,libxxx.so 怎么在 /usr/local/lib 下,不是应该在 /usr/lib 下的吗?这你可不能怪别人,别人想安装在哪里都行,下次还可能安装到 /lib 目录下呢。
以上的场景并非虚构,我都经历过好几次,明明在本机上好好的,在别人的机器上连编译都过不去。可能两人的操作系统一模一样,需要的库都安装上,只是由于个人 喜好不同,安装在不同的目录而已。遇到这种情况,每次都技巧性的绕过去了,用的补丁型的方法,心里老惦记其它地方能不能工作。
pkg-config为解决以上问题提供了一个优美方案。从此,你再也不为此担忧了。
2. pkg-config简介
pkg-config 是一个提供从源代码中编译软件时查询已安装的库时使用的统一接口的计算机软件。pkg-config原本是设计用于Linux的,但现在在各个版本的BSD、windows、Mac OS X和Solaris上都有着可用的版本。
它输出已安装的库的各个信息,包括:
C或C++编译器需要的参数
链接器需要的参数
已安装软件包的版本信息
工作原理
当安装一个库时(从RPM,deb或其他二进制包管理系统),会包括一个后缀名为pc的文件,它会同其他.pc文件一起放入一个文件夹(依赖与你的系统设置)。
在这个文件里包含有数个条目。这些条目通常包含用于其他使用这个库的程序编译时需要的库设置,以及头文件的位置,版本信息和一个简介。
这是一个用于libpng的.pc文件的样例:
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${exec_prefix}/include
Name: libpng12
Description: Loads and saves PNG files
Version: 1.2.8
Libs: -L${libdir} -lpng12 -lz
Cflags: -I${includedir}/libpng12
这个文件告诉我们这些库可以在/usr/local/lib找到,头文件可以在/usr/local/include里找到,库的名字是libpng12并且版本号是1.2.8。它也提供了用于编译依赖于libpng的源代码时需要的链接器参数。
这儿是一个编译时使用pkg-config的样例:
gcc -o test test.c $(pkg-config --libs --cflags libpng)
二、制作自己的函数库
1. 开始时,只有我自己写的一些.c和.h文件,放在base/src/下
2. 在base和base/src下创建Makefile.am文件
base/Makefile.am
SUBDIRS=src pkgconfigdir=${libdir}/pkgconfig pkgconfig_DATA=base.pc
pkgconfig不是关键字;
dir表示要安装的目录;_DATA表示要安装的文件(两者配合使用)。这里指定了pc文件要被安装到哪里
libdir会被换成base.pc.in中相应的值(它最终由./configure --prefix=...指定的值确定)
base/src/Makefile.am
lib_LTLIBRARIES=libbase.la libbase_la_SOURCES= darray.c \ darray.h \ dlist.c \ dlist.h \ darray_iterator.h \ dlist_iterator.h \ hash_table.c \ hash_table.h \ invert.c \ iterator.h \ linear_container_darray.c \ linear_container_darray.h \ linear_container_dlist.c \ linear_container_dlist.h \ linear_container.h \ queue.c \ queue.h \ sort.c \ sort.h \ stack.c \ stack.h \ typedef.h libbase_la_LDFLAGS=-lpthread noinst_PROGRAMS=darray_test dlist_test darray_test_SOURCES=darray.c darray_test_CFLAGS=-DDARRAY_TEST dlist_test_SOURCES=dlist.c dlist_test_CFLAGS=-DDLIST_TEST basedir=$(includedir)/base base_HEADERS=darray.h dlist.h iterator.h linear_container_dlist.h typedef.h \ darray_iterator.h dlist_iterator.h linear_container_darray.h \ linear_container.h EXTRA_DIST=\ linear_container_test.c \ invert_ng.c \ darray_iterator.c \ dlist_iterator.c \ test_helper.c
lib_LTLIBRARIES=libbase.la 共享库用扩展名.la可以同时生成共享库和静态库。
libbase_la_SOURCES=... libbase.la编译时需要的源文件
libbase_la_LDFLAGS=-lpthread libbase.la链接时需要的参数
noinst_PROGRAMS=darray_test dlist_test 关键字noinst_PROGRAMS指定不需要安装的的可执行文件
darray_test_SOURCES=darray.c darray.c --编译--> darray_test
darray_test_CFLAGS=-DDARRAY_TEST 编译和预处理darray.c时需要的参数
basedir=$(includedir)/base
base_HEADERS=darray.h dlist.h iterator.h ... 配套使用,将_HEADERS列出的头文件安装到dir路径
注: $(includedir)在base.pc.in中指定
3.定制configure.in
#autoscan
autom4te: configure.ac: no such file or directory
autoscan: /usr/bin/autom4te failed with exit status: 1
#ls
autoscan.log configure.scan Makefile.am src
#mv configure.scan configure.in
将configure.in稍作修改
# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) #AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS) AC_INIT(base, 0.1, chuanwang66@163.com) #修改 AC_CONFIG_SRCDIR([src/typedef.h]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE(base, 0,1) #新增 # Checks for programs. AC_PROG_CC AC_PROG_LIBTOOL #新增 # Checks for libraries. # FIXME: Replace `main' with a function in `-lpthread': AC_CHECK_LIB([pthread], [main]) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([stdlib.h string.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT
AC_CONFIG_SRCDIR宏用来侦测所指定的源码文件是否存在,来确定源码目录的有效性。
AC_CONFIG_HEADER宏用于生成config.h文件,以便autoheader使用。
AC_PROG_CC:选择 C 编译器。如果在环境中不设置 CC 的话,则检测 gcc。
AC_PROG_CXX:选择 C++ 编译器。
AC_CHECK_LIB(pthread, pthread_kill)
libpthread 在/usr/lib中,AC_CHECK_LIB能检测得到,显示
checking for pthread_kill in -lpthread... yes
#aclocal
#autoheader
#libtoolize --force --copy
#automake -a
#autoconf
6. 使用pkg-config工具告诉调用者“头文件和库在哪,还依赖哪些模块”
#cp /usr/lib/pkgconfig/gtk-engines-2.pc base.pc.in
修改base.pc.in如下:
prefix=@prefix@ #修改, 等到安装时./configure --prefix=... 就可以指定这个变量值 exec_prefix=${prefix} #修改 libdir=${prefix}/lib #修改,库文件安装在此 includedir=${prefix}/include #修改,头文件安装在此 Name: gtk-engines-2 Description: GTK+ Theme Engines Version: 2.8.0 Requires: gtk+-2.0 Libs: -L${libdir} -lbase #这个函数库的库文件可以在${libdir}中找到 Cflags: -I${includedir}/base #这个函数库的头文件可以在${includedir}/base中找到
三、安装这个函数库
#mkdir /home/hadoop/usr
#cd /home/hadoop/base
#./configure --prefix=$HOME/usr
#make
#make install
#make dist
四、应用工程中使用一个第三方函数库
1. 创建工程结构和源码
#pwd
/home/hadoop
#mkdir appdemo
#mkdir appdemo/src
#vi appdemo/src/main.c
#include <dlist.h> int main(int argc, char* argv[]){ DList* dlist=dlist_create(NULL, NULL); dlist_destroy(dlist); return 0; }
2. 创建appdemo/Makefile.am和appdemo/src/Makefile.am
appdemo/Makefile.am
SUBDIRS=src
appdemo/src/Makefile.am
bin_PROGRAMS=appdemo appdemo_SOURCES=main.c appdemo_CFLAGS=@BASE_CFLAGS@ appdemo_LDFLAGS=@BASE_LIBS@
3. 创建configure.in
# autoscan
#mv configure.scan configure.in
修改configure如下:
# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT(appdemo, 0.1, chuanwang66@163.com) #修改 AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE(appdemo, 0.1) #修改 # Checks for programs. AC_PROG_CC # Checks for libraries. # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT PKG_CHECK_MODULES(BASE, ["base"]) #新增,调用pkg-config检查软件包base(生成BASE_CFLAGS和BASE_LIBS两个变量) AC_SUBST(BASE_CFLAGS) #新增,将Makefile.am中的BASE_CFLAGS替换成上面检查出来的值 AC_SUBST(BASE_LIBS) #新增,将Makefile.am中的BASE_LIBS替换成上面检查出来的值
4. 傻乎乎一阵命令
#aclocal
#autoheader
5. 创建 NEWS、README、AUTHORS、ChangeLog
6.
#automake -a
#autoconf
#export PKG_CONFIG_PATH=/home/hadoop/usr/lib/pkgconfig
#./configure --prefix=/home/hadoop/usr
#make ???可以我在这里出错了,为何呢
#make install
#make dist