第十一章:使用make更新靜態庫文件

 

第十一章:使用make更新靜態庫文件


靜態庫文件也稱爲“文檔文件”,它是一些.o文件的集合。在Linux(Unix)中使用工具“ar”對它進行維護管理。它所包含的成員(member)是若干.o文件。

11.1      庫成員作爲目標

一個靜態庫通常由多個.o文件組成。這些成員(.o文件)可獨立的被作爲一個規則的目標,庫成員作爲目標時需要按照如下的格式來書寫:

 

ARCHIVE(MEMBER)

 

注意,這種書寫方式只能出現在規則的目標和依賴,不能出現在規則的命令行中!因爲,絕大多數命令不能支持這種語法,命令不能直接對庫的成員進行操作。這種表達式在規則的目標或者依賴中,它表示庫“ARCHIVE”的成員“MEMBER”。含有這種表達式的規則的命令行只能是“ar”命令或者其它可以對庫成員進行操作的命令。例如:下邊這個規則用於創建庫“foolib”,並將“hack.o”成員加入到庫:

 

foolib(hack.o) : hack.o

ar cr foolib hack.o

 

實際上,這個規則實現了對庫的所有成員進行了更新,其過程使用了隱含規則(創建需要的.o文件)。另外需要注意工具“ar”的用法(可參考ar的man手冊)。

如果在規則中需要同時指定庫的多個成員,可以將多個成員羅列在括號內,例如:

 

foolib(hack.o kludge.o)

 

它就等價於:

 

foolib(hack.o) foolib(kludge.o)

 

在描述庫的多個成員時也可以使用shell通配符。例如:“foolib(*.o)”,它代表庫文件“foolib”的所有.o成員。

11.2      靜態庫的更新

上一節已經講述了規則中形如“A(M)”目標的含義,它代表靜態庫“A”的成員“M”。make爲這樣的一個目標搜索隱含規則時,可重建“(M)”的隱含規則,同樣也被認爲是可重建“A(M)”這個目標的隱含規則。

這就出現了一個特殊的模式規則,它的目標模式是“(%)”。這個特殊的規則被用來更新目標“A(M)”,規則將文件“M”拷貝到庫“A”中,如果之前在靜態庫文件“A”不存,則首先創建這個庫文件。例如:目標爲“foo.a(bar.o)”的規則,執行時將完成:首先使用隱含規則生成其成員“bar.o”,之後將“bar.o”加入到庫“foo.a”中。那麼“bar.o”就作爲庫文件“foo.a”的一個成員(當然如果“foo.a”不存在,就會被創建。這個特殊規則的命令行一般都是“ar”命令)。

這個特殊的規則可以和其它隱含規則一起構成一個隱含規則鏈。因此我們可以直接在命令行中執行“make ‘foo.a(bar.o)’”(注意“foo.a(bar.o)”作爲命令行選項,需要使用引號,否則shell會將“(”作爲特殊字符處理),只要當前目錄下存在“bar.c”這個文件,就會執行如下命令:

 

cc -c bar.c -o bar.o

ar r foo.a bar.o

rm -f bar.o

 

我們可以看到文件“bar.o”是被作爲中間過程文件來處理的。需要說明的是,包含上述命令的規則,在規則的命令行中使用自動化變量“$%”來代表庫成員“bar.o”。

在一個靜態庫(文檔文件)中,其所有的成員名是不包含目錄描述的。就是說對於靜態庫,當使用“nm”命令查看其成員時,能夠獲得的信息只是靜態庫所包含的成員名字(一系列.o文件,文件名中並沒有包含目錄)。但我們在Makefile中,採用“A(M)”格式的目標,書寫建立(重建或者更新)靜態庫的規則時,可以指定爲庫它的成員指定路徑。就是說在描述它的成員時,可以使用包含路徑的文件名。例如一個規則的目標可以這樣書寫“foo.a(dir/file.o)”,在這個規則被執行時可能會執行如下的命令:

 

ar r foo.a dir/file.o

 

和上邊的例子類似,它將指定目錄下的.o文件加入到庫中,此文件(“dir/file.o”)被作爲靜態庫的一個成員。對於類似這樣的目標,重建它的隱含規則的命令行中可能就需要使用自動化變量“%D”和“%F”。

11.2.1       更新靜態庫的符號索引表

本小節的內容相對簡單。前邊提到過,靜態庫文件需要使用“ar”來創建和維護。當給靜態庫增建一個成員時(加入一個.o文件到靜態庫中),“ar”可直接將需要增加的.o文件簡單的追加到靜態庫的末尾。之後當我們使用這個庫進行連接生成可執行文件時,鏈接程序“ld”卻提示錯誤,這可能是:主程序使用了之前加入到庫中的.o文件中定義的一個函數或者全局變量,但連接程序無法找到這個函數或者變量。

這個問題的原因是:之前我們將編譯完成的.o文件直接加入到了庫的末尾,卻並沒有更新庫的有效符號表。連接程序進行連接時,在靜態庫的符號索引表中無法定位剛纔加入的.o文件中定義的函數或者變量。這就需要在完成庫成員追加以後讓加入的所有.o文件中定義的函數(變量)有效,完成這個工作需要使用另外一個工具“ranlib”來對靜態庫的符號索引表進行更新。

我們所使用到的靜態庫(文檔文件)中,存在這樣一個特殊的成員,它的名字是“__.SYMDEF”。它包含了靜態庫中所有成員所定義的有效符號(函數名、變量名)。因此,當爲庫增加了一個成員時,相應的就需要更新成員“__.SYMDEF”,否則所增加的成員中定義的所有的符號將無法被連接程序定位。完成更新的命令是:

 

ranlib ARCHIVEFILE

 

通常在Makefile中我們可以這樣來實現:

 

libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...

ranlib libfoo.a

 

它所實現的是在更新靜態庫成員“x.o”和“y.o”之後,對靜態庫的成員“__.SYMDEF”進行更新(更新庫的符號索引表)。

如果我們使用GNU ar工具來維護、管理靜態庫,我們就不需要考慮這一步。GNU ar本身已經提供了在更新庫的同時更新符號索引表的功能(這是默認行爲,也可以通過命令行選項控制ar的具體行爲。可參考 GNU ar工具的man手冊)。

11.3      make靜態庫的注意事項

在make靜態庫(文檔文件)時,特別需要注意:make的並行執行(執行make時使用“-j”選項)給更新靜態庫帶來的影響。因爲在同一時刻,當同時使用多個“ar”命令來操作相同的靜態庫時,將會靜損壞態庫,甚至導致此靜態庫不可用。

可能在後期的make版本中,會提供一種在並行執行時防止同時多個“ar”命令對同一靜態庫的操作的機制。但是就目前的版本來說,這個問題是存在的。因此要求我們在書寫Makefile時,加入控制策略來避免make併發執行時多個“ar”命令同時操作同一個靜態庫文件。或者放棄使用make的併發執行功能。

11.4      靜態庫的後綴規則

靜態庫的後綴規則屬於後綴規則的特殊應用,後綴規則在目前版本的GNU make中已經被模式規則替代。但目前版本make同樣支持舊風格的後綴規則,主要是爲了兼容老版本的make。對於靜態庫的重建也可以使用後綴規則。目標後綴需要是“.a”(在Linux(Unix)中、靜態庫的後綴爲.a)。例如這樣一個後綴規則:

 

.c.a:

$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o

$(AR) r $@ $*.o

$(RM) $*.o

 

它相當於模式規則:

 

 (%.o): %.c

$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o

$(AR) r $@ $*.o

$(RM) $*.o

 

對於老風格的後綴規則,它的目標是“.a.c”,當轉換爲模式規則時。後綴規則中的“.a”被作爲模式規則的目標文件後綴(目標文件就是N.a);“.c”被作爲了模式規則依賴文件的後綴(依賴文件就是N.c)。

存在這樣一種情況:可能一個不是靜態庫的文件使用了後綴.a。那麼make也會按照正常的方式靜態庫的後綴規則轉換成爲一個模式規則。因此對於一個雙後綴規則的目標“.x.a”,經過make轉換後會產生兩個模式規則:“(%.o): %.x” 和“%.a: %.x”。具體的轉換過程可參考 後綴規則 一節

發佈了13 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章