命令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)過了格式化。