介紹
本文主要講述如何寫一個簡單的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,只需要輸入可執行文件的名字:
$ hello Hello, world!
如果你使用了代碼管理工具,那么這就可以初始化一個代碼庫了。添加文件;並commit你的第一次更改。但是,這不是一定要的,但是
你可以不用代碼工具編寫go代碼。
$ cd $GOPATH/src/github.com/user/hello $ git init Initialized 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 stringutil func 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 main import ( "fmt" "github.com/user/stringutil" ) func main() { fmt.Println(stringutil.Reverse("!oG ,olleH")) }
任何時候go工具安裝一包或者一個二進制文件的時候,其相關的依賴也都會一起安裝。因此,當你安裝hello的時候:
$ go install github.com/user/hello
stringutil包也會自動安裝。
運行新版本的程序,你會看到一個新的,反轉的消息:
$ hello Hello, Go!
以上的步驟都執行完成之后,你的目錄看起來是這樣的:
bin/
hello
pkg/
linux_amd64/
github.com/user/ stringutil.a src/ 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 stringutil import "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/stringutil PASS ok stringutil 2.223s
如果實在package內部運行命令的話,可以省去包的路徑。
$ go test PASS ok 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/hello Hello, Go examples!
如果需要的包不在workspace中,go get命令會把這些包放在GOPATH指定的第一個workspace中。如果包已經存在,那么go get
命令會跳過遠程獲取而直接執行go install類似的命令。
在執行以上的go get命令之后,workspace目錄看起來就是這樣的:
bin/
hello
pkg/
linux_amd64/
github.com/golang/example/ stringutil.a github.com/user/ stringutil.a src/ 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。
