在學習某一函數庫的時候,我們經常做的第一件事, 就是把該函數庫的源碼下載回來進行編譯和安裝(一般是這三步:./configure -> make -> make install),然後寫一些調用庫函數的小用例,看其能否正常運行以及運行後的效果。完成庫函數安裝,當我們滿心期待地使用GCC進行用例編譯的時候, 卻經常遇到以下問題:
test.c:X:XX: fatal error: XXXX.h: No such file or directorycompilation terminated.
又或者:
test.c:(.text+0XX): undefined reference to `XXXX'collect2: ld returned 1 exit status
一些同學遇到這樣的問題後,倒騰幾個小時沒能找到解決問題的方法,心生畏難情緒,最終倒在庫函數學習的入門門檻上。
下文通過分析問題出現的原因,結合glib函數庫執行個體,提出解決方案,一步步地引導初學者進行函數庫入門學習(這篇文章以C/C++源碼作為討論的基礎,預設的編輯器是GNU下的GCC和CC)。
1.原因分析
首先分析以上問題出現的原因,我們先要瞭解這些知識:編譯和連結原理,GCC標頭檔和庫檔案的尋找路徑。
程式的編譯和連結:在進行程式編譯時間,編譯器會檢查文法是否正確,檢查函數與變數的聲明,這需要我們告知編譯器我們的函數和變數聲明在哪個標頭檔。文法正確並且相關標頭檔的路徑都給到編譯器後,編譯器將編譯出中間目標檔案(.o或是.obj檔案)。
在進行程式連結時,連結器會在所有中間目標檔案或者庫檔案(它由中間目標檔案打包而成)中尋找函數的實現,如果連結器未尋找到函數實現,它將返回連結失敗訊息。
知道這些之後,對照文章開頭列出的出錯訊息,出錯原因就很明顯了。第一種出錯情況是程式編譯時間未找到某個標頭檔;第二種是由於連結器未找到某函數所依賴的庫檔案或者該函數壓根就沒有實現。
GCC去哪找標頭檔和庫檔案呢?GCC有其
預設的尋找路徑,對標頭檔的尋找在/usr/include,/usr/local/include等目錄下,編譯時間可以通過GCC的-I選項指定標頭檔
的尋找目錄;對庫檔案的尋找在/usr/lib,/usr/local/lib等目錄下,編譯時間可以通過GCC的-L選項指定庫檔案的尋找目錄。
顯然,上面錯誤訊息裡的標頭檔/庫檔案並不在GCC預設的標頭檔/庫檔案尋找路徑裡。它們被安裝到了哪裡?
2.解決方案
通過查看函數庫源碼檔案中的Makefile我們可以知道函數庫的標頭檔和庫檔案安裝的路徑,以glib函數庫為例,這裡我們只看其Makefile(由./configure命令產生)中的幾個欄位。
prefix = /usr/localexec_prefix = ${prefix}includedir = ${prefix}/includelibdir = ${exec_prefix}/lib
這幾個值指定了glib函數庫的標頭檔/庫檔案的安裝路徑,在執行make install命令後,標頭檔和庫檔案將被分別安裝到指定路徑。
另外,我們可以使用locate命令定位標頭檔和庫檔案的路徑,例如locate glib,包含"glib“欄位的檔案名稱的完整路徑將被列出來,再自行定位庫檔案和標頭檔的路徑。
假設我們已經從源碼安裝好了glib函數庫,我們試著跑個Hello word小程式驗證安裝結果。
#include <stdio.h> #include <glib.h> int main(void) { g_printf("hello world!\n"); return 0; }
執行:
gcc -I /usr/local/include/glib-2.0/ -I /usr/local/lib/glib-2.0/include/ -lglib-2.0 -o hello hello.c
由於glib庫檔案已被安裝在GCC預設的庫檔案連結目錄下(/usr/local/lib/),所以這裡沒有指定庫檔案搜尋路徑。
試試執行:
gcc -o hello hello.cgcc -I /usr/local/include/glib-2.0/ -I /usr/local/lib/glib-2.0/include/ -o hello hello.c
看有什麼結果:)
另一個能協助我們解決標頭檔或庫檔案路徑問題的工具是pkg-config,許多函數
庫都支援pkg-config命令,在glib的Makefile中,有該條語句:pkgconfigdir =
$(libdir)/pkgconfig,它指示了glib對應的.pc檔案(即glib-2.0.pc)安裝路徑。我們來看glib-2.0.pc檔案
裡有哪些主要內容:
prefix=/usr/localexec_prefix=${prefix}libdir=${exec_prefix}/libincludedir=${prefix}/includeLibs: -L${libdir} -lglib-2.0Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include
Cflags給出編譯時間所需的選項,Libs給出了連結時所需的選項,然後這樣使用pkg-config命令:
gcc `pkg-config --cflags --libs glib-2.0` -o hello hello.c
知道問題出現原因和解決方案,下次編譯時間再遇到“No such file or directory”或者“undefined reference to `XXXX'”,就不怕了吧?
本文參考了以下文章:《跟我一起寫Makefile》by陳皓
《Makefile好幫手:pkgconfig》by李先靜