Go语言圣经 - 第10章 包和工具 - 10.7 工具
第10章 包和工具
現(xiàn)在隨便一個(gè)小程序可能就包含10000個(gè)函數(shù),但是我們不可能一個(gè)個(gè)去構(gòu)建,大部分還是來自于他人,這些函數(shù)通過類似包和模塊的方式被重用
go語言的包超過100個(gè),可以在終端中使用go list std |wc -l去查看,開源包可以通過http://godoc.org來檢索
go帶了一個(gè)工具包里面有各種簡化工作區(qū)和包管理的小工具
10.7 工具
現(xiàn)在我們?cè)賮砜纯磄o語言工具箱中的具體功能,包括如何下載、格式化、構(gòu)建、測(cè)試和安裝Go語言編寫的程序
Go語言的工具箱集合了一系列功能的命令集,它是一個(gè)包管理器,可以查詢包、計(jì)算包的依賴關(guān)系、從遠(yuǎn)程版本控制系統(tǒng)下載包;它也是一個(gè)構(gòu)建系統(tǒng),計(jì)算文件的依賴關(guān)系,然后調(diào)用編譯器、匯編器和連接器構(gòu)建程序;另外它也是一個(gè)單元測(cè)試和基準(zhǔn)測(cè)試的驅(qū)動(dòng)程序,我們將在下一章討論這個(gè)問題
我們運(yùn)行一下go命令,看看工具
% go Go is a tool for managing Go source code.Usage:go <command> [arguments]The commands are:bug start a bug reportbuild compile packages and dependenciesclean remove object files and cached filesdoc show documentation for package or symbolenv print Go environment informationfix update packages to use new APIsfmt gofmt (reformat) package sourcesgenerate generate Go files by processing sourceget add dependencies to current module and install theminstall compile and install packages and dependencieslist list packages or modulesmod module maintenancerun compile and run Go programtest test packagestool run specified go toolversion print Go versionvet report likely mistakes in packagesUse "go help <command>" for more information about a command.Additional help topics:buildconstraint build constraintsbuildmode build modesc calling between Go and Ccache build and test cachingenvironment environment variablesfiletype file typesgo.mod the go.mod filegopath GOPATH environment variablegopath-get legacy GOPATH go getgoproxy module proxy protocolimportpath import path syntaxmodules modules, module versions, and moremodule-get module-aware go getmodule-auth module authentication using go.sumpackages package lists and patternsprivate configuration for downloading non-public codetestflag testing flagstestfunc testing functionsvcs controlling version control with GOVCSUse "go help <topic>" for more information about that topic為了達(dá)到零配置的設(shè)計(jì)目標(biāo),Go語言的工具箱很多地方都依賴各種約定。例如,根據(jù)給定的源文件名稱,Go語言的工具可以找到源文件對(duì)應(yīng)的包,因?yàn)槊總€(gè)目錄只包含了單一的包,并且包的導(dǎo)入路徑和工作區(qū)的目錄結(jié)構(gòu)是對(duì)應(yīng)的,給定一個(gè)包的導(dǎo)入路徑,Go語言的工具可以找到與之對(duì)應(yīng)的儲(chǔ)存著實(shí)體文件的目錄。它還可以跟俊導(dǎo)入路徑找到存儲(chǔ)代碼倉庫的遠(yuǎn)程服務(wù)器URL
10.7.1 工作區(qū)結(jié)構(gòu)
對(duì)于大多數(shù)的Go用戶來說只需要配置GOPATH的環(huán)境 變量就可指定當(dāng)前的工作目錄
$ export GOPATH=$HOME/gobook $ go get gopl.io/...按照上述邏輯下載源碼應(yīng)該是如下的目錄結(jié)構(gòu)
GOPATH/src/gopl.io/.git/ch1/helloworld/main.godup/main.go...golang.org/x/net/.git/html/parse.gonode.go...bin/helloworldduppkg/darwin_amd64/...GOPATH對(duì)應(yīng)的工作區(qū)域有三個(gè)子目錄:src/bin/pkg。src存放源代碼,bin存放編譯后可執(zhí)行的程序,pkg用于保存編譯后的包的目標(biāo)文件
第二個(gè)環(huán)境變量GOROOT用來指定Go的安裝目錄,還有它自帶的標(biāo)準(zhǔn)庫包的位置
其中g(shù)o env命令用于查看go語言工具涉及所有環(huán)境變量的值,包括未設(shè)置環(huán)境變量的默認(rèn)值
$ go env GOPATH="/home/gopher/gobook" GOROOT="/usr/local/go" GOARCH="amd64" GOOS="darwin" ...10.7.2 下載包
使用Go語言工具箱的Go命令,不僅可以根據(jù)包導(dǎo)入路徑找到本地工作區(qū)的包,甚至可以從互聯(lián)網(wǎng)上找到并且更新包
使用 go get 可以下載一個(gè)單一的包或者用…下載整個(gè)子目錄里面的每個(gè)包,go命令會(huì)同時(shí)計(jì)算并下載所有依賴的包
一旦包被下載,接著就是安裝包或包對(duì)應(yīng)的可執(zhí)行程序
下載過程:第一個(gè)命令是獲取golint工具,它用于檢測(cè)Go源代碼的編程風(fēng)格是否有問題,第二個(gè)命令是用golint命令對(duì)包代碼進(jìn)行編碼風(fēng)格檢查,它友好的報(bào)告了忘記了包的文檔
$ go get github.com/golang/lint/golint $ $GOPATH/bin/golint gopl.io/ch2/popcount src/gopl.io/ch2/popcount/main.go:1:1:package comment should be of the form "Package popcount ..."go get 命令支持當(dāng)前流行的托管網(wǎng)站GitHub、Bitbucket和Launchpad,可以直接向他們的版本控制系統(tǒng)請(qǐng)求代碼
對(duì)于其他網(wǎng)站,我們可能需要指定版本控制系統(tǒng)的具體路徑和協(xié)議,例如Git或Mercurial。運(yùn)行g(shù)o help importpath獲取相關(guān)的信息
go get命令獲取的代碼是真實(shí)的本地存儲(chǔ)倉庫,而不僅僅只是復(fù)制源文件,因此可以使用版本管理工具進(jìn)行不同版本的切換。例如golang.org/x/net包目錄對(duì)應(yīng)一個(gè)Git倉庫
$ cd $GOPATH/src/golang.org/x/net $ git remote -v origin https://go.googlesource.com/net (fetch) origin https://go.googlesource.com/net (push)注意:導(dǎo)入路徑含有網(wǎng)站域名,這個(gè)和本地Git倉庫對(duì)應(yīng)遠(yuǎn)程服務(wù)地址并不相同,真實(shí)的地址是go.googlesource.com.這是Go語言工具的一個(gè)特性,讓包用一個(gè)自定義的導(dǎo)入路徑,真實(shí)的代碼由更通用的服務(wù)提供,例如googlesource.com或github.com
如下頁面https://golang.org/x/net/html包含了如下的元數(shù)據(jù),它告訴Go語言的工具當(dāng)前包真實(shí)的Git倉庫托管地址
$ go build gopl.io/ch1/fetch $ ./fetch https://golang.org/x/net/html | grep go-import <meta name="go-import"content="golang.org/x/net git https://go.googlesource.com/net">指定-u 命令行參數(shù), go get命令將確保包和依賴的包都是最新版本,如果指定,存在于本地的包則不會(huì)更新到最新版本
但是對(duì)于發(fā)布程序,本地程序可能需要對(duì)依賴的包進(jìn)行精細(xì)化的管理。一般解決方法是使用vendor的目錄用于存儲(chǔ)依賴包的固定版本的源代碼,對(duì)本地依賴的包的版本更新也是謹(jǐn)慎和持續(xù)可控的
10.7.3 構(gòu)建包
go build命令編譯命令行參數(shù)指定的每一個(gè)包
如果包是一個(gè)庫,則忽略輸出結(jié)果,這可以檢測(cè)包是否是正確編譯。如果包的名字是main,go build將調(diào)用鏈接器在當(dāng)前目錄創(chuàng)建一個(gè)可執(zhí)行程序;以導(dǎo)入路徑的最后一段作為可執(zhí)行程序的名字
由于每個(gè)目錄只包含一個(gè)包,因此每個(gè)對(duì)應(yīng)可執(zhí)行程序或者叫Unix術(shù)語中的命令的包,會(huì)要求放到一個(gè)獨(dú)立的目錄中。這些目錄有時(shí)候會(huì)放在名叫cmd目錄的子目錄下面,例如用于提供Go文檔服務(wù)的golang.org/x/tools/cmd/godoc命令就是放在cmd子目錄
每個(gè)包可以由它們的導(dǎo)入路徑指定,就像前面提到的那樣,或者用一個(gè)相對(duì)目錄的路徑名指定,相對(duì)路徑必須以.或…開頭,如果沒有指定參數(shù),那么默認(rèn)指定為當(dāng)前目錄對(duì)應(yīng)的包。下面的命令用于構(gòu)建同一個(gè)包,雖然它們寫法各不相同
$ cd $GOPATH/src/gopl.io/ch1/helloworld $ go build 或者:$ cd anywhere $ go build gopl.io/ch1/helloworld 或者:$ cd $GOPATH $ go build ./src/gopl.io/ch1/helloworld 但不能這樣:$ cd $GOPATH $ go build src/gopl.io/ch1/helloworld Error: cannot find package "src/gopl.io/ch1/helloworld".也可以指定包的源文件列表,這一般只用于構(gòu)建一些小程序,或者做一些臨時(shí)性的實(shí)驗(yàn)。如果是main包,將會(huì)以第一個(gè)Go源文件的基礎(chǔ)文件名作為最終的可執(zhí)行程序的名字
$ cat quoteargs.go package mainimport ("fmt""os" )func main() {fmt.Printf("%q\n", os.Args[1:]) } $ go build quoteargs.go $ ./quoteargs one "two three" four\ five ["one" "two three" "four five"] 特別是對(duì)于這類一次性運(yùn)行的程序,我們希望盡快的構(gòu)建并運(yùn)行它。go run命令實(shí)際上是結(jié)合了構(gòu)建和運(yùn)行的兩個(gè)步驟:$ go run quoteargs.go one "two three" four\ five ["one" "two three" "four five"]其實(shí)也可以偷懶,直接go run *.go
默認(rèn)情況下,go build用于構(gòu)建指定的包和依賴的包,但會(huì)丟棄除可執(zhí)行文件之外的所有中間編譯結(jié)果
go install 則會(huì)保存中間編譯結(jié)果
編譯對(duì)應(yīng)不同的操作系統(tǒng)平臺(tái)和CPU架構(gòu),go install 命令會(huì)將編譯結(jié)果安裝到GOOS和GOARCH對(duì)應(yīng)的目錄
針對(duì)不同的操作系統(tǒng)或CPU交叉構(gòu)建也很簡單,只需設(shè)置好目標(biāo)對(duì)應(yīng)的GOOS和GOARCH,然后運(yùn)行構(gòu)建命令即可,下面交叉編譯的程序?qū)⑤敵鏊诰幾g時(shí)的操作系統(tǒng)和CPU類型
func main(){fmt.Println(runtime.GOOS,runtime.GOARCH) }下面以64位和32位環(huán)境分別編譯和執(zhí)行
$ go build gopl.io/ch10/cross $ ./cross darwin amd64 $ GOARCH=386 go build gopl.io/ch10/cross $ ./cross darwin 386更多的細(xì)節(jié)可以查看文檔
go doc go/build10.7.4 包文檔
Go語言的編碼風(fēng)格鼓勵(lì)為每個(gè)包提供良好的文檔,包成員和包的目的和用法都應(yīng)該在導(dǎo)出前注釋好
注釋是完整的句子,如下
// Fprintf formats according to a format specifier and writes to w. // It returns the number of bytes written and any write error encountered. func Fprintf(w io.Writer, format string, a ...interface{}) (int, error)當(dāng)包的注釋比較多則可以放到另一個(gè)文件中
使用go doc 包名/成員名/方法名可以查看,如下
go doc time go doc time.Since go doc time.Duration.Seconds這個(gè)命令并不需要輸入完整導(dǎo)入路徑或者正確的大小寫
go doc json.decode package json // import "encoding/json"func (dec *Decoder) Decode(v interface{}) errorDecode reads the next JSON-encoded value from its input and stores it in thevalue pointed to by v.See the documentation for Unmarshal for details about the conversion of JSONinto a Go value.第二個(gè)工具godoc 可以提供相互交叉引用的HTML頁面,但是包含和go doc命令相似以及更多的信息。godoc的在線服務(wù) https://godoc.org ,包含了成千上萬的開源包的檢索工具
10.7.5 內(nèi)部包
在Go語言中,包是最重要的封裝機(jī)制
為了滿足我們對(duì)于包的可見性的控制,我們使用Go語言的構(gòu)建工具對(duì)包含的internal名字的路徑段的包導(dǎo)入路徑做了特殊處理。這種包叫做internal包,一個(gè)internal包只能被和internal目錄有一個(gè)同父目錄的包所導(dǎo)入,例如,net/http/internal/chunked內(nèi)部包只能被net/http/httputil或net/http包導(dǎo)入,但是不能被net/url包導(dǎo)入,不過net/url包可以導(dǎo)入net/http/httputil包
net/http net/http/internal/chunked net/http/httputil net/url10.7.6 查詢包
go list可以查詢可用包的信息
其最簡單的形式,可以測(cè)試包是否在工作區(qū)并打印它的導(dǎo)入路徑:$ go list github.com/go-sql-driver/mysql github.com/go-sql-driver/mysqlgo list命令的參數(shù)還可以用"..."表示匹配任意的包的導(dǎo)入路徑。我們可以用它來列出工作區(qū)中的所有包:
$ go list ... archive/tar archive/zip bufio bytes cmd/addr2line cmd/api ...many more... 或者是特定子目錄下的所有包:$ go list gopl.io/ch3/... gopl.io/ch3/basename1 gopl.io/ch3/basename2 gopl.io/ch3/comma gopl.io/ch3/mandelbrot gopl.io/ch3/netflag gopl.io/ch3/printints gopl.io/ch3/surface 或者是和某個(gè)主題相關(guān)的所有包:$ go list ...xml... encoding/xml gopl.io/ch7/xmlselect go list命令還可以獲取每個(gè)包完整的元信息,而不僅僅只是導(dǎo)入路徑,這些元信息可以以不同格式提供給用戶。其中-json命令行參數(shù)表示用JSON格式打印每個(gè)包的元信息。$ go list -json hash {"Dir": "/home/gopher/go/src/hash","ImportPath": "hash","Name": "hash","Doc": "Package hash provides interfaces for hash functions.","Target": "/home/gopher/go/pkg/darwin_amd64/hash.a","Goroot": true,"Standard": true,"Root": "/home/gopher/go","GoFiles": ["hash.go"],"Imports": ["io"],"Deps": ["errors","io","runtime","sync","sync/atomic","unsafe"] } 命令行參數(shù)-f則允許用戶使用text/template包(§4.6)的模板語言定義輸出文本的格式。下面的命令將打印strconv包的依賴的包,然后用join模板函數(shù)將結(jié)果鏈接為一行,連接時(shí)每個(gè)結(jié)果之間用一個(gè)空格分隔:$ go list -f '{{join .Deps " "}}' strconv errors math runtime unicode/utf8 unsafe 上面的命令在Windows的命令行運(yùn)行會(huì)遇到template: main:1: unclosed action的錯(cuò)誤。產(chǎn)生這個(gè)錯(cuò)誤的原因是因?yàn)槊钚袑?duì)命令中的" "參數(shù)進(jìn)行了轉(zhuǎn)義處理。可以按照下面的方法解決轉(zhuǎn)義字符串的問題:$ go list -f "{{join .Deps \" \"}}" strconv 下面的命令打印compress子目錄下所有包的導(dǎo)入包列表:$ go list -f '{{.ImportPath}} -> {{join .Imports " "}}' compress/... compress/bzip2 -> bufio io sort compress/flate -> bufio fmt io math sort strconv compress/gzip -> bufio compress/flate errors fmt hash hash/crc32 io time compress/lzw -> bufio errors fmt io compress/zlib -> bufio compress/flate errors fmt hash hash/adler32 ioWindows下有同樣有問題,要避免轉(zhuǎn)義字符串的干擾:
$ go list -f "{{.ImportPath}} -> {{join .Imports \" \"}}" compress/...go list命令對(duì)于一次性的交互式查詢或自動(dòng)化構(gòu)建或測(cè)試腳本都很有幫助。我們將在11.2.4節(jié)中再次使用它。每個(gè)子命令的更多信息,包括可設(shè)置的字段和意義,可以用go help list命令查看。
在本章,我們解釋了Go語言工具中除了測(cè)試命令之外的所有重要的子命令。
在下一章,我們將看到如何用go test命令去運(yùn)行Go語言程序中的測(cè)試代碼
總結(jié)
以上是生活随笔為你收集整理的Go语言圣经 - 第10章 包和工具 - 10.7 工具的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国天文台
- 下一篇: 计算机交换机配置实验心得,交换机系统配置