天天看點

《Linux核心修煉之道》——2.3 自由軟體的編譯與安裝

本節書摘來自異步社群《linux核心修煉之道》一書中的第2章,第2.3節,作者:華清遠見嵌入式教育訓練中心 任橋偉著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

linux核心修煉之道

在我們使用linux的過程中,經常會遇到需要自己編譯軟體的情況,可以說自由軟體的百家争鳴本身就是linux世界不可分割的一部分。是以,本節簡單介紹下自由軟體的編譯與安裝過程。

自由軟體和私有軟體的差別在于它們對源代碼的通路不同。自由軟體以源代碼檔案壓縮包的形式釋出,使用者必須自己編譯源代碼才能使用。

對于大部分的自由軟體,存在已編譯版本,使用者可以隻安裝這些預編譯的二進制代碼。而某些自由軟體并不以這種形式釋出,或者其早期版本不以二進制代碼形式釋出,而且,如果你使用的是特殊的作業系統或者特殊的硬體架構,許多軟體并沒有為此提供編譯好的版本。更重要的是,親自編譯軟體可以讓你隻啟用自己感興趣的選項,或者通過對該軟體源碼的修正以便它能夠确實滿足自己的需求。

2.3.1 釋出時的組織結構

通常,自由軟體釋出時都具有相同的組織結構。

install檔案:描述安裝步驟。

readme檔案:包含有關該軟體的一般資訊(簡短描述、作者、項目url、相關文檔、有用的連結等)。如果不存在install檔案,通常在readme檔案中會包含安裝步驟簡介。

copying檔案:包含許可證或是釋出該軟體的情形。有時候該檔案叫license。

contrib或是credits檔案:包含該軟體相關人員的清單(活動的參與者、相關評論、第三方軟體等)。

changes檔案:包含最近改進以及故障修正,有時為較少見的news檔案。

makefile檔案:控制該軟體的編譯(對make該檔案是必需的)。如果該檔案一開始不存在,那麼它将由預編譯配置過程生成。

經常也有configure或imakefile檔案以便使用者針對特殊系統自定義生成新的 makefile檔案。

儲存源檔案以及在編譯結束後儲存二進制檔案的目錄,通常為src。

儲存與該軟體相關的文檔(通常是man形式)的目錄,通常為doc。

有時也有儲存該軟體特定資料(一般是配置檔案、資料示例檔案或資源檔案)的目錄。

2.3.2 配置

僅從技術興趣上看,自由軟體作者提供源代碼的目的在于使該軟體可移植,使其能夠在現存的類unix系統上幾乎不加修改地使用,而這需要在編譯這些軟體之前對其進行配置。

有幾種不同的配置方式,但我們需要選擇作者指定的那個。

如果在該軟體的釋出目錄中存在一個名為configure的檔案,則可以使用autoconf進行配置。

如果在該軟體的釋出目錄中存在一個名為imakefile的檔案,則可以使用imake進行配置。

根據install檔案(或readme檔案)的内容提示運作一個shell腳本(比如 install.sh)。

1.autoconf

autoconf用于正确配置軟體,它建立編譯需要的檔案(比如makefile),且有時會直接對源代碼進行修改(比如使用config.h.in檔案)。autoconf的規則十分簡單。

該軟體的作者了解配置他的軟體需要進行哪些測試(比如:“你使用哪一版本的庫檔案?”),他使用一種精确的文法将這些測試編寫進configure.in檔案。

并且,他通過運作autoconf從configure.in檔案生成configure自動配置腳本,該腳本将在配置軟體的時候運作所需的測試。

最終使用者執行該腳本,這樣autoconf就會為編譯進行配置。

下面是一個autoconf的使用示例:

為了更好地控制configure,可以通過指令行或環境變量為其添加選項。比如:

或者(在bash中):

或者:

一般而言,大部分configure腳本執行失敗時的出錯資訊都類似于“configure: error: cannot find library guile”,這表示configure腳本找不到某個庫檔案。configure腳本在測試時會使用該庫檔案編譯一個小測試程式,如果它不能成功編譯該程式,就說明它不能夠編譯該軟體,是以會給出該出錯資訊。出現該錯誤後,我們可以采取下面的措施。

可以在config.log檔案中找到配置過程所執行的每一步驟,從中發現産生這一錯誤的原因。

檢查提示的庫檔案是否已正确安裝。如果沒有,在安裝之後再運作configure。檢查它是否已安裝的有效方式是查找包含提示字元串的庫檔案,一般是lib< 名稱 >.so。比如:

檢查編譯器是否能夠通路該庫檔案,它是否在/usr/lib、/lib、/usr/x11r6/lib (或是在由環境變量ld_library_path指出的)目錄中。

檢查該庫檔案相應的頭檔案是否已安裝于正确地方(通常是/usr/include或 /usr/local/include或/usr/x11r6/include)。

檢查是否擁有足夠的磁盤空間(configure腳本需要一些空間來儲存中間檔案)。可以通過“dh”指令檢視系統中各分區的使用情況。

檢查是否某些環境變量設定錯了,比如ld_library_path。

2.imake

imake讓你能夠使用簡單的規則生成makefile檔案以配置自由軟體,這些規則指定需要編譯那些檔案才能生成所需的二進制檔案,而imake生成相應的makefile。可在imakefile檔案中指定這些規則。

使用imake最為簡單的方法是進入解壓後的代碼目錄,然後運作xmkmf腳本,而它又會調用imake程式。

3.各種shell腳本

閱讀install或readme檔案了解進一步資訊。通常,需要執行install.sh或 configure.sh檔案。該安裝腳本或是非互動的(它自己決定需要什麼),或者會向詢問有關系統的資訊(比如是路徑)。

另外,某些自由軟體在其初期開發階段,有時會要求使用者手動更改某些配置檔案。通常,這些檔案是makefile檔案和config.h檔案。

2.3.3 編譯

既然該軟體已正确配置,所剩的就是編譯了。這個階段通常比較簡單,并且不會出現什麼嚴重的問題。

編譯源代碼的各個步驟常存儲于makefile或gnumakefile檔案中。當執行make時,它會從目前目錄讀取該檔案(如果該檔案存在的話)。如果它不存在,可以通過make的-f選項指定其他檔案。

通常,使用make需要遵循如下一些約定。

不加參數執行make表示僅編譯程式,而不安裝。

make install編譯程式(不是一定的),并随後将檔案安裝到系統的正确位置。某些檔案常常無法正确安裝(比如man、info),可能會需要使用者自己手動複制。有時候,需要在子目錄中再次執行make install,通常這是由于包含了第三方開發的子產品。

make clean清除編譯産生的所有臨時檔案,大多數情況下還會删除可執行檔案。

編譯時最為常見的錯誤有:

(1)main.c:16: decl.h: no such file or directory

編譯器不能找到對應的頭檔案。不過,在軟體配置階段就可能已經發現該錯誤了。解決方法如下。

檢查該頭檔案确實已經存在于以下某個目錄中:/usr/include、/usr/local/include、/usr/x11r6/include或它們的某個子目錄。如果沒有,請在整個磁盤上查找它(可以使用find或locate指令)。如果還是沒有,請檢查是否已經安裝了該頭檔案所對應的庫。

檢查該頭檔案确實可以讀取(可以使用less指令來測試)。

如果它确實在/usr/local/include或/usr/x11r6/include目錄中,你可能需要為自己的編譯器添加額外的參數,打開編譯出錯的那個目錄中的那個makefile檔案,找到出錯的那一行,并在調用編譯器(gcc,有時是$(cc))的地方添加字元串-i< 路徑 >,其中< 路徑 >是包含該頭檔案的路徑。如果不清楚要把該選項加到哪裡,就把它添加到檔案開始處cflags或cc變量定義後面。

再次運作make,如果它還是不起作用,請檢查前面的那個選項是否被添加于編譯過程中出錯的地方。

如果還是不起作用,隻有向周圍的高手或開發社群求助了。

(2)'struct foo' undeclared (first use this function)

結構幾乎是所有軟體都會使用的一種資料類型,系統在頭檔案中可能會定義許多。這個錯誤提示表示該問題可能是由于找不到頭檔案,或誤用頭檔案所造成。解決該問題的正确步驟如下。

試着檢查一下出問題的結構是否已由程式或系統定義,比如用grep指令檢視該結構是否已經在某個頭檔案中定義了。比如,進入該軟體源代碼的根目錄中執行:

在螢幕上可能會出現許多行,找到頭檔案中grep指出的那一行,檢查它是否就是你所需要的。如果是,則說明該頭檔案未包含于出錯的.c檔案中。有兩個解決方法:在出錯的.c檔案開始處添加#include "< 檔案名 >.h";或者将該結構的定義複制到該檔案的開始處。

如果沒有找到,在系統頭檔案(通常位于/usr/include、/usr/x11r6/include或/usr/local/include)中再次查找。不過這一次如果找到的話,就需要在.c檔案中添加#include << 檔案名 >.h >的語句。

如果該結構還是不存在,請試着找找它是否是在哪個庫中定義(請檢視install或readme檔案以确定該軟體使用了哪些庫以及它們的版本)。如果該軟體需要的版本不是系統上安裝的那一個,就需要更新該庫了。

如果依然不起作用,就要檢查該程式是否真能在你的架構上運作了(某些程式還沒有移植到linux系統上),并檢查你是否已經為自己的架構正确配置了該程式(比如在執行configure的時候)。

(3)parse error

這個問題解決起來較為複雜,因為編譯器常會在真正出錯的地方之後報錯。有時候,它僅僅是由于某個資料類型未定義。如果碰上如下的錯誤資訊:

那麼很可能是foo_t類型未曾定義,解決方式同前一個問題類似。

(4)no space left on device

這個問題解決起來較為簡單:磁盤上已經沒有足夠的空間從源檔案生成二進制檔案了。你可以通過釋放安裝目錄所在分區的一些空間來解決(删除臨時檔案或源檔案,解除安裝某些已經不用的程式)。

(5)/usr/bin/ld: cannot open -lglloq: no such file or directory

這表示ld程式無法找到某個庫。為了包含某個庫,ld将會搜尋由-l< 庫 >選項指定的庫檔案,相應檔案為lib< 庫 >.so。如果ld找不到,它将給出一條錯誤資訊。要解決該問題,可以如下操作。

用locate指令檢查該檔案是否在硬碟上。通常,圖形庫在/usr/x11r6/lib目錄中。比如:

如果上述查找沒有結果,就使用find指令再次查找(比如find /usr -name "libglloq.so*")。如果還是找不到,就需要安裝它了。

一旦找到了該庫,請檢查它是否能被ld通路。/etc/ld.so.conf檔案指定了尋找這些庫檔案的目錄,把該庫的路徑添加到該檔案末尾(可能需要重新開機系統以使改動起作用)。也可以把該目錄添加到環境變量ld_library_path中。

如果還是不行,請用file指令檢查該庫檔案是否為一個可執行檔案。如果它是一個符号連結,請檢查該連結完好且沒有指向不存在的檔案(可用nm libglloq.so檢查)。而且,該庫檔案的權限可能是錯誤的(比如,如果不是root使用者且該庫檔案不允許讀)。

(6)glloq.c(.text+0x34): undefined reference to `glloq_init'

該問題是由于在編譯的最後階段某個符号找不到。通常,這是因為某個庫出問題了。可能的問題如下。

首先,請查找該符号是否應該在某個庫檔案中。比如,如果該符号以gtk開頭,它就應該屬于gtk庫。如果這個庫能夠很容易地找到,你可以用nm指令列出該庫中的符号。比如:

使用nm時添加-o選項可以分别在不同行中顯示庫中的名稱,進而使搜尋變得更為簡單。假定我們要搜尋符号bulgroz_max,可以:

可以看到該符号bulgroz_max定義于frobnicate庫中(其名稱前有一個大寫字元t)。然後,你隻需要編輯makefile檔案以在編譯指令行中添加字元串-lfrobnicate(将其添加于定義ldflags或lfglags的那一行的末尾,或是在建立相應二進制檔案的那一行處)。

編譯中使用的庫檔案不是該軟體需要的。請閱讀該釋出版的readme或install檔案,以檢視需要哪個版本。

該釋出版的目标檔案沒有全部正确連結,缺少了定義該函數的檔案。請鍵入nm -o *.o以檢視應該是哪個檔案,然後将相應的.o檔案添加到對應的編譯指令行上。

出錯的函數或變量可能并不存在,可以嘗試删除它:編輯出問題的源檔案(它的名字會出現于出錯資訊的開始處)。這可能會導緻程式執行混亂,以及在啟動時出現segfault(段錯誤)等錯誤。

(7)segmentation fault (core dumped)

有時候,編譯器立即挂起,并産生該錯誤資訊。除了建議安裝一個更新版本的編譯器,沒有更好的辦法了。

(8)no space on /tmp

在不同的階段,編譯器需要臨時工作空間,如果它不能申請到這些空間的話,它将出錯。是以,你需要清理分區,不過請盡量小心,因為删除某些檔案會導緻某些正在執行的程式(x 伺服器、管道等)挂起。你必需能夠清楚知道自己在做什麼!如果/tmp所在的分區并不僅僅包含該目錄(比如根目錄),請搜尋并删除core檔案。

(9)make/configure 死循環

在你的系統上,這常常隻是一個時間上的問題。make确實需要了解計算機時間和它檢查的檔案的時間。它比較這兩個時間,并根據結果确定某個目标是否已過時。

某些日期問題可能導緻make不停地編譯(或不停地遞歸編譯某個子目錄)。在這種情況下,touch(其作用是将有問題的檔案的時間設定為目前時間)通常會解決該問題。比如:

2.3.4 安裝

既然編譯已經完成,接下來就需要将編譯後的檔案複制到一個合适的位置(通常是在/usr/local的某個子目錄中)。執行make install将完成該任務,安裝所有需要的檔案。

通常,在install或readme檔案中将描述該過程。不過有時候,開發人員會忘了提供這一資訊。那時,我們就必須親自安裝所有東西了。

複制可執行檔案(程式)到/usr/local/bin目錄。

複制庫檔案(lib*.so檔案)到/usr/local/lib目錄。

複制頭檔案(*.h檔案)到/usr/local/include目錄。

複制資料檔案通常到/usr/local/share。如果你不知道安裝的過程,可以先試着不複制資料檔案運作程式,然後就可以按照程式的提示将他們複制到正确的位置了(比如根據某個出錯資訊:cannot open /usr/local/share/glloq/data.db)。

文檔的安裝有一點不同:man檔案通常會被複制到某個/usr/local/man的子目錄中,通常這些檔案的格式為troff(或groff),且它們的擴充名是某個數字,它們的檔案名是某個指令的名稱(比如echo.1),根據其擴充名n将其複制到 /usr/local/man/man< n >目錄;info檔案複制到/usr/info或/usr/local/info目錄。