章節概覽#
本章介紹如何從 C 原始碼編譯與安裝軟體。大多數非專有的第三方 Unix 軟體以原始碼形式發布,原因有二:Unix 平台種類繁多,難以為所有平台提供二進位套件;開放原始碼的發布方式鼓勵社群貢獻修正與新功能。
雖然 Linux 發行版提供了方便的套件管理系統來更新核心元件,但你可能仍需自行編譯安裝軟體,理由包括:
- 自訂設定選項
- 將軟體安裝到任意位置,甚至同時安裝多個版本
- 控制安裝的版本(發行版不一定提供最新版)
- 更深入了解套件的運作方式
16.1 軟體建置系統 (Software Build Systems)#
Linux 上有多種程式語言與建置系統,從傳統的 C 到 Python 等直譯式語言,每種都有自己的套件建置與安裝方式。
本章聚焦於 GNU autotools 產生的 configuration scripts,這是最廣泛使用且穩定的建置系統之一。從 C 原始碼安裝套件的基本步驟為:
- 解壓原始碼壓縮檔
- 設定套件 (configure)
- 執行
make編譯程式 - 執行
make install安裝套件
閱讀本章前,建議先理解第 15 章關於開發工具的基礎知識。
16.2 解壓 C 原始碼套件 (Unpacking C Source Packages)#
原始碼通常以 .tar.gz、.tar.bz2 或 .tar.xz 格式發布。解壓前應先用 tar tvf 或 tar ztvf 檢查壓縮檔內容,確認檔案是否都在同一個子目錄中。
# 檢查壓縮檔內容
tar tvf package-1.23.tar.gz需注意的事項#
- 若檔案沒有共同的上層目錄,解壓會弄亂當前目錄——先建立新目錄再解壓
- 若壓縮檔包含絕對路徑(如
/etc/passwd),代表可能含有惡意程式碼,應立即移除
解壓後的檔案分類#
解壓後先閱讀 README 和 INSTALL 檔案,了解套件描述與安裝說明。套件檔案大致分為三類:
- 建置相關檔案:
Makefile、Makefile.in、configure、CMakeLists.txt - 原始碼檔案:副檔名為
.c、.h、.cc(C++ 則為.cc、.C、.cxx) - 目的檔 (object files):副檔名為
.o或二進位檔(正常的原始碼發布不應包含,若有代表套件品質可能有問題)
16.3 GNU Autoconf#
C 原始碼雖然相當可攜,但各平台的差異使得單一 Makefile 無法適用於所有系統。GNU autoconf 是一套自動產生 Makefile 的系統,套件通常包含 configure、Makefile.in 和 config.h.in 等檔案。
核心流程:執行 configure 腳本分析系統特性,再根據 .in 模板檔產生實際的建置檔案。
./configure
make
make install16.3.1 Autoconf 範例#
以 GNU coreutils 為例,示範完整的建置流程:
# 設定安裝到家目錄下的 mycoreutils
./configure --prefix=$HOME/mycoreutils
# 編譯
make
# 執行測試
make check
# 預覽安裝內容(乾跑)
make -n install
# 實際安裝
make install安裝完成後,mycoreutils 目錄下會包含 bin、share 等子目錄。
16.3.2 使用套件管理工具安裝#
可以透過發行版的套件工具來管理自行編譯的軟體:
- Debian/Ubuntu:使用 checkinstall 工具取代
make install,自動建立.deb套件
# 用 checkinstall 取代 make install
checkinstall make install- RPM 系統:使用
rpmdev-setuptree建立目錄結構,再用rpmbuild打包
16.3.3 configure 腳本選項#
--prefix 是最常用的選項,用來指定安裝目錄。預設的安裝前綴為 /usr/local。
常用選項:
--prefix=directory:設定安裝前綴目錄--bindir=directory:指定可執行檔安裝位置--sbindir=directory:指定系統可執行檔安裝位置--libdir=directory:指定函式庫安裝位置--disable-shared:不建置 shared libraries--with-package=directory:指定依賴套件的位置
可以建立獨立的 build directory 來實驗不同的設定選項,從該目錄執行原始碼目錄中的
configure腳本即可。這樣原始碼目錄不會被修改,也方便為不同平台建置。
16.3.4 環境變數#
可以透過環境變數影響 configure 的行為。最重要的變數為 CPPFLAGS、CFLAGS 和 LDFLAGS。
# 定義 preprocessor 巨集
CPPFLAGS=-DDEBUG ./configure
# 指定額外的 include 路徑
CPPFLAGS=-I/include_dir ./configure
# 指定額外的 library 路徑
LDFLAGS=-L/lib_dir ./configure
# 同時指定 library 路徑與 rpath
LDFLAGS="-L/lib_dir -Wl,-rpath=/lib_dir" ./configure設定變數時要格外小心,一個小錯誤就可能導致
configure失敗。例如-I中忘了加-就會導致「C compiler cannot create executables」錯誤。建議使用 CPPFLAGS 而非 CFLAGS 來指定 header file 目錄,因為configure常獨立於 compiler 執行 preprocessor。
16.3.5 Autoconf Targets#
configure 產生的 Makefile 除了標準的 all 和 install 之外,還有幾個實用的 target:
make clean:移除所有 object files、executables 和 librariesmake distclean:類似make clean,但額外移除所有自動產生的檔案(Makefiles、config.h、config.log等),讓原始碼目錄恢復到剛解壓的狀態make check:執行套件的測試套件make install-strip:類似make install,但會 strip 掉 symbol table 和除錯資訊,產生更小的二進位檔
16.3.6 Autoconf 記錄檔#
當 configure 失敗時,可以檢查 config.log 來找出問題。由於 config.log 通常很大,建議的做法是:
- 跳到檔案末尾,向上搜尋錯誤訊息
- 搜尋
for more details等文字片段,通常能快速定位到失敗的 configure 輸出附近
16.3.7 pkg-config#
當系統上有多個第三方函式庫安裝在不同位置時,pkg-config 工具可以幫助你找到正確的 include 路徑和 linker flags。
# 查詢 zlib 需要的 linker flags
pkg-config --libs zlib
# 列出所有已知的函式庫
pkg-config --list-allpkg-config 的運作原理是讀取 .pc 設定檔(通常位於 lib/pkgconfig 目錄)。這些 .pc 檔包含函式庫的前綴路徑、include 路徑、linker flags 等資訊。
許多函式庫的
.pc檔只在安裝了 development packages 後才會出現。例如在 Ubuntu 上,需要安裝libssl-dev才能取得openssl.pc。
若 .pc 檔位於非標準位置,有兩種解決方式:
- 建立 symbolic link 到中央
pkgconfig目錄 - 設定 PKG_CONFIG_PATH 環境變數(不建議系統層級使用)
16.4 安裝實務 (Installation Practice)#
了解如何建置軟體很重要,但知道何時與何處安裝同樣關鍵。
自行安裝的優點#
- 自訂套件的預設設定
- 安裝過程中能更深入了解套件
- 自行控制版本
- 更容易備份自訂套件
- 便於在網路中分發
自行安裝的缺點#
- 可能覆蓋系統既有的重要檔案
- 耗時
- 不會自動升級,需關注安全性更新
- 可能設定錯誤
16.4.1 安裝位置#
預設的安裝前綴 /usr/local 是本機安裝軟體的傳統目錄。作業系統升級會忽略此目錄,適合小規模的本機軟體安裝。但若安裝了大量自訂軟體,/usr/local 可能變得雜亂,此時建議使用 16.3.2 節的套件管理方式。
16.5 套用修補檔 (Applying a Patch)#
Patch(又稱 diff)是對原始碼的修改,格式如下:
--- src/file.c.orig 2015-07-17 14:29:12.000000000 +0100
+++ src/file.c 2015-09-18 10:22:17.000000000 +0100
@@ -2,16 +2,12 @@套用 patch 的方法:
# 直接套用(不剝離路徑前綴)
patch -p0 < patch_file
# 剝離一層路徑前綴(常用於不同版本號的情況)
patch -p1 < patch_file套用 patch 前,注意 patch 檔開頭的路徑,確認你在正確的目錄下執行。若
patch詢問「File to patch:」,通常代表你的工作目錄不正確,或原始碼與 patch 不匹配。
16.6 疑難排解編譯與安裝問題 (Troubleshooting Compiles and Installations)#
閱讀 make 的輸出時,要區分真正的錯誤與可忽略的錯誤:
# 真正的錯誤
make: *** [target] Error 1
# 可忽略的錯誤
make: *** [target] Error 1 (ignored)在大型專案中,GNU make 會遞迴呼叫自身,錯誤訊息會標記 [N]。關鍵是找到第一個 make error 之前的 compiler error message。
16.6.1 常見編譯錯誤#
問題:型別衝突 (conflicting types)
src.c:22: conflicting types for 'item'
/usr/include/file.h:47: previous declaration of 'item'修正方式:移除或註解掉 src.c 中重複宣告的那一行。
問題:未宣告的識別字 (undeclared identifier)
src.c:37: 'time_t' undeclared (first use this function)修正方式:找出該型別所屬的 header file(透過 man 查詢),並在原始碼開頭加入對應的 #include。
問題:找不到 header file
src.c:4: pkg.h: No such file or directory修正方式:安裝缺少的開發套件,或透過 CPPFLAGS 加入 -I include 路徑。在 Debian 系統可用 apt-file search pkg.h 找到對應的套件。
問題:找不到指令 (Command not found)
make: prog: Command not found修正方式:安裝缺少的工具(如 cc、gcc、ld),或修改 Makefile 指定工具的完整路徑。
16.7 展望 (Looking Forward)#
本章只涵蓋了建置軟體的基礎。進一步學習的方向包括:
- 學習 CMake 和 SCons 等其他建置系統
- 為自己的軟體設定建置系統(可參考 Autotools, 2nd edition, John Calcote)
- 編譯 Linux kernel——其建置系統完全獨立,有自己的設定系統用於自訂 kernel 和 modules
- 探索發行版特有的 source packages,如 Debian 的
debuild和 RPM 的mock
建置軟體是學習程式開發的踏腳石。透過本章與前一章的工具知識,你可以進一步深入原始碼、進行修改,甚至創建自己的軟體。