一. libtool的工作原理 libtool 是一个通用库支持脚本,将使用动态库的复杂性隐藏在统一、可移植的接口中;使用libtool的标准方法,可以在不同平台上创建并调用动态库。可以认为libtool是gcc的一个抽象,其包装了gcc(或者其他的编译器),用户无需知道细节,只要告诉libtool需要编译哪些库即可,libtool将处理库的依赖等细节。libtool只与后缀名为lo、la的libtool文件打交道。 libtool主要的一个作用是在编译大型软件的过程中解决了库的依赖问题;将繁重的库依赖关系的维护工作承担下来,从而释放了程序员的人力资源。libtool提供统一的接口,隐藏了不同平台间库的名称的差异等细节,生成一个抽象的后缀名为la高层库libxx.la(其实是个文本文件),并将该库对其它库的依赖关系,都写在该la的文件中。该文件中的dependency_libs记录该库依赖的所有库(其中有些是以.la文件的形式加入的);libdir则指出了库的安装位置;library_names记录了共享库的名字;old_library记录了静态库的名字。 当编译过程到link阶段的时候,如果有下面的命令: $libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la libtool会到/usr/lib路径下去寻找liba.la,然后从中读取实际的共享库的名字(library_names中记录了该名字,比如liba.so)和路径(lib_dir中记录了,比如libdir=’/usr/lib’),返回诸如/usr/lib/liba.so的参数给激发出的gcc命令行。 如果liba.so依赖于库/usr/lib/libb.so,则在liba.la中将会有dependency_libs=’-L/usr/lib -lb’或者dependency_libs=’/usr/lib/libb.la’的行,如果是前者,其将直接把“-L/usr/lib –lb”当作参数传给gcc命令行;如果是后者,libtool将从/usr/lib/libb.la中读取实际的libb.so的库名称和路径,然后组合成参数“/usr/lib/libb.so”传递给gcc命令行。 当要生成的文件是诸如libmylib.la的时候,比如: $libtool --mode=link gcc -o libmylib.la -rpath /usr/lib –L/usr/lib –la 其依赖的库的搜索基本类似,只是在这个时候会根据相应的规则生成相应的共享库和静态库。 注意:libtool在链接的时候只会涉及到后缀名为la的libtool文件;实际的库文件名称和库安装路径以及依赖关系是从该文件中读取的。 2 为何使用 -Wl,--rpath-link -Wl,DIR? 使用libtool解决编译问题看上去没什么问题:库的名称、路径、依赖都得到了很好的解决。但下结论不要那么着急,一个显而易见的问题就是:并不是所有的库都是用libtool编译的。
libtool常见于autoconf/automake,单独用的例子很少。 二. libtool的使用 1.Creating object files # libtool --mode=compile gcc -g -O -c foo.c gcc -g -O -c foo.c -fPIC -DPIC -o.libs/foo.o gcc -g -O -c foo.c -o foo.o >/dev/null2>&1 # libtool --mode=compile gcc -g -O -c hello.c gcc -g -O -c hello.c -fPIC -DPIC -o.libs/hello.o gcc -g -O -c hello.c -o hello.o>/dev/null 2>&1 【说明】libtool编译出两个版本的relocatableobject,一个是fPIC(位置无关的),放在.libs目录下;另一个则是普通的,放在本地。 2.linking shared library # libtool --mode=link --tag=CC gcc -g -O -olibhello.la -rpath /usr/local/lib foo.lo rm -fr .libs/libhello.a.libs/libhello.la .libs/libhello.lai .libs/libhello.so libs/libhello.so.0.libs/libhello.so.0.0.0 gcc -shared .libs/foo.o -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0 (cd .libs && rm -f libhello.so.0&& ln -s libhello.so.0.0.0 libhello.so.0) (cd .libs && rm -f libhello.so &&ln -s libhello.so.0.0.0 libhello.so) ar cru .libs/libhello.a foo.o ranlib .libs/libhello.a creating libhello.la (cd .libs && rm -f libhello.la&& ln -s ../libhello.la libhello.la) 【说明】link出两个共享库,一个是static,一个则是dynamic;需要注意的是,-rpath必须有才能产生dynamic库来,如果用-static,则只创建static库。 ranlib的作用: On some older UNIX systems, ranlib added a tableof contents to archive libraries, which converted each archive to a form thatcould be linked more rapidly. This is no longer needed as the ar commandautomatically provides all the functionality ranlib used to provide. 在一些旧版本的系统上,ranlib负责把静态库转换为其他的某种格式,使得新的库能够更快的链接;现在ar命令已经包含了上述功能; This command is provided as a convenience forsoftware developers who need to maintain Makefiles that are portable across avariety of operating systems. 为了兼容性,在makefile中还是保留ranlib 3.install shared library libtool --mode=install cp libhello.la/usr/local/lib/libhello.la libtool --mode=install install -c libhello.la/usr/local/lib/libhello.la 两个命令都可以,效果相同 4.linking executable file # libtool --mode=link gcc -g -O -o hello hello.lo-rpath /usr/local/lib libhello.la gcc -g -O -o .libs/hello .libs/hello.o ./.libs/libhello.so creating hello -rpath项负责添加运行时库路径,否则只能手工修改LD_LIBRARY_PATH环境变量了。 验证一下: # ldd .libs/hello linux-gate.so.1 => (0xffffe000) libhello.so.0 => /usr/local/lib/libhello.so.0 (0x40019000) libc.so.6 => /lib/tls/libc.so.6 (0x40031000) /lib/ld-linux.so.2 (0x40000000) 5.install executablefile #libtool --mode=install cp hello /usr/local/bin/hello 安装可执行程序。 6.运行 libtool --mode=execute hello 或直接运行hello 注意:此处hello已经安装在/usr/local/bin下了,可以用which hello来查看 【附】源码 foo.c #include <stdio.h> char msg[128]="Hello world"; void print() { printf("%s/n",msg); } hello.c: #include <stdio.h> extern char msg[128]; extern void print(); int main() { print(); } Makefile: LO_OBJS = foo.lo PACKAGE_VERSION = 1:1:1 LIBDIR=/usr/local/lib BINDIR=/usr/local/bin all : hello install : libhello.la hello libtool --mode=install install -clibhello.la ${LIBDIR}/libhello.la libtool --mode=install cp hello${BINDIR}/hello uninstall : ${LIBDIR}/libhello.la ${BINDIR}/hello libtool --mode=uninstall /bin/rm${LIBDIR}/libhello.la libtool --mode=uninstall /bin/rm${BINDIR}/hello hello : libhello.la hello.o libtool --mode=install install -clibhello.la ${LIBDIR}/libhello.la libtool --mode=link gcc -g -O -o hellohello.o -rpath ${LIBDIR} libhello.la libhello.la : $(LO_OBJS) libtool --mode=link --tag=CCgcc -g -O -o libhello.la $(LO_OBJS) -rpath ${LIBDIR} ${PACKAGE_VERSION} foo.lo : foo.c libtool --mode=compile gcc -g -O -cfoo.c hello.lo : hello.c libtool --mode=compile gcc -g -O -chello.c clean : rm -f lib*.a *~ *core *.lo *.o *.la hello rm -rf .libs 这样,用户可以用make编译,makeinstall/uninstall安装/卸载,makeclean清除编译临时文件,安装成功后,可以直接执行hello,不必指明路径也不必再另设环境变量LD_LIBRARY_PATH,非常方便!
lo: 使用libtool编译出的目标文件,其实就是在o文件中添加了一些信息 la: 使用libtool编译出的库文件,其实是个文本文件,记录同名动态库和静态库的相关信息
o: 编译的目标文件 a: 静态库,其实就是把若干o文件打了个包 so: 动态链接库(共享库)