Go 部落格

介紹 Gofix

Russ Cox
2011 年 4 月 15 日

下一版 Go 將在幾個基本的 Go 套件中大幅變更 API。實作 HTTP 伺服器處理程式呼叫 net.Dial呼叫 os.Open,或 使用 reflex 套件 的程式碼,若未更新為使用新的 API,則無法建置。由於我們的版本 更穩定且頻率更低,這將會是常見的情況。這些 API 變更發生在不同的每週快照中,且本身可能可以管理;但整體而言,它們代表著更新現有程式碼的許多手動工作。

Gofix 是一個新工具,可減少更新現有程式碼所需的工夫。它會從來源檔讀取一個程式,尋找舊 API 的使用情況,將其改寫成使用目前 API 的形式,並寫回程式碼至檔案中。並非所有 API 變更都會保留舊 API 的所有功能,因此 gofix 無法在所有情況下都執行完美無缺的工作。若 gofix 無法改寫舊 API 的使用情況,它會印出警告並提供使用檔案與行號,供開發人員檢查並改寫程式碼。Gofix 會處理簡單、重複、繁瑣的變更,以便開發人員能專注於真正需要留意的變更。

每次我們進行重要的 API 變更時,都會新增程式碼至 gofix,以處理轉換,就機械處理能力而言。當您更新至新的 Go 版本,且您的程式碼不再執行時,只要在您的來源目錄上執行 gofix 即可。

您可延伸 gofix 以支援自訂 API 的變更。gofix 程式是一個簡單的驅動程式,會包含稱為修復程式的外掛程式,而每個外掛程式會各自處理特定 API 變更。目前,寫一個新的修復程式需要對 go/ast 語法樹執行掃描和改寫作業,通常與 API 變更的複雜度成正比。如果您有興趣探索,netdialFixosopenFixhttpserverFix 以及 reflectFix 都是說明性的範例,複雜度依序遞增。

我們當然也會撰寫 Go 程式碼,而我們的程式碼也和您的程式碼一樣,會受到這些 API 變更的影響。通常,我們會與 API 變更同時撰寫 gofix 支援,然後再使用 gofix 改寫主要來源樹中的使用情況。我們使用 gofix 來更新其他 Go 程式碼庫和我們的個人專案。在必須針對新的 Go 版本執行建置時,我們甚至會使用 gofix 來更新 Google 的內部來源樹。

舉例來說,gofix 可以改寫類似的程式碼,例如,來自 fmt/print.go 的程式片段

switch f := value.(type) {
case *reflect.BoolValue:
    p.fmtBool(f.Get(), verb, field)
case *reflect.IntValue:
    p.fmtInt64(f.Get(), verb, field)
// ...
case reflect.ArrayOrSliceValue:
    // Byte slices are special.
    if f.Type().(reflect.ArrayOrSliceType).Elem().Kind() == reflect.Uint8 {
        // ...
    }
// ...
}

以適用於新的反映 API

switch f := value; f.Kind() {
case reflect.Bool:
    p.fmtBool(f.Bool(), verb, field)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    p.fmtInt64(f.Int(), verb, field)
// ...
case reflect.Array, reflect.Slice:
    // Byte slices are special.
    if f.Type().Elem().Kind() == reflect.Uint8 {
        // ...
    }
// ...
}

上面幾乎每一行都以小方式進行變更。改寫涉及的變更是廣泛的,但幾乎都是機械性的,正好是電腦擅長處理的工作。

Gofix 得以誕生,是因為 Go 在其標準函式庫中支援 將 Go 原始檔解析成語法樹,以及 將那些語法樹印回 Go 原始碼。重要的是,Go 印表函式庫會採用官方格式印表程式(通常由 gofmt 工具強制執行),因此 gofix 可以對 Go 程式進行機械化變更,而不會造成錯誤的格式變更。事實上,建立 gofmt 的主要動機之一(可能是僅次於避免有關特定大括弧位置的爭論)就是簡化能重寫 Go 程式的工具的建立方式,正如 gofix 所做的那樣。

Gofix 已經讓自己變得不可或缺。特別是,最近的 reflect 變更若沒有自動化轉換將變得難以接受,且 reflect API 迫切需要重新整理。Gofix 讓我們得以修正錯誤或徹底重新構想套件 API,而不必擔心轉換現有程式碼的成本。我們希望您發現 gofix 和我們一樣有用又便利。

下一篇文章:Heroku 上的 Go
上一篇文章:Godoc:紀錄 Go 程式碼
網誌索引