之前討論的autotools 中,偷懶沒去看libtool。後來想到手上參考的資料沒有使用autotool產生shared library的方式。想要補充後發現和libtool有關。看來出來混還是要還的。
目錄
概論
autotool想要處理不同平台之間的移植性問題。而shared library部份的問題是:
不同平台會有不同格式,舉例來說,在Linux下面的shared library的副檔名是so;而在Windows平台上面會是dll的副檔名。
因為平台不同,shared library可能功能相同但是API稍有不同的
Header file可能名稱不同
…
處理這些問題,應用程式可能會寫了一堆很噁心的#ifdef
來呼叫不同平台處理不同的shared library的API。這樣一來不但損害程式碼可讀性,也提高的程式碼的維護成本。所以GNU提出了libtool來解決這樣的問題。
從這邊 可以看到,GNU libtool使用下面的方式來處理平台移植性的中shared library的問題。
用libtool這個script去封裝平台相依性以及提供存取介面
使用者透過libtool這個script存取封裝的資料
因為libtool提供了和平台無關的share library統一介面。Shared library的使用者可以透過autotools安心使用shared library;而Shared library開發者可以安心的使用libtool產生不同平台使用的shared library。
libtool --help
可以看到有--mode
參數。他的合法選項為
clean
compile
execute
finish
install
link
uninstall
範例
測試程式碼
範例程式細節在這邊 ,檔案各別重新分配到src
, include
, libs
這三個目錄。不想看code只要知道每個檔案都有參考到某個自訂的header file就好了。重點在Makefile 如何使用libtool的部份。
測試程式樹狀架構 1
2
3
4
5
6
7
8
9
10
11
├── include
│ ├── liba.h
│ └── libb.h
├── libs
│ ├── liba.c
│ ├── libb.c
│ └── Makefile
├── Makefile
└── src
├── Makefile
└── test.c
測試Makefile
主要demo libtool的部份在這邊。 ./Makefile 主要只是按照指定目錄進入編譯或清除
./Makefile 1
2
3
4
5
6
7
8
9
10
COMPILE_DIRS = libs src
INC_DIR = $( shell pwd ) /include
.PHONY : all
all :
for i in $( COMPILE_DIRS) ; do make -C $$ i INC_DIR = $( INC_DIR) ; done
clean :
for i in $( COMPILE_DIRS) ; do make -C $$ i INC_DIR = $( INC_DIR) clean; done
使用libtool編譯函式庫
libs/Makefile大概描述如下
產生兩個libraries, liba.a(靜態函式庫)和libb.so(動態函式庫)
編譯時使用–mode=compile作為參數
連結時使用–mode=link作為參數,都是指定gcc產生*.la檔案
靜態和動態的參數差別為是否有指定runtime elf搜尋路徑-rpath
-rpath
加入後libtools會產生 動態和靜態兩種函式庫 ,不加看起來只有產生靜態函式庫
libs/Makefile 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LIBTOOL = libtool
SRCS = liba.c libb.c
OBJS = $( patsubst %.c, %.o, $( SRCS))
CFLAGS = -I$( INC_DIR)
CC = gcc
TARGET = liba.la libb.la
all : $( OBJS ) $( TARGET )
liba.la : liba .lo
$( LIBTOOL) --mode= link $( CC) $( CFLAGS) -o $@ $^
libb.la : libb .lo
$( LIBTOOL) --mode= link $( CC) $( CFLAGS) -o $@ $^ -rpath /usr/local/lib
%.o : %.c
$( LIBTOOL) --mode= compile $( CC) $( CFLAGS) -c $^
clean :
rm -rf $( OBJS) .libs *.lo $( TARGET) *.a
先來看編譯程式碼的輸出
libs/Makefile 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
libtool --mode = compile gcc -I../include -c liba.c
libtool : compile : gcc -I ../include -c liba .c -fPIC -DPIC -o .libs /liba .o
libtool : compile : gcc -I ../include -c liba .c -o liba .o >/dev /null 2>&1
libtool --mode = compile gcc -I../include -c libb.c
libtool : compile : gcc -I ../include -c libb .c -fPIC -DPIC -o .libs /libb .o
libtool : compile : gcc -I ../include -c libb .c -o libb .o >/dev /null 2>&1
libtool --mode = link gcc -I../include -o liba.la liba.lo
libtool : link : ar cru .libs /liba .a .libs /liba .o
libtool : link : ranlib .libs /liba .a
libtool : link : ( cd ".libs " && rm -f "liba .la " && ln -s "../liba .la " "liba .la " )
libtool --mode = link gcc -I../include -o libb.la libb.lo -rpath /usr/local/lib
libtool : link : gcc -shared -fPIC -DPIC .libs /libb .o -Wl , -soname -Wl , libb .so .0 -o .libs /libb .so .0.0.0
libtool : link : (cd ".libs " && rm -f "libb .so .0" && ln -s "libb .so .0.0.0" "libb .so .0")
libtool : link : (cd ".libs " && rm -f "libb .so " && ln -s "libb .so .0.0.0" "libb .so ")
libtool : link : ar cru .libs /libb .a libb .o
libtool : link : ranlib .libs /libb .a
libtool : link : ( cd ".libs " && rm -f "libb .la " && ln -s "../libb .la " "libb .la " )
如果把gcc換成cc會發現不會有FPIC參數跑出來,詳細原因不明。
可以看到
現在libs下面檔案除了object檔案外,還多了.lo和 .la檔案。
libs/Makefile 1
2
$ ls libs
liba.c liba.la liba.lo liba.o libb.c libb.la libb.lo libb.o Makefile
我們先看lo檔,可以看到lo檔存放PIC和一般的object檔案位置資訊。
libs/Makefile 1
2
3
4
5
6
7
8
9
10
11
12
$ cat liba.lo
# liba.lo - a libtool object file
# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1ubuntu1
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object = '.libs/liba.o'
# Name of the non-PIC object
non_pic_object = 'liba.o'
而la檔案則是存放library資訊,我們可以看到靜態和動態主要的差別是有沒有指定so檔名稱以及存放路徑
libs/Makefile 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
$ diff libs/libb.la libs/liba.la
1c1
< # libb.la - a libtool library file
---
> # liba.la - a libtool library file
8c8
< dlname = 'libb.so.0'
---
> dlname = ''
11c11
< library_names = 'libb.so.0.0.0 libb.so.0 libb.so'
---
> library_names = ''
14c14
< old_library = 'libb.a'
---
> old_library = 'liba.a'
25,28c25,28
< # Version information for libb.
< current = 0
< age = 0
< revision = 0
---
> # Version information for liba.
> current =
> age =
> revision =
41c41
< libdir = '/usr/local/lib'
---
> libdir = ''
使用libtool編譯執行檔
src/Makefile中主要就是連結的時候除了指定object檔案外,指定連結la檔案而不是.a或是 .so檔案。libtool會幫你從la檔案中找到對的library binary。
src/Makefile 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LIBTOOL = libtool
SRCS = test.c
OBJS = $( patsubst %.c, %.o, $( SRCS)) ../libs/libb.la ../libs/liba.la
CFLAGS = -I$( INC_DIR)
CC = gcc
TARGET = test
$(TARGET) : $( OBJS )
$( LIBTOOL) --mode= link $( CC) $( CFLAGS) -o $@ $( OBJS)
%.o : %.c
$( LIBTOOL) --mode= compile $( CC) $( CFLAGS) -c $^
clean :
rm -rf $( OBJS) .libs *.lo $( TARGET)
安裝
如果--mode=link
有加-rpath
資訊,libtool會支援安裝功能:
libtool –mode=install cp libs/libb.la /usr/local/lib
另外值得一提的是安裝時la檔案是有被更動的。比對如下
src/Makefile 1
2
3
4
5
$ diff libs/libb.la /usr/local/lib/libb.la
31c31
< installed = no
---
> installed = yes
應該有人會問怎麼沒看到autotool怎麼用,這當作下次題目吧。
參考資料及資源