這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
這篇文章基於2013年4月中旬我給雪梨Go使用者組分享中描述Go構建的部分。
在郵件組和IRC頻道中,經常有人詢問有關 Go編譯器,運行時和內部實現的文檔。目前關於 Go內部實現權威的文檔就是原始碼,我鼓勵大家都去看原始碼。之前已經說過,自從 Go1.0發布之後,Go構建的過程已經固定下來,所以這篇文章很可能維持一段時間。
這篇文章從原始碼開始,到充分測試的 Go安裝過程結束,講述了Go構建過程的九個步驟。為簡單起見,所有這裡提到的路徑都相對於簽出原始碼的根目錄 , $GOROOT/src 。
至於背景知識要求,你應該讀讀 golang.org上面的從原始碼安裝Go。
第一步, all.bash
% cd $GOROOT/src % ./all.bash
第一步有點虎頭蛇尾,因為 all.bash會調用另外兩個shell指令碼: make.bash和run.bash .如果你用 Windows或者Plan9 ,步驟是一樣的,只不過指令碼名稱分別以 .bat和.rc 結尾。以下敘述過程中,請用相應作業系統的副檔名進行替代。
第二步. make.bash
. ./make.bash --no-banner
make.bash 源於 all.bash ,因此調用exit會正常終止構建過程.make.bash 有三個主要功能, 第一個功能是驗證正在編譯Go的環境的完整性。完整性檢查在過去幾年內建立起來,目的主要是避免用已知的損壞了的工具構建或者在構建會失敗的環境中構建。
第三步. cmd/dist
gcc -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist cmd/dist/*.c
一旦完整性檢查完成,make.bash 會編譯 cmd/dist. cmd/dist 替代了Go1之前的基於Makefile的構建系統並管理pkg/runtime中少量的代碼產生。cmd/dist 是一個C程式,這使得它能夠利用系統的C編譯器和標頭檔來處理大多數的主機平台檢測問題.cmd/dist 總是會檢測主機的作業系統和架構, $GOHOSTOS and $GOHOSTARCH. 如果你是交叉編譯,這些變數的值可能會與設定的不一樣。實際上,Go構建過程就是在建立一個交叉編譯器,但大多數情況下宿主機和目標機平台是一致的。之後, make.bash 用引導參數調用 cmd/dist ,後者先編譯編譯器套件使用的支援庫 lib9, libbio 和 libmach, 然後編譯編譯器自身。 這些工具也是用c寫成的,用系統的c編譯器編譯。
echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH."buildall="-a"if [ "$1" = "--no-clean" ]; then buildall=""fi./cmd/dist/dist bootstrap $buildall -v # builds go_bootstrap
cmd/dist 用編譯器套件編譯了 go 工具 go_bootstrap 的一個版本。 go_bootstrap 是一個不完整的 go 工具,比如為了避免對cgo的依賴,去掉了 pkg/net 。由於cmd/dist 工具包含將要編譯的包或者庫及其依賴的資料夾清單,所以要格外小心避免引入新的 cmd/go 的構建依賴。
第四步. go_bootstrap
現在go_bootstrap 已經構建完成, make.bash 的最後一步是用go_bootstrap 編譯所有的Go標準庫包括一個完整版本的go 工具。
echo "# Building packages and commands for $GOOS/$GOARCH.""$GOTOOLDIR"/go_bootstrap install -gcflags "$GO_GCFLAGS" \ -ldflags "$GO_LDFLAGS" -v std
第五步. run.bash
現在 make.bash 已經執行完了。執行控制又回到 all.bash 。接下來會調用run.bash 。run.bash的作用是編譯並測試標準庫、運行時和語言測試套件。
bash run.bash --no-rebuild
使用–no-rebuild 是因為 make.bash 和 run.bash 都會調用 go install -a std, 。為了避免重複調用,–no-rebuild 會跳過第二次 go install。
# allow all.bash to avoid double-build of everythingrebuild=trueif [ "$1" = "--no-rebuild" ]; then shiftelse echo '# Building packages and commands.' time go install -a -v std echofi
第六步. go test -a std
echo '# Testing packages.' time go test std -short -timeout=$(expr 120 \* $timeout_scale)s echo
run.bash 接下來運行標準庫中所有包中的單元測試,這些單元測試用到了testing包。因為$GOPATH 和 $GOROOT 下的代碼屬於同一個命名空間,我們不能用go test …(這回測試$GOPATH中所有的包)。因此用一個別名std來聲明標準庫中的包,由於一些測試需要很長時間或者消耗很多記憶體,可以用-short標誌來過濾這些測試。
第七步. runtime and cgo tests
run.bash 下一步運行了一系列的針對支援cgo平台的測試,少量基準測試,編譯各種各樣的和Go一起發雜項程式。慢慢地,這種雜項程式越來越多。因為我們發現,這些雜項程式在構建過程中不可或缺。
第八步. go run test
(xcd ../testunset GOMAXPROCStime go run run.go) || exit $?
這是run.bash 的倒數第二個階段。調用位於$GOROOT test 檔案夾下編譯器和運行時的測試案例。這些都是關於編譯器和運行時自己的底層細節的測試案例。在測試語言規範的同時, test/bugs 和 test/fixedbugs 子檔案夾下的測試案例獨立運行以捕獲之前已經發現並修複的問題.所有這些測試的驅動是 $GOROOT/test/run.go ,它會執行test檔案夾下面每一個.go檔案。一些.go檔案會在第一行包含一些指令,這些指令會指示run.go ,比如退出程式或者拋出特定的輸出序列。
第九步. go tool api
echo '# Checking API compatibility.'go tool api -c $GOROOT/api/go1.txt,$GOROOT/api/go1.1.txt \ -next $GOROOT/api/next.txt -except $GOROOT/api/except.txt
run.bash 的最後一步就是調用 api 工具。 api工具的作用是在Go1 2012年發布時執行Go1 API的約定,Go1 API包括匯出符號、常量、函數、變數、類型、方法等等。 Go1把這些寫在 api/go1.txt中,Go 1.1寫在api/go1.1.txt 中。Go1.1之後,一個額外的檔案 api/next.txt 指明了標準庫和運行時新增符號。一旦Go1.2發布,這個檔案會成為Go1.2的約定,並且會有一個新的next.txtT。還有一個包含Go1 約定異常的小檔案 except.txt。修改這些檔案不可掉以輕心。
其他貼士和技巧
你可能已經發現make.bash 在不運行測試案例時構建Go很有用,而 run.bash 用來構建和測試Go運行時。區別就是:前者可以用於交叉編譯;如果你基於標準庫開發,後者很有協助。
更新: 感謝Russ Cox 和 Andrew Gerrand的反饋和建議。
英文原文:Dave Cheney,感謝@Codefor 的熱心翻譯。如果其他朋友也有不錯的原創或譯文,可以嘗試推薦給伯樂線上。
譯文連結:http://blog.jobbole.com/41054/
【非特殊說明,轉載必須在本文中標註並保留原文連結、譯文連結和譯者等資訊,謝謝合作!】