這是一個建立於
的文章,其中的資訊可能已經有所發展或是發生改變。
介紹
本文主要講述如何寫一個簡單的Go包和如何使用golang的工具,如何擷取、編譯和安裝Go的包,以及如何使用go的命令。
Go的工具需要將代碼按照一定的方式來組織。所以請認真閱讀本文。
代碼的組織
workspace
go工具是設計用來處理公開程式碼程式庫的開原始碼的,雖然你不是一定要公開你的代碼,但是工作的模式是一樣的。
Go代碼必須儲存在一個workspace中。一個workspace必須要在根目錄下包含三個子目錄:
* src 包含了Go的源檔案,這些源檔案以package的方式存在
* pkg 包含了包對象
* bin 包含了可執行檔
Go工具會編譯源檔案並把編譯成的二進位檔案存放在pkg和bin目錄中。
如下是一個典型的go項目的目錄:
bin/ hello # 命令可執行檔 outyet # 命令可執行檔pkg/ linux_amd64/ github.com/golang/example/ stringutil.a # 包對象src/ github.com/golang/example/ .git/ hello/ hello.go # 源檔案 outyet/ main.go # 源檔案 main_test.go # 測試 源檔案 stringutil/ reverse.go # 源檔案 reverse_test.go # 測試 源檔案
這個workspace只包含了一個程式碼程式庫(example),其中包含了兩個包hello和outyet以及一個庫stringutil。
一個典型的workspace可以包含多個程式碼程式庫,以及每個庫中的多個包和庫。多數的Go開發人員把這些都放在一個單獨的workspace中。
包和庫是從不同的源檔案包中編譯而成的。我們會稍後討論這一點。
GOPATH環境變數
GOPATH環境變數指定的就是你的workspace的位置。這個極可能是你開發Go項目的時候唯一的一個需要設定的環境變數。
開始前先建立一個workspace目錄,然後在GOPATH中指定這個workspace目錄的位置。你可以把GOPATH指定在任何的你喜歡的位置。
但是本文會使用$HOME/work為workspace目錄的位置。注意,這個目錄絕對不可以和你的go的安裝目錄相同。
$ mkdir $HOME/work$ export GOPATH=$HOME/work
然後把workspace的bin目錄添加到PATH中。
$ export PATH=$PATH:$GOPATH/bin
包路徑
標準庫的包路徑都會用簡寫:"fmt"和"net/http"。你自己的包就需要選擇一個路徑了。因為,你不會希望以後和標準庫的包或者其他
使用的包的名稱互相衝突的。
如果你把你的代碼儲存在某程式碼程式庫中,那麼你應該使用這個程式碼程式庫的根作為目錄使用:
$ mkdir -p $GOPATH/src/github.com/user
###你的第一個程式
要編譯、運行你的程式,首先選擇一個包路徑(我們會使用github.com/user/hello)並在你的workspace裡建立一個相應的包路徑
$ mkdir $GOPATH/src/github.com/user/hello
然後,建立一個hello.go的檔案。並在這個檔案中添加如下的代碼:
“`go
package main
import “fmt”
func main() {
fmt.Println(“Hello, world!”)
}
“`
現在你可以編譯並運行你的代碼了:
go install github.com/user/hello
注意你可以在你的系統的任何地方來運行這個命令。go工具可以根據GOPATH從workspace的github.com/user/hello找到源檔案
上面的命令列執行之後,會在workspace的bin目錄下產生一個可以自行檔案,這裡是hello(或者,windows下的hello.exe)。在我們的
例子中目錄為:$GOPATH/bin/hello。
go工具只會在出錯的時候列印輸出資訊。所以,如果沒有列印任何資訊的話那就是編譯成功了。
現在你可以運行你的程式了:
$ $GOPATH/bin/hello
Hello, world!
或者,你已經在PATH中添加了GOPATH/bin,只需要輸入可執行檔的名字:
$ helloHello, world!
如果你使用了代碼管理工具,那麼這就可以初始化一個程式碼程式庫了。添加檔案;並commit你的第一次更改。但是,這不是一定要的,但是
你可以不用代碼工具編寫go代碼。
$ cd $GOPATH/src/github.com/user/hello$ git initInitialized empty Git respository in /home/user/work/src/github.com/user/hello/.git/$ git add hello.go$ git commit -m "initial commit"[master (root-commit) 0b4507d] initial commit 1 file changed, 1 insertion(+) create mode 100644 hello.go
把代碼發布到遠端程式碼程式庫,讓其他的讀者可以看到你的代碼。
你的第一個庫
接下來,我們建立一個庫,然後在hello代碼中使用這個庫。
按照常理我們需要選擇一個包路徑(我們這裡使用github.com/user/stringutil)並建立包路徑
$ mkdir $GOPATH/src/github.com/user/stringutil
然後,穿件一個檔案:reverse.go, 內容為:
package stringutilfunc Reverse(s string) string { var r = []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r)}
現在,用go build命令測試一下代碼是否能夠編譯。
$ go build github.com/user/stringutil
或者你在這個包的路徑下的話只需要:
$ go build
這不會產生任何檔案。要組建檔案的話就必須要使用go install。執行完這個命令之後就會把這個包編譯後產生在pkg目錄中。
在stringutil編譯之後,修改hello.go的代碼:
package mainimport ( "fmt" "github.com/user/stringutil")func main() { fmt.Println(stringutil.Reverse("!oG ,olleH"))}
任何時候go工具安裝一包或者一個二進位檔案的時候,其相關的依賴也都會一起安裝。因此,當你安裝hello的時候:
$ go install github.com/user/hello
stringutil包也會自動安裝。
運行新版本的程式,你會看到一個新的,反轉的訊息:
$ helloHello, Go!
以上的步驟都執行完成之後,你的目錄看起來是這樣的:
bin/ hellopkg/ linux_amd64/ github.com/user/ stringutil.asrc/ github.com/user/ hello/ hello.go stringutil/ reverse.go
注意go install把stringutil.a放在了pkg/linux_amd64目錄中,並且會在linux_amd64下建立和源檔案所在的目錄相同的結構。
go工具之後的編譯中如果發現pkg中已經存在這一檔案,那麼就不會再次編譯。這樣可以避免不必要的重複編譯。linux_amd64目錄是為了交叉編譯
,並且反映出所在的作業系統和系統架構。
Go的可執行檔是靜態連結的。
包名稱
Go的源檔案第一行必須是:
package name
包名稱是import的時候使用的預設名稱,同一個包中的檔案必須使用同樣的包名稱。
Go的慣例是包名稱是import語句的最後一個元素。crypto/rot13引入的包就應該命名為rot13.
main函數所在的包必須用package main作為包名稱。
包的名稱沒有要求必須要唯一,但是報的整個引入路徑必須要唯一。
測試
Go有一個輕量級的測試架構,由go test和testing包組成。
寫一個測試只需要,建立一個_test.go結尾的檔案,這個檔案裡包含了名稱為TestXXX的方法,這些測試方法的簽名為func (t *testing.T)。
測試架構會運行每一個這樣的方法。如果某個測試方法調用了t.Error或者t.Fail, 那麼這個測試被認為是失敗的。
給stringutil添加一個測試。在$GOPATH/src/github.com/user/sringutil/目錄建立檔案reverse_test.go。這個檔案包含
如下代碼:
package stringutilimport "testing"func TestReverse(t *testing.T) { case := []struct { in, want string } { {"Hello, world", "dlrow ,olleH"}, {"Hello, 世界", "界世 ,olleH"}, {"", ""}, } for _, c := range cases { got := Reverse(c.in) if got != c.want{ t.Errorf("Reverse(%q) == %q", c.in, got, c.want) } }}
然後使用命令go test來運行測試:
$ go test github.com/user/stringutilPASSok stringutil 2.223s
如果實在package內部運行命令的話,可以省去包的路徑。
$ go testPASSok stringutil 2.223s
運行命令go help test可以看到更多的關於測試命令的細節。
Remote packages 遠端程式碼程式庫
一個import路徑和使用Git擷取遠程代碼的路徑是一樣的。go工具使用這個路徑自動從遠端程式碼程式庫擷取代碼。比如,上面使用的代碼
託管在GitHub的路徑為:github.com/golang/example。如果在代碼的import語句中包含了遠端代碼,go get命令會自動擷取、編譯
並install(安裝)這些代碼。
$ go get github.com/golang/example/hello$ $GOPATH/bin/helloHello, Go examples!
如果需要的包不在workspace中,go get命令會把這些包放在GOPATH指定的第一個workspace中。如果包已經存在,那麼go get
命令會跳過遠程擷取而直接執行go install類似的命令。
在執行以上的go get命令之後,workspace目錄看起來就是這樣的:
bin/ hellopkg/ linux_amd64/ github.com/golang/example/ stringutil.a github.com/user/ stringutil.asrc/ github.com/golang/example/ .git/ hello/ hello.a stringutil/ reverse.go reverse_test.go github.com/user/ hello/ hello.go stringutil/ reverse.go reverse_test.go
GitHub上託管的hello依賴於同一個包中的stringutil。hello.go中的import語句使用的是一樣的慣例。因此go get命令
可以定位並安裝(install)依賴包。
import "github.com/golang/example/stringutil
這一慣例會讓你的代碼非常容易被別人使用。
接下來。。。
Effective Go有更多的關於有效編寫Go代碼的內容。
A Tour of Go有更多的關於Go的知識。
擷取協助
到這裡在官方的mailing list討論Go語言。
在這裡Go issue tracker提交bug。
原文