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の変更をサポートすることができます。gofixプログラムは、特定のAPI変更を処理する修正と呼ばれるプラグインを中心としたシンプルなドライバーです。現時点では、新しい修正を作成するには、go/ast構文ツリーのスキャンと書き換えを行う必要があり、通常、API変更が複雑になるほど比例します。詳しく知りたい場合は、netdialFixosopenFixhttpserverFix、およびreflectFixはすべて、複雑さが増す順に説明する例です。

もちろん、私たちもGoコードを書いており、私たちのコードもあなたのコードと同じようにこれらのAPI変更の影響を受けます。通常、API変更と同時にgofixサポートを記述し、gofixを使用してメインソースツリーの使用箇所を書き換えます。私たちはgofixを使用して他のGoコードベースや個人的なプロジェクトを更新しています。新しいGoリリースに対してビルドする時期になると、Googleの内部ソースツリーを更新するためにもgofixを使用しています。

例として、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の作成の重要な動機の1つは、おそらく特定の括弧がどこにあるべきかについての議論を避けることの次に、gofixのようにGoプログラムを書き換えるツールの作成を簡素化することでした。

Gofixはすでに不可欠なものとなっています。特に、最近のreflectの変更は、自動変換がなければ受け入れられなかったでしょうし、reflect APIはやり直しが非常に必要でした。Gofixは、既存のコードを変換するコストを心配することなく、間違いを修正したり、パッケージAPIを完全に再考したりする機能を提供してくれます。私たちと同じように、gofixが便利で役立つことを願っています。

次の記事:HerokuでのGo
前の記事:Godoc:Goコードのドキュメント化
ブログインデックス