鍍金池/ 教程/ GO/ go fix與go tool fix
go install
go clean
go list
go test
go doc與godoc
go build
go fix與go tool fix
go tool pprof
go run
go env
go tool cgo
標(biāo)準(zhǔn)命令詳解
go get
go vet與go tool vet

go fix與go tool fix

命令go fix會(huì)把指定代碼包的所有Go語言源碼文件中的舊版本代碼修正為新版本的代碼。這里所說的版本即Go語言的版本。代碼包的所有Go語言源碼文件不包括其子代碼包(如果有的話)中的文件。修正操作包括把對(duì)舊程序調(diào)用的代碼更換為對(duì)新程序調(diào)用的代碼、把舊的語法更換為新的語法,等等。

這個(gè)工具其實(shí)非常有用。在編程語言的升級(jí)和演進(jìn)的過程中,難免會(huì)對(duì)過時(shí)的和不夠優(yōu)秀的語法及標(biāo)準(zhǔn)庫(kù)進(jìn)行改進(jìn)。這樣的改進(jìn)對(duì)于編程語言的向后兼容性是個(gè)挑戰(zhàn)。我們?cè)谇懊嫣岬竭^向后兼容這個(gè)詞。簡(jiǎn)單來說,向后兼容性就是指新版本的編程語言程序能夠正確識(shí)別和解析用該編程語言的舊版本編寫的程序和軟件,以及在新版本的編程語言的運(yùn)行時(shí)環(huán)境中能夠運(yùn)行用該編程語言的舊版本編寫的程序和軟件。對(duì)于Go語言來說,語法的改變和標(biāo)準(zhǔn)庫(kù)的變更都會(huì)使得用舊版本編寫的程序無法在新版本環(huán)境中編譯通過。這就等于破壞了Go語言的向后兼容性。對(duì)于一個(gè)編程語言、程序庫(kù)或基礎(chǔ)軟件來說,向后兼容性是非常重要的。但有時(shí)候?yàn)榱俗屲浖觾?yōu)秀,軟件的開發(fā)者或維護(hù)者不得不在向后兼容性上做出一些妥協(xié)。這是一個(gè)在多方利益之間進(jìn)行權(quán)衡的結(jié)果。本小節(jié)所講述的工具正是Go語言的創(chuàng)造者們?yōu)榱瞬蛔屵@種妥協(xié)給語言使用者帶來困擾和額外的工作量而編寫的自動(dòng)化修正工具。這也充分體現(xiàn)了Go語言的軟件工程哲學(xué)。下面讓我們來詳細(xì)了解它們的使用方法和內(nèi)部機(jī)理。

命令go fix其實(shí)是命令go tool fix的簡(jiǎn)單封裝。這甚至比go fmt命令對(duì)gofmt命令的封裝更簡(jiǎn)單。像其它的Go命令一樣,go fix命令會(huì)先對(duì)作為參數(shù)的代碼包導(dǎo)入路徑進(jìn)行驗(yàn)證,以確保它是正確有效的。像在本小節(jié)開始處描述的那樣,go fix命令會(huì)把有效代碼包中的所有Go語言源碼文件作為多個(gè)參數(shù)傳遞給go tool fix命令。實(shí)際上,go fix命令本身不接受任何標(biāo)記,它會(huì)把加入的所有標(biāo)記都原樣傳遞給go tool fix命令。go tool fix命令可接受的標(biāo)記如下表。

表0-15 go tool fix命令的標(biāo)記說明

標(biāo)記名稱 標(biāo)記描述
-diff 不將修正后的內(nèi)容寫入文件,而只打印修正前后的內(nèi)容的對(duì)比信息到標(biāo)準(zhǔn)輸出。
-r 只對(duì)目標(biāo)源碼文件做有限的修正操作。該標(biāo)記的值即為允許的修正操作的名稱。多個(gè)名稱之間用英文半角逗號(hào)分隔。
-force 使用此標(biāo)記后,即使源碼文件中的代碼已經(jīng)與Go語言的最新版本相匹配了,也會(huì)強(qiáng)行執(zhí)行指定的修正操作。該標(biāo)記的值就是需要強(qiáng)行執(zhí)行的修正操作的名稱,多個(gè)名稱之間用英文半角逗號(hào)分隔。
在默認(rèn)情況下,```go tool fix```命令程序會(huì)在目標(biāo)源碼文件上執(zhí)行所有的修正操作。多個(gè)修正操作的執(zhí)行會(huì)按照每個(gè)修正操作中標(biāo)示的操作建立日期以從早到晚的順序進(jìn)行。我們可以通過執(zhí)行```go tool fix -?```來查看```go tool fix```命令的使用說明以及當(dāng)前支持的修正操作。 與本書對(duì)應(yīng)的Go語言版本的```go tool fix```命令目前只支持兩個(gè)修正操作。一個(gè)是與標(biāo)準(zhǔn)庫(kù)代碼包```go/printer```中的結(jié)構(gòu)體類型```Config```的初始化代碼相關(guān)的修正操作,另一個(gè)是與標(biāo)準(zhǔn)庫(kù)代碼包``net```中的結(jié)構(gòu)體類型```IPAddr```、```UDPAddr```和```TCPAddr```的初始化代碼相關(guān)的修正操作。從修正操作的數(shù)量來看,自第一個(gè)正式版發(fā)布以來,Go語言的向后兼容性還是很好的。從Go語言官網(wǎng)上的說明也可以獲知,在Go語言的第二個(gè)大版本(Go 2.x)出現(xiàn)之前,它會(huì)一直良好的向后兼容性。 值得一提的是,上述的修正操作都是依靠Go語言的標(biāo)準(zhǔn)庫(kù)代碼包```go```及其子包中提供的功能來完成的。實(shí)際上,```go tool fix```命令程序在執(zhí)行修正操作之前,需要先將目標(biāo)源碼文件中的內(nèi)容解析為一個(gè)抽象語法樹實(shí)例。這一功能其實(shí)就是由代碼包```go/parser```提供的。而在這個(gè)抽象語法樹實(shí)例中的各個(gè)元素的結(jié)構(gòu)體類型的定義以及檢測(cè)、訪問和修改它們的方法則由代碼包```go/ast```提供。有興趣的讀者可以閱讀這些代碼包中的代碼。這對(duì)于深入理解Go語言對(duì)代碼的靜態(tài)處理過程是非常有好處的。 回到正題。與```gofmt```命令相同,```go tool fix```命令也有交互模式。我們同樣可以通過執(zhí)行不帶任何參數(shù)的命令來進(jìn)入到這個(gè)模式。但是與```gofmt```命令不同的是,我們?cè)赻``go tool fix```命令的交互模式中輸入的代碼必須是完整的,即必須要符合Go語言源碼文件的代碼組織形式。當(dāng)我們輸入了不完整的代碼片段時(shí),命令程序?qū)@示錯(cuò)誤提示信息并退出。示例如下: hc@ubt:~$ go tool fix -r='netipv6zone' a := &net.TCPAddr{ip4, 8080} standard input:1:1: expected 'package', found 'IDENT' a 相對(duì)于上面的示例,我們必須要這樣輸入源碼才能獲得正常的結(jié)果: hc@ubt:~$ go tool fix -r='netipv6zone' package main import ( "fmt" "net" ) func main() { addr := net.TCPAddr{"127.0.0.1", 8080} fmt.Printf("TCP Addr: %s\n", addr) } standard input: fixed netipv6zone package main import ( "fmt" "net" ) func main() { addr := net.TCPAddr{IP: "127.0.0.1", Port: 8080} fmt.Printf("TCP Addr: %s\n", addr) } 上述示例的輸出結(jié)果中有這樣一行提示信息:“standard input: fixed netipv6zone”。其中,“standard input”表明源碼是從標(biāo)準(zhǔn)輸入而不是源碼文件中獲取的,而“fixed netipv6zone”則表示名為netipv6zone的修正操作發(fā)現(xiàn)輸入的源碼中有需要修正的地方,并且已經(jīng)修正完畢。另外,我們還可以看到,輸出結(jié)果中的代碼已經(jīng)經(jīng)過了格式化。