[翻譯] go build 命令是如何工作的?

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

之前 Dave Cheney 已經為我們講解過了 Go 是如何用 go 編譯自己的。這裡,他繼續給大家講解一下 go build 命令是如何工作的(原文)。

————翻譯分隔線————

go build 命令是如何工作的?

本文以 Go 的標準庫為例,介紹了 Go 編譯過程的工作原理。

gc 工具鏈

本文將關注 gc 工具鏈。gc 工具鏈的名字來自 Go 的前端編譯器 cmd/gc,這主要是為了與 gccgo 工具鏈進行區分。當人們討論 Go 編譯器的時候,多半是指 gc 工具鏈。本文不關注 gccgo 工具鏈。

gc 工具鏈是直接從 Plan 9 的工具鏈剝離出來的。該工具鏈由一個 Go 編譯器、一個 C 編譯器、一個彙編器和一個連結器組成。可以在 Go 代碼的 src/cmd/ 子目錄找到這些工具,包括在所有實現下共用的前端,和在不同處理器架構下特定的後端。後端用特定的字母標識,這也是 Plan 9 的一個傳統。命令包括:

5g、6g 和 8g 是 .go 檔案的對應 arm、amd64 和 386 的編譯器;
5c、6c 和 8c 是 .c 檔案的對應 arm、amd64 和 386 的編譯器;
5a、6a 和 8a 是 .s 檔案的對應 arm、amd64 和 386 的編譯器;
5l、6l 和 8l 是用於上面命令產生的檔案的連結器,同樣對應 arm、amd64 和 386。

需要注意的是,每個命令都可以在任何被支援的平台上進行編譯,這也是 Go 的交叉編譯能力的一種體現。你可以在這篇文章裡瞭解到更多關於交叉編譯的情況。

構建包

構建一個 Go 包至少包含兩個步驟,編譯 .go 檔案,然後將編譯結果打包。考慮到 crypto/hmac 很小,只有一個源檔案和測試檔案,所以用其舉例說明。使用 -x 選項以告訴 go build 列印出它所執行的每一步

% go build -x crypto/hmacWORK=/tmp/go-build249279931mkdir -p $WORK/crypto/hmac/_obj/mkdir -p $WORK/crypto/cd /home/dfc/go/src/pkg/crypto/hmac/home/dfc/go/pkg/tool/linux_arm/5g -o $WORK/crypto/hmac/_obj/_go_.5 -p crypto/hmac -complete -D _/home/dfc/go/src/pkg/crypto/hmac -I $WORK ./hmac.go/home/dfc/go/pkg/tool/linux_arm/pack grcP $WORK $WORK/crypto/hmac.a $WORK/crypto/hmac/_obj/_go_.5

逐一瞭解這些步驟

WORK=/tmp/go-build249279931mkdir -p $WORK/crypto/hmac/_obj/mkdir -p $WORK/crypto/

go build 建立了一個臨時目錄 /tmp/go-build249279931 並且填充一些架構性的子目錄用於儲存編譯的結果。第二個 mkdir 可能是多餘的,已經建立了 issue 6538 來跟蹤這個問題。

cd /home/dfc/go/src/pkg/crypto/hmac/home/dfc/go/pkg/tool/linux_arm/5g -o $WORK/crypto/hmac/_obj/_go_.5 -p crypto/hmac -complete -D _/home/dfc/go/src/pkg/crypto/hmac -I $WORK ./hmac.go

go 工具切換 crypto/hmac 的原始碼目錄,並且調用架構對應的 go 編譯器,在本例中是 5g。實際上是沒有 cd 的,當 5g 執行時 /home/dfc/go/src/pkg/crypto/hmac 作為 exec.Command.Dir 的參數傳遞。這意味著為了讓命令列更加精簡,.go 源檔案可以使用對應其原始碼目錄的相對路徑。

編譯器產生唯一的一個臨時檔案 $WORK/crypto/hmac/_obj/_go_.5 將在最後一步中使用。

/home/dfc/go/pkg/tool/linux_arm/pack grcP $WORK $WORK/crypto/hmac.a $WORK/crypto/hmac/_obj/_go_.5

最後一步是打包目標檔案到將被連結器和編譯器使用的歸檔檔案 .a 中。

由於在包上調用了 go build ,$WORK 中的結果將在編譯結束後刪除。如果調用 go install -x 將會輸出額外的兩行

mkdir -p /home/dfc/go/pkg/linux_arm/crypto/cp $WORK/crypto/hmac.a /home/dfc/go/pkg/linux_arm/crypto/hmac.a

這示範了 go build 與 install 的不同之處; build 構建,install 構建並且安裝,以便用於其他構建。

構建更加複雜的包

你可能正在思考前面例子中的打包步驟。這裡的編譯器和連結器僅接受了單一的檔案作為包的內容,如果包含多個目標檔案,在使用它們之前,必須將其打包到一個單一的 .a 歸檔檔案中。

cgo 是一個常見的產生超過一個中間目標檔案的例子,不過它對於本文來說太過複雜了,這裡用包含 .s 彙編檔案的情況作為替代的例子,例如 crypto/md5。

% go build -x crypto/md5WORK=/tmp/go-build870993883mkdir -p $WORK/crypto/md5/_obj/mkdir -p $WORK/crypto/cd /home/dfc/go/src/pkg/crypto/md5/home/dfc/go/pkg/tool/linux_amd64/6g -o $WORK/crypto/md5/_obj/_go_.6 -p crypto/md5 -D _/home/dfc/go/src/pkg/crypto/md5 -I $WORK ./md5.go ./md5block_decl.go/home/dfc/go/pkg/tool/linux_amd64/6a -I $WORK/crypto/md5/_obj/ -o $WORK/crypto/md5/_obj/md5block_amd64.6 -D GOOS_linux -D GOARCH_amd64 ./md5block_amd64.s/home/dfc/go/pkg/tool/linux_amd64/pack grcP $WORK $WORK/crypto/md5.a $WORK/crypto/md5/_obj/_go_.6 $WORK/crypto/md5/_obj/md5block_amd64.6

這個例子執行在 linux/amd64 主機上,6g 被調用以編譯兩個 .go 檔案:md5.go 和 md5block_decl.go。後面這個包含一些用彙編實現的函數的聲明。

這時 6a 被調用以彙編 md5block_amd64.s。選擇哪個 .s 來編譯的邏輯在我之前的關於條件編譯的文章中進行了說明。

最後調用 pack 來打包 Go 的目標檔案 _go_.6,以及彙編目標檔案 md5block_amd64.6 到單一的一個歸檔檔案中。

構建命令

一個 Go 命令是一個命名為 main 的包。main 包,或者說命令的編譯方式與其他包一致,不過它們會在內部經過一些額外的步驟來連結成最終的可執行檔。讓我們通過 cmd/gofmt 來研究一下這個過程

% go build -x cmd/gofmtWORK=/tmp/go-build979246884mkdir -p $WORK/cmd/gofmt/_obj/mkdir -p $WORK/cmd/gofmt/_obj/exe/cd /home/dfc/go/src/cmd/gofmt/home/dfc/go/pkg/tool/linux_amd64/6g -o $WORK/cmd/gofmt/_obj/_go_.6 -p cmd/gofmt -complete -D _/home/dfc/go/src/cmd/gofmt -I $WORK ./doc.go ./gofmt.go ./rewrite.go ./simplify.go/home/dfc/go/pkg/tool/linux_amd64/pack grcP $WORK $WORK/cmd/gofmt.a $WORK/cmd/gofmt/_obj/_go_.6cd ./home/dfc/go/pkg/tool/linux_amd64/6l -o $WORK/cmd/gofmt/_obj/exe/a.out -L $WORK $WORK/cmd/gofmt.acp $WORK/cmd/gofmt/_obj/exe/a.out gofmt

前面的六行應該已經熟悉了,main 包會向其他 Go 包一樣編譯和打包。

不同之處在於倒數第二行,調用連結器產生二進位的可執行檔。

/home/dfc/go/pkg/tool/linux_amd64/6l -o $WORK/cmd/gofmt/_obj/exe/a.out -L $WORK $WORK/cmd/gofmt.a

最後一行複製並重新命名編譯後的二進位檔案到其最終的位置和名稱。如果使用了 go install,二進位也會被複製到 $GOPATH/bin(如果設定了 $GOBIN,則為 $GOBIN)。

曆史原因

如果你回到足夠久遠的時代,回到 go tool 之前,回到 Makefiles 的時代,你可以找到 Go 編譯過程的核心。這個例子來自 release.r60 的文檔

$ cat >hello.go <<EOFpackage mainimport "fmt"func main() {        fmt.Printf("hello, world\n")}EOF$ 6g hello.go$ 6l hello.6$ ./6.outhello, world

也就是這些,6g 編譯了 .go 檔案到 .6 目標檔案,6l 連結目標檔案以及 fmt (和運行時)包來產生二進位檔案 6.out。

結束語

在本文中,我們討論了 go build 的工作原理,並瞭解了 go install 對編譯結果處理的不同方式。

現在你已經知道 go build 是如何工作的了,以及如何通過 -x 展示編譯過程,可以嘗試傳遞這個標識到 go test 並且觀察其結果。

另外,如果已經在系統中安裝了 gccgo,可以向 go build 傳遞 -compiler gccgo,然後使用 -x 來瞭解 Go 代碼是如何用這個編譯器進行編譯的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.