## 第?18?章?編譯工具鏈
**目錄**
[](ch18.html#id3083823)
[標準編譯安裝](ch18s02.html)
[為什么要編譯安裝](ch18s02.html#id3083834)
[編譯環境](ch18s02.html#id3083233)
[標準編譯安裝](ch18s02.html#id3083952)
[編譯過程](ch18s03.html)
[gcc 編譯器](ch18s04.html)
[自動化編譯](ch18s05.html)
[autoconf](ch18s05.html#id3084783)
[automake](ch18s05.html#id3084909)
[Makefile](ch18s05.html#id3084989)
[使用 make](ch18s06.html)
[emerge](ch18s07.html)
[設置 USE標記](ch18s07.html#id3085722)
[編譯選項](ch18s07.html#id3085838)
[微調](ch18s07.html#id3086063)
[使用 emerge](ch18s07.html#id3086226)
## 標準編譯安裝
### 為什么要編譯安裝
包管理系統是絕大多數發行版的必備組件,也是一個發行版區別于其它發行版的主要特征。但是有些軟件,并不能通過包管理系統安裝,這就需要下載源碼編譯安裝。
一個軟件可能有許多功能,但是發行版中提供的安裝包,通常只具有一些常見的功能。如果提供所有功能,那么無疑會占用更多的資源,而這些功能,大多數用戶不會用到;[[24](ch18s02.html#ftn.id3083206)]而你會用到的功能,可能安裝包中剛好沒有。編譯安裝可以靈活地定制軟件,選擇自己需要的,取消自己不需要的。
編譯安裝還可以針對特定的硬件進行優化,以獲得更好的性能表現。[[25](ch18s02.html#ftn.id3083225)]
### 編譯環境
編譯環境包括多個工具,它們環環相扣,稱作編譯工具鏈。主要包括以下工具:
| 工具 | 簡介 |
| --- | --- |
| binutils | 連接器、匯編器和其他用于目標文件和檔案的工具 |
| gcc | 編譯器,將源代碼轉換為機器代碼 |
| glibc | C庫,提供標準例程(C函數) |
還有一些工具,能夠調用工具鏈,實現自動化編譯:
| | |
| --- | --- |
| autoconf | 自動生成 `Makefile` 文件 |
| automake |
| make | 按照 `Makefile` 文件中的規則編譯程序 |
在后面的部分將分別介紹這些工具
### 標準編譯安裝
首先,下載源代碼,通常是壓縮包,如:`xxx.tar.gz` 或者 `xxx.tar.bz2`,解包:
| 壓縮包格式 | 命令 |
| --- | --- |
| .tar.gz | tar zxvf xxx.tgz |
| .tgz |
| .tar.bz2 | tar jxvf xxx.tar.bz2 |
通常解包后會在當前位置得到一個 `xxx/` 目錄,進入這個目錄
```
cd xxx/
```
使用下列命令編譯安裝:
```
./configure --prefix=/opt/xxx
make
sudo make install
make clean
```
> [](ch18s02.html#build-01) 配置軟件特性,檢查編譯環境,生成 `Makefile文件`
> [](ch18s02.html#build-05) 最常用配置選項:指定軟件的安裝路徑
> [](ch18s02.html#build-02) 根據 `Makefile` 編譯源代碼
> [](ch18s02.html#build-03) 將編譯完成的程序安裝到系統中。通常需要 root權限
> [](ch18s02.html#build-04) 清除源代碼目錄中的編譯結果
* * *
> [[24](ch18s02.html#id3083206)] Windows 系統下的一些經典軟件,如 ACDsee、Nero、Winamp 等,集成了越來越多的功能,使它們越來越臃腫。而且不能夠只選擇自己喜歡的功能,要么全盤接收,要么改尋它途
> [[25](ch18s02.html#id3083225)] 通常發行版提供的安裝包,已經進行了優化。自己編譯的軟件,性能未必更好
## 編譯過程
將下面代碼保存為 `Hello.c`:
```
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
```
> [](ch18s03.html#build-20) **printf()** 函數
執行命令 `cc Hello.c`[[26](ch18s03.html#ftn.id3084190)],得到一個可執行文件 `a.out`,執行它 `./a.out`
可以看到,C的源代碼(`Hello.c`)是純文本,不能夠直接執行。可執行代碼是計算機的本機語言或機器語言表示的代碼,這種語言是由數字代碼表示的詳細指令組成,不同的計算機具有不同的機器語言。
編譯器是一個程序,其工作是將源代碼轉換為可執行代碼。
* 編譯器用來將 C語言 轉換成特定的機器語言。
* 編譯器還從C的庫中向最終程序加入代碼。[[27](ch18s03.html#ftn.id3084254)]
* 編譯器還檢查源代碼是否為有效的C語言程序。如果編譯器發現錯誤,將報告錯誤,而且不生成可執行文件
編譯器分三步完成這個工作:
| | |
| --- | --- |
| 預處理 | 調用預處理器 cpp 對源代碼文件中的文件包含(include)、預編譯語句(如宏定義 define 等) 進行分析 |
| 編譯 | 調用編譯器 cc 將源代碼轉換為中間代碼 |
| 鏈接 | 調用鏈接器 ld 將中間代碼與其它代碼結合起來生成可執行文件 |
* 這種方法使用程序便于模塊化。分別編譯各個模塊,然后使用鏈接器將編譯過的模塊結合起來。這樣,如果需要改變一個模塊,則不必重新編譯所有其它模塊。
可執行文件包含`目標文件`、`庫例程`和`啟動代碼`
編譯器將源代碼轉換為機器語言代碼(中間代碼),將結果放置在目標文件(`*.o`)中。雖然目標文件包含機器代碼,但該文件還不能運行,它還不是一個完整的程序。
啟動代碼(start-up code)相當于程序和操作系統之間的接口。[[28](ch18s03.html#ftn.id3084399)]
庫例程為函數的實現。幾乎所有C程序都利用標準C庫中所包含的例程,目標代碼文件不包含這一函數的代碼,它只包含調用函數的指令。實際代碼存儲在一個稱為“庫”的文件中。庫文件中包含許多函數的目標代碼
鏈接器的作用是將這3個元素(目標代碼、系統的標準啟動代碼和庫代碼[[29](ch18s03.html#ftn.id3084420)])結合在一起,并將它們存放在可執行文件中。
* * *
> [[26](ch18s03.html#id3084190)] 在 Linux 系統中,編譯器為gcc,cc為它的鏈接
> [[27](ch18s03.html#id3084254)] 庫中包含許多標準例程供您使用,例如**printf()**。更準確的說,是一個被稱為鏈接器(linker)的程序將庫例程引入的,但在多數系統上,編譯器為您運行鏈接器。
> [[28](ch18s03.html#id3084399)] 硬件相同的情況下,在 DOS 或 Linux 下可以使用同樣的目標代碼,但 DOC 與 Linux 要使用不同的啟動代碼,因為這兩種系統處理程序的方式是不同的。
> [[29](ch18s03.html#id3084420)] 程序有兩種方法來使用這些庫函數,如果靜態連接一個程序,這些函數就會被復制到可執行程序中,這就是`lib*.a`函數庫的作用
> 如果你動態的連接一個程序(默認),那么當程序運行時需要庫中的代碼,它就會調用`lib*.so`中的內容。
## gcc 編譯器
gcc 是 GNU 推出的功能強大、性能優越的多平臺編譯器,是 GNU 的代表作品之一。它能將C、C++語言源程序、匯編語言源程序和目標程序編譯、鏈接成可執行文件,如果沒有給出可執行文件的名字,gcc 將生成一個名為 `a.out` 的文件。
gcc 通過后綴來區分輸入文件的類型:
| 后綴 | 類型 |
| --- | --- |
| .c | C語言源代碼文件 |
| .a | 由目標文件構成的檔案庫文件 |
| .C|.cc|.cxx | C++源代碼文件 |
| .h | 程序所包含的頭文件 |
| .i | 預處理過的C源代碼文件 |
| .ii | 預處理過的C++源代碼文件 |
| .m | Objective-C源代碼文件 |
| .o | 編譯后的目標文件 |
| .s | 匯編語言源代碼文件 |
| .S | 預編譯的匯編語言源代碼文件 |
前面我們已經使用 gcc 編譯了一個程序:`cc Hello.c`
gcc 還有許多選項:
| | |
| --- | --- |
| -c | 只編譯,不鏈接成為可執行文件 |
| -o 文件名 | 設定輸出文件名。默認為`a.out` |
| -g | 加入調試符號(默認)。 |
| -O | 編譯、鏈接時進行優化,耗時比較多,但產生的可執行文件執行效率更高 |
| -O2 | 更高的優化級別,耗時更多 |
> [](ch18s04.html#build-21) 可以使用 gdb 進行調試使用下面的命令去掉調試符號:
> ```
> strip --strip-unneeded a.out
> strip --strip-debug a.out
> ```
> 不要在庫文件上使用 **--strip-unneeded**
## 自動化編譯
在前面的[標準編譯安裝](ch18s02.html#std-build)中,第一步是`./configure`[[30](ch18s05.html#ftn.id3084729)],它會根據`Makefile.in`生成`Makefile`文件,然后make根據`Makefile`自動編譯軟件
通常在一個源碼包中,已經包含了`configure`腳本和`Makefile`文件,作為課外知識,我們大致了解一下怎么生成這兩個文件
### autoconf
autoconf用來生成`configure`腳本,它可以檢查系統特性、編譯環境、環境變量、軟件參數、依賴關系等
autoconf需要用到 m4
1. 用autoscan描源代碼目錄生成`configure.scan`文件
2. 將`configure.scan`改名為`configure.in`
3. 用aclocal根據`configure.in`文件的內容,自動生成`aclocal.m4`文件
4. 使用autoconf,根據`configure.in`和`aclocal.m4`來產生`configure`文件
### automake
automake可以從`Makefile.am`文件自動生成`Makefile.in`,它主要用來配置源代碼
automake需用到perl
* 手工寫`Makefile.am`
* 使用`automake`,根據`configure.in`和`Makefile.am`來產生`Makefile.in`
### Makefile
使用`configure`腳本,配合`Makefile.in`可以生成`Makefile`文件,然后用make自動化的編譯軟件
這里有一張生成`Makefile`的流程圖:

`Makefile`的用途不只是編譯軟件,還可以利用它完成一些瑣碎的工作,只要最后輸出一個文件,都可以用make來完成
這是一個最簡單的`Makefile`
```
filelist:*
ls -lF > filelist
```
> [](ch18s05.html#build-31) 輸出的目標文件,不能省略。如果有多個文件,可以使用**all**
> [](ch18s05.html#build-32) 分隔符,不能省略
> [](ch18s05.html#build-33) 輸入文件,可以省略
> [](ch18s05.html#build-34) 這一行必須以`TAB`字符起始,不能使用空格代替
> [](ch18s05.html#build-35) make的命令
可以使用變量代替命令,便于維護
```
TARGET = filelist
SOURCE = *
ARG = -lF
APPLICATION = ls
$(TARGET):$(SOURCE)
$(APPLICATION) $(ARG) $(SOURCE) > $(TARGET)
```
> [](ch18s05.html#build-41) 定義變量,傳統上用大寫
> [](ch18s05.html#build-42) 使用變量寫`Makefile`
`Makefile`可以有多個目標文件,我們前面提到,gcc編譯時先生成目標文件,再把目標文件鏈接成可執行文件,Makefile應該是這樣的:
```
OBJECTS = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
exe : $(OBJECTS)
cc -o exe $(OBJECTS)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm exe $(OBJECTS)
```
> [](ch18s05.html#build-51) 如果寫在多行,要用脫字符換行
> [](ch18s05.html#build-52) 如何生成中間文件
> [](ch18s05.html#build-53) 偽目標文件,`make clean`并不生成`clean`文件,而是清理編譯結果
`Makefile`還有很多強大的機制,這里就不詳細介紹了
* * *
> [[30](ch18s05.html#id3084729)] 執行當前目錄下的`configure`腳本
## 使用 make
利用 `configure`所產生的`Makefile`文件有幾個預先設定的目標可供使用:
| 目標 | 用途 |
| --- | --- |
| make all | 產生預設的目標,只敲入`make`也可以 |
| make clean | 清除編譯結果 |
| make distclean | 除了清除編譯結果,也把configure所產生的 `Makefile` 清除掉 |
| make install | 將程序安裝到系統中 |
| make dist | 將程序和相關的文檔打包為一個壓縮文檔以供發布 |
| make distcheck | 打包并檢驗 |
## emerge
雖然我們能夠使用autoconf、automake、make等工具實現自動化編譯,但這種針對單個軟件包的編譯系統,在編譯多個軟件時仍然十分繁瑣
假設需要編譯emacs和vim,使用 xft字體、圖形界面支持,并去掉調試符號,需要分別作如下配置
```
emacs
./configure --prefix=/usr/local/ \
`--no-debug` \
`--with-xft --with-x-toolkit=gtk` \
--with-freetype
vim
./configure --prefix=/usr/local/ \
`--no-debug` \
`--with-xft --with-x-toolkit=gtk`
```
這就像點菜時,你必須告訴廚師:魚香肉絲(多放辣椒、不放蒜)、宮保雞丁(多放辣椒、不放蒜)、麻婆豆腐(多放辣椒、不放蒜)……
實際上,大多數人這樣點菜:魚香肉絲、宮保雞丁、麻婆豆腐……多放辣椒、不放蒜
emerge就是這樣一種點菜方式,它是gentoo的包管理系統,提供了更為現代化的編譯方式
可以通過指定USE標記`xft`、`gtk`、`-debug`來確定所有軟件的編譯方式
### 設置 USE標記
以下方法按優先級由低到高排列:
`/etc/make.profile/`目錄是一個符號鏈接,里面包含一些`make.defaults`文件,放置開發者設置的 USE標記[[31](ch18s07.html#ftn.id3085747)]:
```
/usr/portage/profile/base/make.defaults
/usr/portage/profile/default-linux/make.defaults
/usr/portage/profile/default-linux/x86/make.defaults
/usr/portage/profile/default-linux/x86/2008.0/make.defaults
```
在`/etc/make.conf`文件中聲明永久 USE標記(推薦)
```
USE="nptl nptlonly nls cjk php mysql -kde -qt3 -qt4"
```
* 帶`-`的 USE標記,表示排除
在`/etc/portage/package.use`文件中為單個包聲明 USE標記
```
app-editors/emacs-cvs xft
www-servers/lighttpd fastcgi
dev-lang/php mysqli cgi gd ctype pcre session unicode pic posix
dev-db/phpmyadmin vhosts
app-shells/zsh doc
net-ftp/pure-ftpd -ldap mysql pam ssl vchroot
```
使用環境變量聲明臨時 USE標記
```
USE="-java"
emerge seamonkey
```
查看使用的 USE標記:
```
merge --pretend --verbose seamonkey
Calculating dependencies ...done!
[ebuild R ] www-client/seamonkey-1.0.7 USE="crypt gnome java -debug -ipv6
-ldap -mozcalendar -mozdevelop -moznocompose -moznoirc -moznomail -moznopango
-moznoroaming -postgres -xinerama -xprint" 0 kB
```
### 編譯選項
`/etc/make.conf`
```
CFLAGS="-O2 -march=i686 -pipe"
CXXFLAGS="-O2 -march=i686 -pipe"
CHOST="i686-pc-linux-gnu"
MAKEOPTS="-j2"
FEATURES="parallel-fetch ccache"
CCACHE_DIR="/var/tmp/ccache"
CCACHE_SIZE="2G"
ACCEPT_KEYWORDS="x86"
USE="nptl nptlonly nls cjk php mysql"
FETCHCOMMAND="/usr/bin/axel -a -n4 \${URI} -o \${DISTDIR}"
RESUMECOMMAND="/usr/bin/axel -a -n4 \${URI} -o \${DISTDIR}"
```
> [](ch18s07.html#build-e21) 針對C語言的優化選項,**-march=**設置目標架構
> [](ch18s07.html#build-e22) 針對C++語言的優化選項
> [](ch18s07.html#build-e23) 進行編譯工作的機器架構
> [](ch18s07.html#build-e24) 編譯選項
> [](ch18s07.html#build-e25) emerge 特性。并行下載、使用 ccache 緩沖編譯結果
> [](ch18s07.html#build-e26) ccache 緩存目錄
> [](ch18s07.html#build-e27) ccache 緩存大小
> [](ch18s07.html#build-e28) 通過關鍵字選擇分支。`x86`表示 x86 架構的穩定分支,`~x86`表示 x86 架構的不穩定分支
> [](ch18s07.html#build-e29) USE標記
> [](ch18s07.html#build-e30) 使用axel加速下載
gentoo支持多種架構:x86、 sparc、 amd64、 ppc、 ppc64、 alpha、 hppa、 mips、 ia64、 arm,我們使用的 PC 多為 x86 架構
假設你主要使用 x86 穩定分支,但少數軟件要使用最新版本,在`/etc/portage/package.keywords`文件中為單個包設置關鍵字
```
app-editors/emacs-cvs ~x86
x11-misc/emacs-desktop ~x86
app-i18n/fcitx ~x86
app-editors/vim x86
app-editors/vim-core x86
media-video/mplayer ~x86
media-libs/win32codecs ~x86
app-i18n/man-pages-zh_CN ~x86
```
### 微調
在`/etc/portage/`目標下包含一些文件,可以在軟件包級別上進行調節。前面已經介紹了`package.use`和`package.keywords`
package.keywords
還未被確認適合你的系統或架構,但是你希望能安裝的軟件包
package.use
特定軟件包而不是整個系統使用的 USE標記
package.provided
屏蔽的軟件包(需要指明版本號)
package.mask
永遠不希望 Portage 安裝的軟件包。
package.unmask
被 Gentoo 開發者屏蔽的軟件包,但是你希望能安裝的軟件包。
軟件包可能由于以下原因被屏蔽
| | |
| --- | --- |
| ~架構 keyword | 意味著這個軟件沒有經過充分的測試,不能進入穩定分支,請等待一段時間后在嘗試使用它 |
| -架構 keyword 或 -* keyword | 意味著這個軟件不能工作在您機器的體系結構中 |
| missing keyword | 意味著這個軟件還沒有在您機器的體系結構中進行過測試 |
| package.mask | 意味著這個軟件被認為是損壞的,不穩定的或者有更嚴重的問題,它被故意標識為“不應使用” |
| profile | 意味著這個軟件不適用于您的 profile。安裝這樣的應用軟件可能會破壞您的系統,或者只是不能與您使用的 profile 相兼容 |
例如:
```
gnome-base/gnome-2.8.0_pre1 (masked by: ~x86 keyword)
lm-sensors/lm-sensors-2.8.7 (masked by: -sparc keyword)
sys-libs/glibc-2.3.4.20040808 (masked by: -* keyword)
dev-util/cvsd-1.0.2 (masked by: missing keyword)
games-fps/unreal-tournament-451 (masked by: package.mask)
sys-libs/glibc-2.3.2-r11 (masked by: profile)
```
### 使用 emerge
> 注意:本部分內容來源于[gentoo 中文手冊](http://www.gentoo.org/doc/zh_cn/handbook/)
**查找.?**
查找名字包含 pdf 的軟件包
```
emerge --search pdf
```
查找與 pdf 相關的軟件包
```
emerge --searchdesc pdf
emerge -S pdf
```
查看軟件擁有的 USE標記
```
emerge -vp 軟件包名稱
```
**管理.?**
安裝軟件包
```
emerge 軟件包名稱
```
模擬安裝軟件包
```
emerge --pretend 軟件包名稱
```
下載軟件包的源代碼包
```
emerge --fetchonly 軟件包名稱
```
從系統中刪除軟件包
```
emerge --unmerge 軟件包名稱
```
**更新.?**
更新系統
```
emerge --update --ask world
emerge -ua world
```
更新整個系統
```
emerge --update --deep world
emerge -uD world
```
使用新的 USE標記 重新構建系統
```
emerge --update --deep --newuse world
emerge -uDN world
```
移除孤立依賴的軟件包
```
emerge --update --deep --newuse world
emerge --depclean
revdep-rebuild
```
> [](ch18s07.html#build-e31) 重新構建系統
> [](ch18s07.html#build-e32) 清除孤立依賴包
> [](ch18s07.html#build-e33) 重新構建依賴關系
revdep-rebuild工具由gentoolkit包提供;使用前別忘了首先 emerge 它:
```
emerge gentoolkit
```
* * *
> [[31](ch18s07.html#id3085747)] 在升級 Portage 的時候,這些文件將會被覆蓋,請不要在這里設置
- 開源世界旅行手冊
- 授權
- 致謝
- 序言
- 更新紀錄
- 導讀
- 如何寫作科技文檔
- 部分?I.?氣候
- 第?1?章?GUI? CLI?
- 第?2?章?UNIX 縮寫風格
- 第?3?章?版本號的迷霧
- 第?4?章???Vim 還是 Emacs
- 第?5?章???DocBook 還是 TeX
- 第?6?章?完全用 Gnu/Linux 工作
- 第?7?章?病毒
- 第?8?章?磁盤 分區
- 第?9?章?文件系統
- 第?10?章???發行版介紹
- 第?11?章???編程語言
- 第?12?章?無根的根:無名師的 Unix 心傳
- 部分?II.?地理
- 第?13?章?基礎知識
- 第?14?章?命令系統
- 第?15?章?基本系統
- 第?16?章?軟件管理
- 第?17?章?核心工具集
- 第?18?章?編譯工具鏈
- 第?19?章?圖形界面
- 第?20?章?國際化
- 第?21?章???內核
- 第?22?章?Grub
- 第?23?章?服務器
- 第?24?章?Vim 編輯器
- 第?25?章?Emacs 入門
- 第?26?章?正則表達式
- 第?27?章?docbook 指南
- 第?28?章?Git 版本控制系統
- 第?29?章?ConTeXt 入門指南
- 部分?III.?景觀
- 第?30?章?終極 Shell -- ZSH
- 第?31?章?完美工作站 Archlinux
- 第?32?章?組織你的意念:Emacs org mode
- 第?33?章???Zsh+screen
- 第?34?章???gentoo stage3
- 第?35?章???硬件問題
- 第?36?章???網絡設置
- 第?37?章???自制 LiveCD
- 第?38?章?awesome
- 第?39?章?openbox 工作環境
- 第?40?章???Emacs muse
- 第?41?章???寫作工具鏈
- 第?42?章?使用 lftp
- 第?43?章???Firefox 使用技巧
- 第?44?章???FVWM
- 部分?IV.?地質
- 第?45?章?Unix
- 第?46?章???Gnu
- 第?47?章?軟件業自由之神——Richard Stallman
- 第?48?章?Linux
- 第?49?章?GNOME與KDE的戰爭
- 第?50?章???Vim Emacs
- 第?51?章???年代紀
- 第?52?章?我的選擇
- 第?53?章???補遺