Go 博客

Gofix 介绍

Russ Cox
2011 年 4 月 15 日

下一个 Go 版本将在几个基础 Go 包中包含重大的 API 更改。那些 实现 HTTP 服务器处理程序调用 net.Dial调用 os.Open使用 reflect 包 的代码,除非更新使用新 API,否则将无法构建。现在我们的版本更稳定且不那么频繁,这种情况将会很常见。这些 API 更改中的每一个都发生在不同的每周快照中,并且可能本身就可以处理;然而,总的来说,它们代表了更新现有代码的大量手动工作。

Gofix 是一个新工具,可以减少更新现有代码所需的工作量。它从源文件中读取程序,查找旧 API 的用法,将它们重写为使用当前 API,然后将程序写回文件。并非所有 API 更改都保留了旧 API 的全部功能,因此 gofix 并非总是能做到完美。当 gofix 无法重写旧 API 的用法时,它会打印一条警告,给出用法的文件名和行号,以便开发人员可以检查和重写代码。Gofix 负责简单、重复、繁琐的更改,这样开发人员就可以专注于那些真正值得关注的更改。

每次我们进行重大的 API 更改时,我们都会向 gofix 添加代码来处理转换,尽可能机械地进行。当您更新到新的 Go 版本并且您的代码不再构建时,只需在您的源目录上运行 gofix 即可。

您可以扩展 gofix 以支持您自己的 API 更改。go fix 程序是一个简单的驱动程序,围绕着称为“fixes”的插件,每个插件处理特定的 API 更改。目前,编写一个新的 fix 需要对 go/ast 语法树进行一些扫描和重写,通常与 API 更改的复杂程度成正比。如果您想探索,netdialFixosopenFixhttpserverFixreflectFix 都是具有代表性的示例,复杂度递增。

当然,我们自己也编写 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 {
        // ...
    }
// ...
}

来适应新的 reflect 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 的关键动机之一(也许仅次于避免关于大括号应放在哪里的争论)是为了简化 gofix 等重写 Go 程序的工具的创建。

Gofix 已经证明了它的不可或缺性。特别是,如果没有自动转换,最近的 reflect 更改将是难以忍受的,而 reflect API 确实需要重做。Gofix 使我们能够修复错误或彻底重新思考包 API,而无需担心转换现有代码的成本。我们希望您觉得 gofix 和我们一样有用和方便。

下一篇文章: Go 在 Heroku
上一篇文章: Godoc:文档化 Go 代码
博客索引