libtool 創建庫的工具
1. 背景
在不同的系統中建立動態鏈接庫的方法有很大的差別,這主要是因為每個系統對動態鏈接庫的用法和實現並不相同,以及編譯器對動態鏈接庫支持的選項也不太一樣。
對於開發人員,如果嘗試將使用動態庫的軟件在這些系統之間移植,需要參考枯澀難懂的系統手冊,以及修改相應的 Makefile,這一工作是乏味的,並且具有一定的難度。
使用 GNU Libtool 可以容易的在不同的系統中建立動態鏈接庫。它通過一個稱為 Libtool 庫的抽象,隱藏了不同系統之間的差異,給開發人員提供了一致的的接口。對於大部分情況,開發人員甚至不用去查看相應的系統手冊,只需要掌握 GNU Libtool 的用法就可以了。並且,使用 Libtool 的 Makefile 也只需要編寫一次就可以在多個系統上使用。
Libtool 庫可以是一個靜態鏈接庫,可以是一個動態鏈接庫,也可以同時包含兩者。
在這篇文檔中,我們圍繞 Libtool 庫的建立和使用,只是在適當的說明 Libtool 庫和系統動態或者靜態鏈接庫之間的映射關系。
2. Libtool 是一個工具
雖然 Libtool 隱藏了在不同平台創建鏈接庫的復雜性,但其最終還是需要底層系統對鏈接庫的支持,它不能超越系統的限制,例如,Libtool 並不能在不支持動態鏈接庫的系統中創建出動態鏈接庫。
3. Libtool 基本用法
以實例來說明如何使用 Libtool 從源代碼創建最終鏈接庫以及執行程序的完整步驟,這是軟件開發過程中經常使用的內容,包括 :
- 創建 Libtool 對象文件 ;
- 創建 Libtool 庫;
- 安裝 Libtool 庫 ;
- 使用 Libtool 庫 ;
- 卸載 Libtool 庫 ;
首先需要准備一個源文件 compress.c,代碼如下
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <limits.h> #include <assert.h> #include <zlib.h> /* 一個簡單的文件壓縮函數 */ int compress_file (const char *filename) { int src_fd, dest_fd; struct stat sb; Bytef *src, *dest; uLong dest_len; char dest_file[PATH_MAX]; src_fd = open (filename, O_RDONLY); assert (dest_fd != -1); assert (fstat (src_fd, &sb) != -1); src = mmap (NULL, sb.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0); assert (src != MAP_FAILED); dest_len = compressBound (sb.st_size); dest = malloc (dest_len); assert (dest); assert (compress (dest, &dest_len, src, sb.st_size) == Z_OK); munmap (src, sb.st_size); close (src_fd); snprintf (dest_file, sizeof (dest_file), "%s.z", filename); dest_fd = creat (dest_file, S_IRUSR | S_IWUSR); assert (dest_fd != -1); write (dest_fd, dest, dest_len); close (dest_fd); free (dest); return 0; }
這個文件實現了一個函數 compress_file(),它接收一個文件名作為參數,然后對文件進行壓縮,生成一個 .z結尾的壓縮文件。在這個文件中使用了 compress()函數,這個函數是有由 libz 提供的。
從源文件建立 Libtool 庫需要經過兩個步驟,先建立 Libtool 對象文件,再建立 Libtool 庫。
(1) Libtool 對象文件
如果使用傳統的方式,建立對象文件通常使用下面的命令
$ gcc -c compress.c
使用 Libtool 則使用下面的命令 :
$ libtool --mode=compile gcc -c foo.c
可以看到,使用 Libtool 只需要將“傳統”的命令 (gcc -c foo.c) 作為參數傳遞給 Libtool 即可。
在上面的命令中,libtool 使用 compile模式 (--mode=compile 選項 ),這是建立對象文件的模式,Libtool 還有其它的模式,后面將介紹。
上面的命令輸出如下 :
mkdir .libs gcc -c compress.c -fPIC -DPIC -o .libs/compress.o gcc -c compress.c -o compress.o >/dev/null 2>&1
它建立了兩個文件,一個是 .libs/compress.o,在建立這個文件時,Libtool 自動插入了 -fPIC和 -DPIC選項,告訴編譯器生成位置獨立的代碼,之后將用這個文件來建立動態鏈接庫。生成第二個文件 compress
.o沒有添加額外的選項,它准備用來建立靜態鏈接庫。
除了上面的兩個文件之外,Libtool 還建立了一個文件 compress.lo,這個文件就是 Libtool 對象文件,實際上也就是一個文本文件,里面記錄了建立動態鏈接庫和靜態鏈接庫分別所需要的真實文件名稱,后面 Libtool 將使用這個文件而不是直接的使用 .libs/compress.o 和 compress.o。
(2)建立 Libtool 庫
$ libtool --mode=link gcc -o libcompress.la【目標文件】 compress.lo【輸入文件】 -rpath /tmp -lz
注意這里使用 compress.lo 作為輸入文件,並且告訴 Libtool 生成的目標文件為 libcompress.la,.la 是 Libtool 的庫文件后綴。
-rpath選項告訴 Libtool 這個庫將被安裝到什么地方,如果省略了 -rpath選項,那么不會生成動態鏈接庫。
因為我們的庫中使用了 libz 提供的 compress 函數,所以也提供了 -lz 選項,Libtool 會記住這個依賴關系,后續在使用我們的庫時自動的將依賴的庫鏈接進來。
gcc -shared .libs/compress.o -lz -Wl,-soname -Wl,libcompress.so.0 -o .libs/libcompress.so.0.0.0 (cd .libs && rm -f libcompress.so.0 && ln -s libcompress.so.0.0.0 libcompress.so.0) (cd .libs && rm -f libcompress.so && ln -s libcompress.so.0.0.0 libcompress.so) ar cru .libs/libcompress.a compress.o ranlib .libs/libcompress.a creating libcompress.la (cd .libs && rm -f libcompress.la && ln -s ../libcompress.la libcompress.la)
可以看到,Libtool 自動的插入了建立動態鏈接庫需要的編譯選項 -shared。並且,它也建立了靜態鏈接庫 .libs/libcompress.a,后面我們將會介紹如何控制 Libtool 只建立需要的庫
你可能會奇怪為什么建立的動態鏈接庫有 .0 和 .0.0.0 這樣的后綴,這里先不用理會它,后面在介紹 Libtool 庫版本信息時將會解釋這點。
值得注意的是,Libtool 希望后續使用 libcompress.la 文件而不是直接使用 libcompress.a 和 libcompress.so 文件,如果你這樣做,雖然可以,但會破壞 Libtool 庫的可移植性。
4. 安裝 Libtool 庫
發布建立好的 Libtool 庫,可以使用下面的命令安裝它 :
$ libtool --mode=install install -c libcompress.la /tmp
我們需要告訴 Libtool 使用的安裝命令,Libtool 支持 install 和 cp,這里使用的是 install。
雖然前面我們在建立庫時,通過 -rpath 選項指定了庫准備安裝的路徑 (/tmp),但是這里我們還得要提供安裝路徑。請確保它們一致。
這個命令的輸出如下 :
install .libs/libcompress.so.0.0.0 /tmp/libcompress.so.0.0.0 (cd /tmp && { ln -s -f libcompress.so.0.0.0 libcompress.so.0 || { rm -f libcompress.so.0 && ln -s libcompress.so.0.0.0 libcompress.so.0; }; }) (cd /tmp && { ln -s -f libcompress.so.0.0.0 libcompress.so || { rm -f libcompress.so && ln -s libcompress.so.0.0.0 libcompress.so; }; }) install .libs/libcompress.lai /tmp/libcompress.la install .libs/libcompress.a /tmp/libcompress.a chmod 644 /tmp/libcompress.a ranlib /tmp/libcompress.a ...
可以看到它安裝了真實的動態鏈接庫和靜態鏈接庫,同時也安裝了 Libtool 庫文件 libcompress.la,這個文件可以被后續的 Libtool 命令使用。
在安裝完成之后,可能還需要做一些配置才能正確使用,Libtool 的 finish 模式可以在這方面給我們一些提示 :
$ libtool -n --mode=finish /tmp
這個命令的輸出有點長,所以不在這里列出,如果不能正常的使用安裝好的庫,請運行這個命令。
5.使用 Libtool 庫
要在應用程序中使用前面創建的 Libtool 庫很簡單,准備一個源文件 main.c,它將使用 libcompress.la 庫中定義的函數,代碼如下 :
清單 2: main.c
#include <stdio.h> extern int compress_file (const char *filename); int main (int argc, char *argv[]) { if (argc < 2) { printf ("usage : %s file\n", argv[0]); return 1; } return compress_file (argv[1]); }
我們還是要先為 main.c 建立 Libtool 對象文件,這和前面的方法一樣 :
$ libtool --mode=compile gcc -c main.c
。。。。。
參考博客:
https://www.ibm.com/developerworks/cn/aix/library/1007_wuxh_libtool/index.html