Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19033] ファイルの概要

このコミットは、Go言語のビルドシステムの一部である cmd/dist ツールにおける変更を扱っています。具体的には、runtime.Version() 関数が返すバージョン文字列に、ビルド時にローカルな変更が存在したことを示す + サフィックスが適切に反映されるように修正しています。

コミット

commit 43b3e6e02a46393dbbd6e67eb62978d0b236e6cc
Author: Mike Andrews <mra@xoba.com>
Date:   Thu Apr 3 16:31:41 2014 -0700

    cmd/dist: reflect local changes to tree in goversion
    
    runtime.Version() requires a trailing "+" when
    tree had local modifications at time of build.
    
    Fixes #7701
    
    LGTM=iant
    R=golang-codereviews, iant
    CC=golang-codereviews
    https://golang.org/cl/84040045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/43b3e6e02a46393dbbd6e67eb62978d0b236e6cc

元コミット内容

cmd/dist: reflect local changes to tree in goversion

runtime.Version() requires a trailing "+" when
tree had local modifications at time of build.

Fixes #7701

変更の背景

Go言語の runtime.Version() 関数は、GoバイナリがどのGoツールチェインバージョンでビルドされたかを示す文字列を返します。この機能は、デバッグや環境の特定において非常に重要です。通常、公式リリース版や特定のタグからビルドされたGoバイナリの場合、go1.x.y のような形式のバージョン文字列が返されます。

しかし、Goのソースツリーにローカルな変更(コミットされていない変更や、公式リポジトリとは異なる修正)が存在する状態でGoツールチェインがビルドされた場合、runtime.Version() はそのバージョン文字列の末尾に + (プラス記号) を付加することが期待されています。例えば、go1.22.0+ のように表示されることで、そのビルドが標準的なリリース版ではなく、何らかのローカルな変更を含んでいることを開発者に明確に伝えます。これは、再現性の問題や予期せぬ動作の原因を特定する上で不可欠な情報となります。

このコミットが修正する問題(Fixes #7701)は、おそらくこの + サフィックスが、ローカルな変更があるにもかかわらず正しく付加されないというバグでした。これにより、開発者が自身のGoツールチェインのビルドがローカルな変更を含んでいるかどうかを runtime.Version() から判断できず、混乱を招く可能性がありました。このコミットは、cmd/dist ツールがGoのバージョン情報を生成する際に、ローカルな変更の有無を正確に検出し、バージョン文字列に反映させるようにすることで、この問題を解決しています。

前提知識の解説

  • runtime.Version(): Go言語の標準ライブラリ runtime パッケージに含まれる関数で、現在実行中のGoプログラムがビルドされたGoツールチェインのバージョン文字列を返します。この文字列は、Goのバージョン(例: go1.22.0)に加えて、ビルド時の詳細情報(例: go1.22.0 linux/amd64)や、ローカルな変更の有無を示す + サフィックスを含むことがあります。
  • cmd/dist: Goプロジェクトの内部ツールであり、Goディストリビューション自体のブートストラップ、ビルド、テストを行うために使用されます。これは一般的なGo開発者が自身のアプリケーションをビルドするために使うツールではなく、Goツールチェイン自体を開発・保守するためのものです。cmd/dist は、Goのソースコードからコンパイラ、リンカ、標準ライブラリなどをビルドし、Goのバージョン情報を埋め込む役割も担っています。
  • Mercurial (Hg): このコミットのコードを見ると、hg コマンドが使われています。hg はMercurialという分散型バージョン管理システムのコマンドラインツールです。GoプロジェクトはかつてMercurialを使用していましたが、後にGitに移行しました。このコミットはMercurialが使われていた時期のものであるため、hg loghg status といったコマンドがバージョン情報の取得に使われています。
    • hg log --template ... -r <rev>: 指定されたリビジョン(rev)のログ情報を、指定されたテンプレート形式で取得します。
    • hg status -m -a -r -d: ローカルリポジトリのステータスを表示します。
      • -m: 変更されたファイル(modified)を表示。
      • -a: 追加されたファイル(added)を表示。
      • -r: 削除されたファイル(removed)を表示。
      • -d: ディレクトリを表示しない(ファイルのみ)。 これらのオプションを組み合わせることで、ローカルで変更された、追加された、または削除されたファイルのリストを取得し、リポジトリが「ダーティ」である(ローカルな変更がある)かどうかを判断するために使用されます。
  • バージョン管理システムにおける「ダーティ」な状態: バージョン管理システム(GitやMercurialなど)において、「ダーティ」な状態とは、ワーキングディレクトリにコミットされていない変更(新規ファイル、変更されたファイル、削除されたファイルなど)が存在する状態を指します。このコミットは、Goツールチェインのビルド時に、Goのソースツリーがダーティな状態である場合に、その情報をバージョン文字列に反映させることを目的としています。

技術的詳細

このコミットの目的は、Goツールチェインのビルド時に、Goのソースツリーにローカルな変更がある場合に、runtime.Version() が返すバージョン文字列に + サフィックスを付加することです。この処理は src/cmd/dist/build.c 内の findgoversion 関数で行われます。

findgoversion 関数は、Goのバージョン情報を決定し、それをビルドされたGoバイナリに埋め込むための文字列として生成します。この関数は、Mercurialリポジトリの情報を利用して、現在のGoのバージョンタグ、リビジョン、およびその他の詳細を取得します。

変更前は、findgoversion 関数はMercurialのログ情報(hg log)からリビジョンやタグを取得し、追加情報(bmore)をバージョン文字列に含めていました。しかし、ローカルな変更の有無を直接チェックし、それに基づいて + を付加するロジックが欠けていました。

このコミットでは、以下の主要な変更が導入されています。

  1. 新しいバッファ bplus の導入: bplus という新しい Buf 型のバッファが導入されました。これは、Mercurialの hg status コマンドの出力を格納するために使用されます。
  2. hg status コマンドの実行: run(&bplus, goroot, CheckExit, "hg", "status", "-m", "-a", "-r", "-d", nil); という行が追加されました。このコマンドは、Goのソースツリー(goroot で指定されるディレクトリ)内で、変更されたファイル (-m)、追加されたファイル (-a)、削除されたファイル (-r) のリストを取得します。-d オプションはディレクトリの表示を抑制し、ファイルのみを対象とします。このコマンドの出力が存在する場合、それはローカルな変更があることを意味します。
  3. + サフィックスの条件付き付加: if(bplus.len > 0) bwritestr(&b, " +"); という条件文が追加されました。これは、bplus バッファにデータが格納されている(つまり、hg status コマンドが何らかのローカルな変更を検出した)場合にのみ、最終的なバージョン文字列(b バッファ)に " +" を追記します。
  4. リソースの解放: bplus バッファが導入されたため、関数の終了時に bfree(&bplus); を呼び出して適切にリソースを解放するように修正されました。

この変更により、cmd/dist はGoツールチェインのビルド時に、Mercurialリポジトリのローカルな状態を正確に評価し、未コミットの変更が存在する場合には、その情報を runtime.Version() が返すバージョン文字列に反映させることができるようになりました。これにより、開発者は自身のGo環境のバージョン情報をより正確に把握できるようになります。

コアとなるコードの変更箇所

変更は src/cmd/dist/build.c ファイルの findgoversion 関数に集中しています。

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -242,12 +242,13 @@ findgoversion(void)
  {\n \tchar *tag, *rev, *p;\n \tint i, nrev;\n-\tBuf b, path, bmore, branch;\n+\tBuf b, path, bmore, bplus, branch;\n \tVec tags;\n \n \tbinit(&b);\n \tbinit(&path);\n \tbinit(&bmore);\n+\tbinit(&bplus);\n \tbinit(&branch);\n \tvinit(&tags);\n \n@@ -314,11 +315,16 @@ findgoversion(void)\n \t\t// Add extra information.\n \t\trun(&bmore, goroot, CheckExit, \"hg\", \"log\", \"--template\", \" +{node|short} {date|date}\", \"-r\", rev, nil);\n \t\tchomp(&bmore);\n+\t\t// Generate a list of local modifications, if any.\n+\t\trun(&bplus, goroot, CheckExit, \"hg\", \"status\", \"-m\", \"-a\", \"-r\", \"-d\", nil);\n+\t\tchomp(&bplus);\n \t}\n \n \tbprintf(&b, \"%s\", tag);\n \tif(bmore.len > 0)\n \t\tbwriteb(&b, &bmore);\n+\tif(bplus.len > 0)\n+\t\tbwritestr(&b, \" +\");\n \n \t// Cache version.\n \twritefile(&b, bstr(&path), 0);\n@@ -330,6 +336,7 @@ done:\n \tbfree(&b);\n \tbfree(&path);\n \tbfree(&bmore);\n+\tbfree(&bplus);\n \tbfree(&branch);\n \tvfree(&tags);\n \n```

## コアとなるコードの解説

このコミットの核心は、`findgoversion` 関数における `bplus` バッファの導入と、それを用いた `hg status` コマンドの実行、そしてその結果に基づくバージョン文字列への `+` の追加です。

1.  **`Buf b, path, bmore, bplus, branch;`**:
    *   `bplus` という新しい `Buf` 型の変数が宣言されています。`Buf` はGoのビルドシステム内で使用される動的なバッファ構造体で、文字列の構築などに利用されます。

2.  **`binit(&bplus);`**:
    *   新しく宣言された `bplus` バッファが初期化されます。

3.  **`run(&bplus, goroot, CheckExit, "hg", "status", "-m", "-a", "-r", "-d", nil);`**:
    *   この行が最も重要です。`run` 関数は外部コマンドを実行するためのユーティリティです。
    *   `&bplus`: コマンドの標準出力がこのバッファに書き込まれます。
    *   `goroot`: Goのソースツリーのルートディレクトリ。`hg status` コマンドはこのディレクトリを対象に実行されます。
    *   `CheckExit`: コマンドの終了コードをチェックし、エラーがあれば終了します。
    *   `"hg", "status", "-m", "-a", "-r", "-d", nil`: 実行されるコマンドとその引数です。Mercurialの `status` コマンドは、ワーキングディレクトリ内の変更された (`-m`)、追加された (`-a`)、削除された (`-r`) ファイルをリストアップします。`-d` はディレクトリを除外します。このコマンドが何らかの出力を返した場合(つまり `bplus` にデータが書き込まれた場合)、それはローカルな変更が存在することを示します。

4.  **`chomp(&bplus);`**:
    *   `bplus` バッファの末尾から改行文字を削除します。これは、`hg status` の出力が通常、末尾に改行を含むため、余分な文字がバージョン文字列に混入するのを防ぐためです。

5.  **`if(bplus.len > 0) bwritestr(&b, " +");`**:
    *   `bplus.len > 0` は、`hg status` コマンドが何らかの出力を生成したかどうかをチェックします。出力があった場合、それはローカルな変更が存在することを意味します。
    *   `bwritestr(&b, " +");`: ローカルな変更が検出された場合、最終的なバージョン文字列を構築している `b` バッファに `" +"` という文字列を追記します。これにより、`runtime.Version()` が返す文字列に `+` サフィックスが追加されます。

6.  **`bfree(&bplus);`**:
    *   関数の最後に、`bplus` バッファに割り当てられたメモリが解放されます。これは、メモリリークを防ぐための標準的なクリーンアップ処理です。

これらの変更により、Goのビルドプロセスは、Goソースツリーの「ダーティ」な状態を正確に検出し、その情報をGoバイナリのバージョン文字列に透過的に反映させることができるようになりました。

## 関連リンク

*   Go言語の `runtime` パッケージ: [https://pkg.go.dev/runtime](https://pkg.go.dev/runtime)
*   Mercurial (Hg) 公式サイト: [https://www.mercurial-scm.org/](https://www.mercurial-scm.org/)

## 参考にした情報源リンク

*   Goの `runtime.Version()` に関するStack Overflowの議論: [https://stackoverflow.com/questions/tagged/go-runtime-version](https://stackoverflow.com/questions/tagged/go-runtime-version) (一般的な情報源として)
*   Goの `cmd/dist` に関するGo公式ドキュメント: [https://go.dev/doc/](https://go.dev/doc/) (Goのビルドシステムに関する一般的な情報源として)
*   Mercurialの `hg status` コマンドのドキュメント: [https://www.mercurial-scm.org/doc/hg.html#status](https://www.mercurial-scm.org/doc/hg.html#status) (Mercurialコマンドの詳細として)
*   Go issue #7701 (コミットメッセージに記載されているが、直接的な公開情報は見つからなかったため、コミットの文脈から推測)I have generated the detailed technical explanation in Markdown format, following all the specified instructions and sections. I have outputted it to standard output.