メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#77897likely_accept

cmd/go: only set vcs.modified=true if changes are relevant to build

ステータス変更: active likely_accept

要約

AIによる要約であり、誤りを含む場合があります。

概要

go buildgo install を実行した際にバイナリに埋め込まれる vcs.modified フラグ(runtime/debug.BuildSetting の定義済みキー)の精度を向上させる提案です。現在はビルドに無関係なファイルが変更されていても vcs.modified=true+dirty サフィックス)が設定されてしまう問題を解決します。

ステータス変更

activelikely_accept
Go Command Working Group(@golang/go-command)が2026年6月4日に機能の有用性と実装の妥当性を確認し、2026年6月10日に @aclements がproposal review meetingでの議論を踏まえて likely_accept に移行させました。実装CL(CL 779200)が提出されており、既存のパッケージロードロジックを再利用する設計が評価されました。

技術的背景

現状の問題点

go build を実行すると、カレントディレクトリに成果物のバイナリが生成されます。Gitはこのバイナリを未追跡ファイルとして検出するため、リポジトリが「変更あり」状態になります。続けて go install を実行すると、バイナリが $GOBIN にインストールされ、カレントディレクトリのバイナリは削除されます。この結果、同じコードから2回インストールしたにもかかわらず、1回目は +dirty サフィックスが付き、2回目は付かないという非冪等な動作が生じます。
また、CPUプロファイル(x.prof)やログファイルなど、ビルドとは無関係なファイルがリポジトリ内に存在するだけで vcs.modified=true になるため、バイナリのバージョン情報の信頼性が低下しています。

// 現在の動作: リポジトリ内に任意のファイルが存在するだけで+dirtyになる
// $ ls
// main.go  go.mod  x.prof  # x.profはビルドに無関係
// $ go install
// → インストールされるバイナリのvcs.modified=true (+dirty)

提案された解決策

vcs.modified=true を設定する条件を、ビルドに実際に関係するファイルに限定します。具体的には以下の精密なアルゴリズムを採用します。
新しい定義:

modified = 現在のビルドで使用されるファイルのうちVCS変更済みのものが存在する、またはクリーンなVCSチェックアウトでのビルドで使用されるファイルのうちVCS変更済みのものが存在する
実装アルゴリズムの概要:

  1. VCS変更済みファイルのリストを取得する
  2. 変更済みファイルが現在のビルドに含まれていれば modified=true で終了
  3. 変更済みファイルが現在のビルドのembed パターンにマッチすれば modified=true で終了
  4. 各変更済みファイルについて:
    • 現在のビルド内パッケージのディレクトリ外ならスキップ
    • ソースコードのサフィックス(.go, .c, .h 等)でなければスキップ
    • ファイル名がビルドから除外されるもの(_test.go、クロスコンパイル対象外のOSサフィックス等)ならスキップ
    • VCSからそのファイルの旧バージョンのヘッダーを読み込み、ビルドタグを確認。クリーンビルドに含まれていれば modified=true で終了
      この設計により、「ファイルを削除してビルドが依然として成功するケース」(デッドコード、init 関数のみ、embed ディレクトリ内ファイルなど)における偽陰性(false negative)を防ぎます。

これによって何ができるようになるか

vcs.modified フラグがビルドの実態を正確に反映するようになり、バイナリのバージョン情報の信頼性が大幅に向上します。

コード例

// Before: ビルド成果物がカレントディレクトリに残っているだけで+dirty
// assume completely clean repo
// go build          → ./foo バイナリ生成(リポジトリが"modified"状態になる)
// go install        → $GOBIN/foo に+dirtyサフィックス付きでインストール
// go install(再度)→ $GOBIN/foo にサフィックスなしでインストール
// ※ 同じソースから2回インストールしたのに結果が異なる
// After: ビルドに無関係なファイルはvcs.modifiedに影響しない
// $ ls
// main.go  go.mod  ./foo(直前のgo buildの成果物)
// go install → +dirtyなし(./fooはGoソースファイルでないため無関係)
// ビルドに関係するファイルが変更された場合は正しく検出
// $ vim main.go(ソースを編集)
// go install → +dirty付き(main.goはビルドに参加するため検出される)

議論のハイライト

  • 偽陰性(false negative)問題の発見と対処: 当初の提案「変更済みファイルが現在のビルドに含まれるかチェック」では、ファイルを削除してビルドが引き続き成功するケース(デッドコード、//go:build ignore タグの追加等)で誤って modified=false になる問題があった。これは「クリーンなVCSでのビルドに含まれるかも確認する」という2段階チェックで解決された。
  • VCSからのファイル読み取りコスト: VCSから旧バージョンのファイルを読み込む必要があるケースについて、ネットワーク不要かどうかが懸念された。Subversionもローカルにベースファイルを保持しているため、オフラインで動作可能であると確認された(svn diff がオフラインで動作することから類推)。
  • 実装のコード共有: @aclements から、ビルドにファイルが含まれるかどうかの判定ルールが既存ロジックと乖離するリスクを懸念する声があった。@rsc は既存のパッケージロードロジックをそのまま再利用する設計(CL 779200)で対応し、embed のような新たなファイル取り込み方法が追加された場合のみ更新が必要と説明した。
  • .gitignore とVCS管理の関係: @mvdan が「VCS無視ファイルもビルドに関与する場合は dirty にすべき」と指摘。@rsc は「.gitignore の有無は関係なく、VCS内のコピーとの差分でmodifiedを判定する」と定義を明確化した。
  • 非Goディレクトリ内のCファイルへの対応: @seankhliao がcgoを使う場合の非Goディレクトリ内Cファイルについて質問したが、@rsc は「そのような構成はビルドキャッシュも機能せず、そもそも誤った設定」として対象外とした。

関連リンク