轉自 https://www.ibm.com/developerworks/cn/aix/library/1007_wuxh_libtool/
介紹
在不同的系統中建立動態鏈接庫的方法有很大的差別,這主要是因爲每個系統對動態鏈接庫的看法和實現並不相同,以及編譯器對動態鏈接庫支持的選項也不太一樣。對於開發人員,如果嘗試將使用動態庫的軟件在這些系統之間移植,需要參考枯澀難懂的系統手冊,以及修改相應的 Makefile,這一工作是乏味的,並且具有一定的難度。
使用 GNU Libtool 可以容易的在不同的系統中建立動態鏈接庫。它通過一個稱爲 Libtool 庫的抽象,隱藏了不同系統之間的差異,給開發人員提供了一致的的接口。對於大部分情況,開發人員甚至不用去查看相應的系統手冊,只需要掌握 GNU Libtool 的用法就可以了。並且,使用 Libtool 的 Makefile 也只需要編寫一次就可以在多個系統上使用。
Libtool 庫可以是一個靜態鏈接庫,可以是一個動態鏈接庫,也可以同時包含兩者。在這篇文檔中,我們圍繞 Libtool 庫的建立和使用,只是在適當的說明 Libtool 庫和系統動態或者靜態鏈接庫之間的映射關係。
Libtool 是一個工具
雖然 Libtool 隱藏了在不同平臺創建鏈接庫的複雜性,但其最終還是需要底層系統對鏈接庫的支持,它不能超越系統的限制,例如,Libtool 並不能在不支持動態鏈接庫的系統中創建出動態鏈接庫。
Libtool 基本用法
這一節以實例來說明如何使用 Libtool 從源代碼創建最終鏈接庫以及執行程序的完整步驟,這是軟件開發過程中經常使用的內容,包括 :
- 創建 Libtool 對象文件 ;
- 創建 Libtool 庫;
- 安裝 Libtool 庫 ;
- 使用 Libtool 庫 ;
- 卸載 Libtool 庫 ;
首先需要準備一個源文件 compress.c,代碼如下:
清單 1: compress.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #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 庫。
建立 Libtool 對象文件
如果使用傳統的方式,建立對象文件通常使用下面的命令 :
1 | $ gcc -c compress.c |
使用 Libtool 則使用下面的命令 :
1 | $ libtool --mode=compile gcc -c foo.c |
可以看到,使用 Libtool 只需要將“傳統”的命令 (gcc -c foo.c) 作爲參數傳遞給 Libtool 即可。
在上面的命令中,libtool 使用 compile模式 (--mode=compile 選項 ),這是建立對象文件的模式,Libtool 還有其它的模式,後面將介紹。
上面的命令輸出如下 :
1 2 3 | 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。
建立 Libtool 庫
用下面的命令建立 Libtool 庫 :
1 | $ 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 會記住這個依賴關係,後續在使用我們的庫時自動的將依賴的庫鏈接進來。
上面的命令輸出如下 :
1 2 3 4 5 6 7 8 9 10 11 | 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 庫的可移植性。
安裝 Libtool 庫
如果打算髮布建立好的 Libtool 庫,可以使用下面的命令安裝它 :
1 | $ libtool --mode=install install -c libcompress.la /tmp |
我們需要告訴 Libtool 使用的安裝命令,Libtool 支持 install 和 cp,這裏使用的是 install。
雖然前面我們在建立庫時,通過 -rpath 選項指定了庫準備安裝的路徑 (/tmp),但是這裏我們還得要提供安裝路徑。請確保它們一致。
這個命令的輸出如下 :
1 2 3 4 5 6 7 8 9 10 11 | 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 模式可以在這方面給我們一些提示 :
1 | $ libtool -n --mode=finish /tmp |
這個命令的輸出有點長,所以不在這裏列出,如果不能正常的使用安裝好的庫,請運行這個命令。
使用 Libtool 庫
要在應用程序中使用前面創建的 Libtool 庫很簡單,準備一個源文件 main.c,它將使用 libcompress.la 庫中定義的函數,代碼如下 :
清單 2: main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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 對象文件,這和前面的方法一樣 :
1 | $ libtool --mode=compile gcc -c main.c |
使用安裝的庫
1 2 | 然後使用下面的命令鏈接執行文件 : $ libtool --mode=link gcc -o main main.lo /tmp/libcompress.la |
我們也可以直接使用 libcompress.a 或者 libcompress.so,但是使用 Libtool 更加簡單,因爲它會將幫助你解決依賴關係,例如我們的 libcompress 依賴 libz。
上面命令的輸出如下 :
1 2 | gcc -o main .libs/main.o /tmp/libcompress.so -lz -Wl,--rpath -Wl,/tmp -Wl,--rpath -Wl,/tmp |
這裏,Libtool 自動選擇鏈接動態鏈接庫,並且加上了運行時需要的 --rpath 選項,以及依賴的庫 -lz。
如果要使用靜態鏈接庫,只需要加上 -static-libtool-libs選項即可,如下 :
1 | $ libtool --mode=link gcc -o main main.lo /tmp/libcompress.la -static-libtool-libs |
這個命令的輸出如下 :
1 | gcc -o main .libs/main.o /tmp/libcompress.a -lz |
使用未安裝的庫
也可以使用還沒有安裝的庫,這和使用安裝好的庫幾乎相同,只是指定的輸入文件位置不一樣,假如我們在同一個目錄中開發 compress.c 和 main.c,那麼使用下面的命令 :
1 | $ libtool --mode=link gcc -o main main.lo ./libcompress.la |
和使用安裝的庫不一樣,這個時候建立的 main 程序只是一個封裝腳本,如果你直接執行它不會有什麼問題,但是如果你想調試它,例如 :
1 | $ gdb main |
gdb 會報怨 main 不是可執行格式,不能接受。這個時候我們需要使用 Libtool 的執行模式,使用下面的命令調試程序 :
1 | $ libtool --mode=execute gdb main |
卸載 Libtool 庫
使用下面的命令可以卸載安裝的庫 :
1 | $ libtool --mode=uninstall rm /tmp/libcompress.la |
這個命令的輸出如下 :
1 2 | rm /tmp/libcompress.la /tmp/libcompress.so.0.0.0 /tmp/libcompress.so.0 /tmp/libcompress.so /tmp/libcompress.a |
這將刪除所有安裝的庫文件。
Libtool 高級用法
這一節將對 Libtool 進行更加全面的描述,包括下面的內容 :
- 創建可動態加載模塊 ;
- 禁止創建動態或者靜態鏈接庫 ;
- Libtool 命令模式 ;
- 庫版本信息 ;
創建可動態加載模塊
有些高級的軟件系統在建立時不需要與特定的庫鏈接,而在運行時可以動態加載符合規範的庫,來提供額外的功能,這通常稱爲插件系統。
可動態加載的庫和通常的庫有一些區別,它們可以通過 dlopen() 打開,並且可以通過 dlsym() 查詢它輸出的符號。
使用 Libtool 可以很容易的建立這樣的庫,還是以前面的 compress.c 爲例,我們可以通過這樣的命令建立一個可動態加載模塊 :
1 2 | $ libtool --mode=link gcc -o compress.la compress.lo -rpath /tmp -lz -module -avoid-version |
這裏添加了額外的兩個參數,-module告訴 Libtool 建立一個可動態加載的模塊,-avoid-version告訴 Libtool 不要添加版本號。
在 UNIX/Linux 系統中,庫通常以 lib 作爲前綴,可在上面我們指定的輸出文件爲 compress.la而不是 libcompress.la,這也是可動態加載模塊的一個特徵,它不需要遵循通常的庫命名規則。
在實際應用中,可動態加載模塊通常會使用主程序提供的一些函數,爲了滿足動態模塊的需求,在編譯主程序時,需要添加 -export-dynamic選項。
禁止創建動態或者靜態鏈接庫
大部分情況下,Libtool 都配置成同時創建動態鏈接庫和靜態鏈接庫。可以通過下面的命令查看 Libtool 的當前配置 :
1 | $ libtool – features |
它的輸出如下 :
1 2 3 | host: i686-pc-linux-gnu enable shared libraries enable static libraries |
可是有時侯,只想創建動態鏈接庫或者只想創建靜態鏈接庫,這需要修改 Libtool 自身。後面介紹 Libtool 結合 Autoconf 和 Automake 使用時,將有更加簡單的辦法。
禁止創建動態鏈接庫
Libtool 自身是一個安裝在 /usr/bin 目錄下的 Shell 腳本,爲了修改它,我們需要將它複製到一個有修改權限的目錄,然後找到下面的兩行 :
1 2 | 52 # Whether or not to build shared libraries. 53 build_libtool_libs=yes |
將 yes 改爲 no。之後用這個修改過的 libtool 就不會創建動態鏈接庫。
還有其它幾個方法可以禁止創建動態鏈接庫,第一個方法是在鏈接時不提供 -rpath 選項,第二個方法是在鏈接時使用 -all-static選項,第三個方法是指定目標文件爲 libcompress.a 而不是 libcompress.la。
禁止創建靜態鏈接庫
在 Libtool 腳本中找到下面兩行
1 2 | 55 # Whether or not to build static libraries. 56 build_old_libs=no |
將 yes 改爲 no。之後用這個修改過的 libtool 就不會創建靜態鏈接庫。
Libtool 命令模式
在前面,我們已經用到了 Libtool 的大部分命令模式,每個命令模式用於不同的階段,Libtool 根據當前的命令模式添加需要的編譯器選項。
Libtool 支持下面的幾個命令模式 :
- 編譯模式 ;
- 連接模式 ;
- 安裝模式 ;
- 完成模式 ;
- 卸載模式 ;
- 執行模式 ;
- 清除模式 ;
每個命令模式對應開發中的不同階段,但並不是在每個項目中都需要使用上面所有的模式,例如一個不需要安裝的庫就不需要安裝和卸載模式。
編譯模式
編譯模式用於建立從源文件建立對象文件。它需要一個編譯器名稱作爲第一個參數,並且還要提供 -c 選項,Libtool 將根據源文件名稱自動選擇目標文件名稱。如果是建立動態鏈接庫,它也會加入相應的選項,例如 -fPIC等等。
編譯模式使用示例 :
1 | $ libtool --mode=compile gcc -c src.c |
鏈接模式
鏈接模式用於建立 Libtool 庫或者可執行文件,如果輸出文件名稱以 .la 結尾,那麼它將嘗試建立 Libtool 庫。如果輸出文件名稱以 .a 結尾,它就只建立靜態鏈接庫。如果輸出文件名稱以 .lo 或者 .o 結尾,則建立一個可重新加載的對象文件,這經常叫做部分鏈接。否則它就建立一個執行文件。
鏈接模式使用示例 :
1 | $ libtool --mode=link gcc -o library.la src.lo -rpath /usr/local/lib |
安裝模式
安裝模式用於安裝 Libtool 庫或者執行程序。它的第一個參數必須是 install 或者 cp,之後是要安裝的文件以及目標路徑。
安裝模式使用示例 :
1 | $ libtool --mode=install install -c library.la /usr/local/lib |
完成模式
完成模式是在安裝完 Libtool 庫之後,在使用之前進行適當的配置。finish 模式需要一個參數,即 Libtool 庫的安裝路徑。
完成模式使用示例 :
1 | $ libtool --mode=finish /usr/local/lib |
卸載模式
卸載模式用於卸載已經安裝的 Libtool 庫或者執行程序。
卸載模式使用示例 :
1 | $ libtool --mode=uninstall rm /usr/local/lib/library.la |
執行模式
執行模式用來執行應用程序,它在啓動應用程序之前自動的設置好庫的路徑。
執行模式使用示例 :
1 | $ libtool --mode=execute gdb program |
清除模式
清除模式和卸載模式差不多,只是它用來清除開發過程中的中間文件。
清除模式使用示例 :
1 | $ libtool --mode=clean rm library.la |
庫版本信息
和應用程序一樣,庫也需要不斷的升級,考慮一個第三方應用程序使用了我們之前發佈的 libcompress。現在我們對 libcompress 的性能進行了優化,並且提供了新的接口,所以我們又發佈了這個新版本。
這引入了幾個問題,如果以前的應用程序採用靜態方式鏈接,那麼如果它想使用新庫的功能,就必須用新庫重新鏈接應用程序。如果是採用動態鏈接方式,那麼新庫安裝後,應用程序應該使用新庫還是舊庫呢?並且,如何避免新庫和舊庫之間的衝突呢?
庫版本號可以解決上述這些問題,一個動態鏈接庫有一個版本號,它在鏈接時硬編碼到動態鏈接庫中,當一個應用程序鏈接動態鏈接庫時,它也存儲了鏈接庫的版本信息,動態加載器 ( 如 ld-linux.so.2) 可以在程序啓動時正確的加載版本匹配的庫。
用 ldd命令可以查看應用程序使用的動態鏈接庫以及它們的版本信息 :
1 2 3 4 5 6 | $ ldd .libs/lt-main linux-gate.so.1 => (0x00182000) libcompress.so.0 => /home/.../.libs/libcompress.so.0 (0x00c25000) libz.so.1 => /lib/libz.so.1 (0x00565000) libc.so.6 => /lib/libc.so.6 (0x003ad000) /lib/ld-linux.so.2 (0x0038d000) |
我們需要用 .libs/lt-main 作爲輸入文件,當前目錄下的 main 只是一個封裝腳本。從上面的輸出可以看到,main 程序依賴 libcompress.so 的版本 0。
庫接口
庫接口是 應用程序可以和庫交流的入口,常用的庫接口包括 :
- 全局變量、結構體、常數 ;
- 全局函數,包括參數類型、數量和返回類型 ;
- socket、管道,以及其它進程間通訊的協議格式 ;
當然還有其它的接口。在設計和實現庫時,應該考慮儘量在將來減少庫接口的改變。
庫版本號
通常一個庫有兩個版本號,一個主版本號,一個次版本號,主版本號指示接口的改變,次版本號指示性能增強或者錯誤修復。但不是每個系統都是如此。
在前面的例子中我們可以看到,爲動態鏈接庫 libcompress.so.0.0.0 建立了兩個符號鏈接 :
- libcompress.so -> libcompress.so.0.0.0
- libcompress.so.0 -> libcompress.so.0.0.0
其中 libcompress.so 是供鏈接器 ( 例如 ld) 使用的,它應該指向當前系統中 libcompress 的最新版本,這樣新程序總是可以鏈接最新的庫版本。
libcompress.so.0 是供動態加載器 ( 例如 ld-linux.so.2) 使用的,它應該指向當前系統中相同接口號 ( 這裏是 0) 的最新版本,這樣動態鏈接器就可以加載相同接口最新的庫版本。
Libtool 庫版本號
每個系統的庫版本機制並不一樣,Libtool 通過一種抽象的版本機制,最終在創建庫時映射到具體的系統版本機制。
Libtool 的版本號分爲 3 個部分 :
- current: 表示當前庫輸出的接口的數量 ;
- revision: 表示當前庫輸出接口的修改次數 ;
- age: 表示當前庫支持先前的庫接口的數量,例如 age爲 2,表示它可以和支持當前庫接口的執行文件,或者支持前面兩個庫接口的執行文件進行鏈接。所以 age應該總是小於或者等於 current。
Libtool 的庫版本通過參數 -version-infocurrent:revision:age指定,例如下面的例子 :
1 | $ libtool --mode=link gcc -l libcompress.la -version-info 0:1:0 |
如果沒有指定,默認版本是 0.0.0。
注意,應該儘可能少的更新庫版本號,尤其是不能強行將庫版本號和軟件發行號保持一致,下面是更新庫版本號的幾個策略 :
- 如果修改了庫的源代碼,那麼應該增加 revision。這是當前接口的新的修訂版本。
- 如果改變了接口,應該增加 current,將 revision重置爲 0。這是接口的新版本。
- 如果新接口是前面接口的超集 ( 前面的接口還是可用 ),那麼應該增加 age。這是一個向後兼容的版本。
- 如果新接口刪除了前面接口的元素,那麼應該將 age重置爲 0。這是一個新的,但是不向後兼容的版本。
避免版本信息
有些動態鏈接庫,例如可動態加載模塊,不需要版本號,這時可使用 Libtool 的 -avoid-version選項,例如下面的命令 :
1 | $ libtool --mode=link gcc -o libcompress.la compress.lo -rpath /tmp -avoid-version |
將只會創建一個 .so 結尾的動態鏈接庫,而沒有 .0.0.0 這樣的版本後綴。
結合 Autoconf 和 Automake 使用 Libtool
在使用 Autoconf 和 Automake 的項目中使用 Libtool 更加容易,只需要添加或者修改幾個地方,後續由 Automake 來幫你正確的調用 Libtool。
和 Autoconf 和 Automake 一樣,當在其它主機上編譯發佈的軟件包時,不需要安裝 Libtool。
我們以前面的 compress.c 文件爲例,介紹如何將它轉換成一個 Autoconf/Automake/Libtool 項目。
建立 configure.ac
使用下面的命令建立一個 configure.ac 模板 :
1 | $ autoscan |
這將生成一個 configure.scan 文件,將它改名爲 configure.ac。
在 AC_INIT() 之後加入下面幾行 :
1 2 3 4 | # 初始話 Automake AM_INIT_AUTOMAKE([-Wall]) # 這是在 Autoconf 中使用 Libtool 唯一必須的宏 AC_PROG_LIBTOOL |
在 AC_OUTPUT 之前加入幾行 :
1 2 | # 告訴 Autoconf 通過 Makefile.in 自動生成 Makefile AC_CONFIG_FILES([Makefile]) |
建立 Makefile.am
建立一個 Makefile.am 文件,內容如下 :
1 2 3 4 5 6 7 | # _LTLIBRARIES 是 Automake 支持 Libtool 的原語 lib_LTLIBRARIES = libcompress.la libcompress_la_SOURCES = compress.c # 可以通過 _LDFLAGS 傳遞選項給 Libtool libcompress_la_LDFLAGS = # 通過 _LIBADD 可以指定庫依賴關係 libcompress_la_LIBADD = -lz |
注意上面用 lib_LTLIBRARIES,而不是 lib_LIBRARIES,這告訴 Automake 使用 Libtool 創建 Libtool 庫。
建立 configure 和 Makefile
用下面的命令建立幾個空文件 :
1 | $ touch NEWS README AUTHORS ChangeLog |
然後運行 :
1 2 3 | $ autoreconf -i -s 這將建立 configure 腳本,運行它將得到 Makefile: $ ./configure |
同時,configure 也建立了 libtool 腳本,後續 Automake 將使用這個 libtool 腳本,而不是系統的那個。
建立 Libtool 庫
現在已經有了 Makefile,我們只需要簡單的輸入 :
1 | $ make |
便可以創建 libcompress 了,這比手動調用 Libtool 要方便很多。
注意 Automake 自動爲 Libtool 選擇了 -rpath 的路徑,這是跟隨 UNIX 系統習慣定義的,庫文件安裝到 $prefix/lib 目錄中,頭文件安裝到 $prefix/include 目錄中。我們可以通過 configure 腳本的 --prefix選項改變上面的 $prefix,也可以使用 configure 腳本的 --libdir明確的指定庫文件的安裝目錄。
靜態庫和動態庫
前面在 configure.ac 中的 AC_PROG_LIBTOOL 宏爲 configure 腳本添加了兩個選項 :
- --enable-static
- --enable-shared
這兩個選項可以控制是否建立動態或者靜態鏈接庫,例如,如果只想建立動態鏈接庫,可以這樣運行 configure:
1 | $ ./configure --enable-shared – disable-static |
在開發過程中,禁止創建動態鏈接庫有幾個優勢 :
- 編譯速度提高了,這可以節省時間 ;
- 調試更加容易,因爲不用處理任何動態鏈接庫引入的複雜性 ;
- 你可以瞭解 Libtool 在只支持靜態鏈接庫的平臺的行爲 ;
爲了避免在 configure 時忘記 --disable-shared選項,你可以在 configure.ac 中 AC_PROG_LIBTOOL 之前加入一行 :
1 | AC_DISABLE_SHARED |
可動態加載模塊
Libtool 的 鏈接模式支持 -module選項,它用來建立一個可動態加載模塊,可以通過 Automake 將這個選項傳遞給 Libtool。只需要選項添加到 Makefile.am 中的 libcompress_la_LDFLAGS變量即可,所以,要建立可動態加載模塊,我們需要修改 Makefile.am:
1 | libcompress_la_LDFLAGS = -module -avoid-version |
修改 Makefile.am 之後,需要運行 Automake:
1 | $ automake |
這將重新生成 Makefile.in 文件,以至於 Makefile。
安裝 Libtool 庫
安裝 Libtool 庫非常的簡單,只需要運行 :
1 | $ make install |
卸載 Libtool 庫
和安裝 Libtool 庫同樣簡單 :
1 | $ make uninstall |
建立執行程序
通過 Automake 使用 Libtool 庫也非常容易,我們需要在 Makefile.am 中加入下面的幾行 :
1 2 3 4 | bin_PROGRAMS = main main_SOURCES = main.c main_LDFLAGS = main_LDADD = libcompress.la |
注意在建立 libcompress.la 是,我們通過 _LIBADD 指定依賴庫,而建立執行文件 main 時,我們通過 _LDADD 指定依賴庫,要記住這點區別。
也記得把前面爲測試可動態加載模塊時修改的 libcompress_la_LDFLAGS 變量改回來 :
1 | libcompress_la_LDFLAGS = |
修改 Makefile.am 之後,需要運行 Automake 更新 Makefile.in:
1 | $ automake |
然後運行
1 | $ make |
就可以建立可執行程序 main。
調試執行程序
在結合 Autoconf 和 Automake 使用 Libtool 時,我們幾乎永遠都不會直接調用 Libtool,除了一個例外,那就是 Libtool 的執行模式。
例如我們在開發時要調試執行程序,可以使用下面的命令 :
1 | $ libtool --mode=execute gdb main |
如果直接使用 :
1 | $ gdb main |
gdb 會抱怨 main 的格式不可接受,因爲使用 Libtool 建立的 main 只是一個封裝腳本,它最終啓動的是 .lib/lt-main。
小結
本文檔描述了 GNU Libtool 解決的問題,它的工作方式,以及在實際工作中使用 Libtool 的方法。有興趣的讀者應該進一步參考 GNU Libtool 手冊獲得更詳細的信息。在安裝了 GNU Libtool 的系統中可以直接通過 info libtool 來查看手冊。