【Go命令教程】2. go build

go build 命令用於編譯咱們 指定的  源碼文件代碼包 以及它們的依賴包html

例如,若是咱們在執行 go build 命令時不後跟任何代碼包,那麼命令將試圖編譯當前目錄所對應的代碼包。例如,咱們想編譯 goc2p 項目的代碼包 logging。其中一個方法是進入 logging 目錄並直接執行該命令:linux

hc@ubt:~/golang/goc2p/src/logging$ go build

由於在代碼包 logging 中只有庫源碼文件和測試源碼文件,因此在執行 go build 命令以後不會在當前目錄和 goc2p 項目的 pkg 目錄中產生任何文件。golang

插播:Go 語言的源碼文件有三大類,即:命令源碼文件庫源碼文件 和 測試源碼文件。他們的功用各不相同,而寫法也各有各的特色。命令源碼文件老是做爲可執行的程序的入口。庫源碼文件通常用於集中放置各類待被使用的程序實體(全局常量、全局變量、接口、結構體、函數等等)。而測試源碼文件主要用於對前兩種源碼文件中的程序實體的功能和性能進行測試。另外,後者也能夠用於展示前二者中程序的使用方法。windows

另一種編譯 logging 包的方法是:併發

hc@ubt:~/golang/goc2p/src$ go build logging

在這裏,咱們把代碼包 logging 的導入路徑做爲參數傳遞給 go build 命令。另外一個例子:若是咱們要編譯代碼包 cnet/ctcp,只須要在任意目錄下執行命令 go build cnet/ctcp 便可。app

插播:之因此這樣的編譯方法能夠正常執行,是由於咱們已經在環境變量 GOPATH 中加入了 goc2p 項目的根目錄(即 ~/golang/goc2p/)。這時,goc2p 項目的根目錄就成爲了一個工做區目錄。只有這樣,Go 語言才能正確識別咱們提供的 goc2p 項目中某個代碼包的導入路徑。而代碼包的導入路徑是指,相對於 Go 語言自身的源碼目錄(即 $GOROOT/src)或咱們在環境變量 GOPATH 中指定的某個目錄的 src 子目錄下的子路徑。例如,這裏的代碼包 logging 的絕對路徑是 ~/golang/goc2p/src/logging。而不論 goc2p 項目的根文件夾被放在哪兒,logging 包的導入路徑都是 logging。顯而易見,咱們在稱呼一個代碼包的時候老是以其導入路徑做爲其稱謂。tcp

言歸正傳,除了上面的簡單用法,咱們還能夠同時編譯多個 Go 源碼文件:編輯器

hc@ubt:~/golang/goc2p/src$ go build logging/base.go logging/console_logger.go logging/log_manager.go logging/tag.go
# 編譯多個go文件
#

可是,使用這種方法會有一個限制。做爲參數的多個 Go 源碼文件必須在同一個目錄中。也就是說,若是咱們想用這種方法既編譯 logging 包又編譯 basic 包是不可能的。不過別擔憂,在須要的時候,那些被編譯目標依賴的代碼包會被 go build 命令自動的編譯。例如,若是有一個導入路徑爲 app 的代碼包,同時依賴了 logging 包和 basic 包。那麼在執行 go build app 的時候,該命令就會自動的在編譯app包以前去檢查 logging 包和 basic 包的編譯狀態。若是發現它們的編譯結果文件不是最新的,那麼該命令就會先去的編譯這兩個代碼包,而後再編譯 app 包。函數

注意,go build 命令在編譯只包含庫源碼文件的代碼包(或者同時編譯多個代碼包)時,只會作檢查性的編譯,而不會輸出任何結果文件。工具

另外,go build 命令既不能編譯包含多個命令源碼文件的代碼包,也不能同時編譯多個命令源碼文件。由於,若是把多個命令源碼文件做爲一個總體看待,那麼每一個文件中的 main 函數就屬於重名函數,在編譯時會拋出重複定義錯誤。假如,在 goc2p 項目的代碼包 cmd(此代碼包僅用於示例目的,並不會永久存在於該項目中)中包含有兩個命令源碼文件 showds.go 和 initpkg_demo.go,那麼咱們在使用 go build 命令同時編譯它們時就會失敗。示例以下:

hc@ubt:~/golang/goc2p/src/cmd$ go build showds.go initpkg_demo.go
# command-line-arguments
./initpkg_demo.go:19: main redeclared in this block
        previous declaration at ./showds.go:56

請注意上面示例中的 command-line-arguments。在這個位置上應該顯示的是做爲編譯目標的源碼文件所屬的代碼包的導入路徑。可是,這裏顯示的並非它們所屬的代碼包的導入路徑 cmd。這是由於,命令程序在分析參數的時候若是發現第一個參數是 Go 源碼文件而不是代碼包,則會在內部生成一個虛擬代碼包。這個虛擬代碼包的導入路徑和名稱都會是 command-line-arguments。在其餘基於編譯流程的命令程序中也有與之一致的操做,好比 go install 命令和 go run 命令。

另外一方面,若是咱們編譯的多個屬於 main 包的源碼文件中沒有 main 函數的聲明,那麼就會使編譯器當即報出 「未定義 main 函數聲明」 的錯誤並停止編譯。換句話說,在咱們同時編譯多個 main 包的源碼文件時,要保證其中有且僅有一個 main 函數聲明,不然編譯是沒法成功的。

如今咱們使用 go build 命令編譯單一命令源碼文件。咱們在執行命令時加入一個標記 -v。這個標記的意義在於可使命令把執行過程當中構建的包名打印出來。咱們會在稍後對這個標記進行詳細說明。如今咱們先來看一個示例:

hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg_demo.go
hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -v initpkg_demo.go
command-line-arguments
hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg_demo  initpkg_demo.go

咱們在執行命令 go build -v initpkg_demo.go 以後被打印出的 command-line-arguments」 就是命令程序爲命令源碼文件 initpkg_demo.go 生成的虛擬代碼包的包名。順帶說一句,

命令 go build 會把編譯命令源碼文件後生成的結果文件存放到執行該命令時所在的目錄下。這個所說的結果文件就是與命令源碼文件對應的可執行文件。它的名稱會與命令源碼文件的主文件名相同。

順便說一下,若是咱們有多個聲明爲屬於 main 包的源碼文件,且其中只有一個文件聲明瞭 main 函數的話,那麼是可使用 go build 命令同時編譯它們的。在這種狀況下,不包含 main 函數聲明的那幾個源碼文件會被視爲庫源碼文件(理所固然)。如此編譯以後的結果文件的名稱將會與咱們指定的編譯目標中最左邊的那個源碼文件的主文件名相同。

其實,除了讓 Go 語言編譯器自行決定可執行文件的名稱,咱們還能夠自定義它。示例以下:

hc@ubt:~/golang/goc2p/src/basic/pkginit$ go build -o initpkg initpkg_demo.go
hc@ubt:~/golang/goc2p/src/basic/pkginit$ ls
initpkg    initpkg_demo.go

使用 -o 標記能夠指定輸出文件(在這個示例中指的是可執行文件)的名稱。它是最經常使用的一個 go build 命令標記。但須要注意的是,當使用標記 -o 的時候,不能同時對多個代碼包進行編譯。

標記 -i 會使 go build 命令安裝那些編譯目標依賴的且還未被安裝的代碼包。這裏的安裝意味着產生與代碼包對應的歸檔文件,並將其放置到當前工做區目錄的 pkg 子目錄的相應子目錄中。在默認狀況下,這些代碼包是不會被安裝的。

除此以外,還有一些標記不但受到 go build 命令的支持,並且對於後面會提到的 go install、go run、go test 等命令一樣是有效的。下表列出了其中比較經常使用的標記。

表0-1 go build命令的經常使用標記說明

標記名稱 標記描述
-a 強行對全部涉及到的代碼包(包含標準庫中的代碼包)進行從新構建,即便它們已是最新的了。
-n 打印編譯期間所用到的其它命令,可是並不真正執行它們。
-p n 指定編譯過程當中執行各任務的並行數量(確切地說應該是併發數量)。在默認狀況下,該數量等於CPU的邏輯核數。可是在 darwin/arm 平臺(即 iPhone 和 iPad 所用的平臺)下,該數量默認是1。
-race 開啓競態條件的檢測。不過此標記目前僅在 linux/amd6四、freebsd/amd6四、darwin/amd64和windows/amd64 平臺下受到支持。
-v 打印出那些被編譯的代碼包的名字。
-work 打印出編譯時生成的臨時工做目錄的路徑,並在編譯結束時保留它。在默認狀況下,編譯結束時會刪除該目錄。
-x 打印編譯期間所用到的其它命令。注意它與-n標記的區別。

咱們在這裏忽略了一些並不經常使用的或做用於編譯器或鏈接器的標記。在本小節的最後將會對這些標記進行簡單的說明。若是讀者有興趣,也能夠查看 Go 語言的官方文檔以獲取相關信息。

下面咱們就用其中幾個標記來查看一下在構建代碼包logging時建立的臨時工做目錄的路徑:

hc@ubt:~/golang/goc2p/src$ go build -v -work logging
WORK=/tmp/go-build888760008
logging

上面命令的結果輸出的第一行是爲了編譯 logging 包,Go 建立的一個臨時工做目錄,這個目錄被建立到了 Linux 的臨時目錄下。輸出的第二行是對標記 -v 的響應。這意味着這次命令執行時僅編譯了logging 包。關於臨時工做目錄的用途和內容,咱們會在講解 go run 命令和 go test 命令的時候詳細說明。

如今咱們再來看看若是強制從新編譯會涉及到哪些代碼包:

hc@ubt:~/golang/goc2p/src$ go build -a -v -work logging
WORK=/tmp/go-build929017331
runtime
errors
sync/atomic
math
unicode/utf8
unicode
sync
io
syscall
strings
time
strconv
reflect
os
fmt
log
logging

怎麼會多編譯了這麼多代碼包呢?能夠肯定的是,代碼包 logging 中的代碼直接依賴了標準庫中的 runtime 包、strings 包、fmt 包和 log 包。那麼其餘的代碼包爲何也會被從新編譯呢?

從代碼包編譯的角度來講,若是代碼包A依賴代碼包 B,則稱代碼包 B 是代碼包 A 的依賴代碼包(如下簡稱依賴包),代碼包 A 是代碼包 B 的觸發代碼包(如下簡稱觸發包)。

go build 命令在執行時,編譯程序會先查找目標代碼包的全部依賴包,以及這些依賴包的依賴包,直至找到最深層的依賴包爲止。在此過程當中,若是發現有循環依賴的狀況,編譯程序就會輸出錯誤信息並當即退出。此過程完成以後,全部的依賴關係也就造成了一棵含有重複元素的依賴樹。對於依賴樹中的一個節點(代碼包)來講,它的直接分支節點(前者的依賴包),是按照代碼包導入路徑的字典序從左到右排列的。最左邊的分支節點會最早被編譯。編譯程序會依此設定每一個代碼包的編譯優先級。

執行 go build 命令的計算機若是擁有多個邏輯 CPU 核心,那麼編譯代碼包的順序可能會存在一些不肯定性。可是,它必定會知足這樣的約束條件:依賴代碼包 -> 當前代碼包 -> 觸發代碼包。

標記 -p n 能夠限制編譯過程當中任務執行的併發數量,n 默認爲當前計算機的 CPU 邏輯核數。若是在執行 go build 命令時加入標記 -p 1,那麼就能夠保證代碼包編譯順序嚴格按照預先設定好的優先級進行。如今咱們再來編譯 logging 包:

hc@ubt:~/golang/goc2p/src$ go build -a -v -work -p 1 logging
WORK=/tmp/go-build114039681
runtime
errors
sync/atomic
sync
io
math
syscall
time
os
unicode/utf8
strconv
reflect
fmt
log
unicode
strings
logging

咱們能夠認爲,以上示例中所顯示的代碼包的順序,就是 logging 包直接或間接依賴的代碼包按照優先級從高到低排列後的排序。

另外,若是在命令中加入標記 -n,那麼編譯程序只會輸出所用到的命令而不會真正運行。在這種狀況下,編譯過程不會使用併發模式。

在本節的最後,咱們對一些並不太經常使用的標記進行簡要的說明:

  • -asmflags

此標記能夠後跟另一些標記,如 -D、-I、-S 等。這些後跟的標記用於控制 Go 語言編譯器編譯彙編語言文件時的行爲。

  • -buildmode

此標記用於指定編譯模式,使用方式如 -buildmode=default(這等同於默認狀況下的設置)。此標記支持的編譯模式目前有6種。藉此,咱們能夠控制編譯器在編譯完成後生成靜態連接庫(即 .a 文件,也就是咱們以前說的歸檔文件)、動態連接庫(即 .so 文件)或/和可執行文件(在 Windows 下是 .exe 文件)。

  • -compiler

此標記用於指定當前使用的編譯器的名稱。其值能夠爲 gc 或 gccgo。其中,gc 編譯器即爲 Go 語言自帶的編輯器,而 gccgo 編譯器則爲 GCC 提供的 Go 語言編譯器。而 GCC 則是 GNU 項目出品的編譯器套件。GNU 是一個衆所周知的自由軟件項目。在開源軟件界不該該有人不知道它。好吧,若是你確實不知道它,趕忙去 google 吧。

  • -gccgoflags

此標記用於指定須要傳遞給 gccgo 編譯器或連接器的標記的列表。

  • -gcflags

此標記用於指定須要傳遞給 go tool compile 命令的標記的列表。

  • -installsuffix

爲了使當前的輸出目錄與默認的編譯輸出目錄分離,可使用這個標記。此標記的值會做爲結果文件的父目錄名稱的後綴。其實,若是使用了 -race 標記,這個標記會被自動追加且其值會爲 race。若是咱們同時使用了 -race 標記和 -installsuffix,那麼在 -installsuffix 標記的值的後面會再被追加 _race,並以此來做爲實際使用的後綴。

  • -ldflags

此標記用於指定須要傳遞給 go tool link 命令的標記的列表。

  • -linkshared

此標記用於與 -buildmode=shared 一同使用。後者會使做爲編譯目標的非 main 代碼包都被合併到一個動態連接庫文件中,而前者則會在此之上進行連接操做。

  • -pkgdir

使用此標記能夠指定一個目錄。編譯器會只從該目錄中加載代碼包的歸檔文件,並會把編譯可能會生成的代碼包歸檔文件放置在該目錄下。

  • -tags

此標記用於指定在實際編譯期間須要受理的編譯標籤(也可被稱爲編譯約束)的列表。這些編譯標籤通常會做爲源碼文件開始處的註釋的一部分,例如,在 $GOROOT/src/os/file_posix.go 開始處的註釋爲:

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows

最後一行註釋即包含了與編譯標籤有關的內容。你們能夠查看代碼包 go/build 的文檔已得到更多的關於編譯標籤的信息。

  • -toolexec

此標記可讓咱們去自定義在編譯期間使用一些 Go 語言自帶工具(如 vet、asm 等)的方式。

 

 

摘自:

http://wiki.jikexueyuan.com/project/go-command-tutorial/0.1.html

相關文章
相關標籤/搜索