[インデックス 16251] ファイルの概要
このコミットは、Go言語のコマンドラインツールcmd/go
における以前の変更(CL 8119049)を元に戻すものです。具体的には、引数なしでgo run
コマンドを実行した際に、カレントディレクトリ内のGoソースファイルを自動的に検出して実行する機能を削除し、明示的にファイル名を指定する必要がある元の挙動に戻しています。
コミット
- コミットハッシュ:
ed9644fc3954d6852c725e2efd062fb46c0786a7
- Author: Russ Cox rsc@golang.org
- Date: Tue Apr 30 17:04:58 2013 -0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ed9644fc3954d6852c725e2efd062fb46c0786a7
元コミット内容
cmd/go: undo CL 8119049
Manual undo due to later changes in doc/go1.1.html; cmd/go/test.bash still passes.
Rationale, from CL 8119049 review log:
This makes the 'go run' command different from every other command.
For example, 'go test' does not mean 'go test *.go'.
If we were going to handle the no arguments case in 'go run', I would hope that
it would scan the current directory to find a package just like 'go build' or
'go test' would, and then it would require that package to be 'package main',
and then it would run that package. This would make it match 'go test' and 'go
build' and 'go install' and so on. It would mean that if you are working on a
command in a directory that is 'go install'able, then 'go run' will run the
binary for you. The current CL does not accomplish that when build constraints
or file name constraints are involved.
For example, if I am working on a program like:
$ ls
main.go
main_386.s
main_arm.s
main_amd64.s
$
Then 'go run' will fail here because the .s files are ignored.
If instead I am working on a program like:
$ ls
main.go
main_386.go
main_arm.go
main_amd64.go
$
then 'go run' will fail because too many files are included.
I would like to see this command implemented so that it is compatible with the
other go subcommands. Since it is too late to do that for Go 1.1, I would like
to see this CL reverted, to preserve the option to do it better later.
R=golang-dev, iant, r
CC=golang-dev
https://golang.org/cl/8797049
変更の背景
このコミットは、以前に導入されたCL 8119049(Go 1.1リリースサイクル中に導入されたと思われる変更)を元に戻すものです。CL 8119049は、go run
コマンドが引数なしで実行された場合に、カレントディレクトリ内のGoソースファイルを自動的に検索して実行する機能を追加しました。
しかし、この変更はGoコマンドラインツールの設計思想と一貫性がないという問題が指摘されました。具体的には、以下の点が問題視されました。
- 他の
go
サブコマンドとの不整合:go test
やgo build
などの他のサブコマンドは、引数なしで実行された場合にカレントディレクトリのパッケージを対象としますが、go test *.go
のようにワイルドカードを自動的に展開するような挙動はしませんでした。CL 8119049によるgo run
の挙動は、この一貫性を破るものでした。 - ビルド制約やファイル名制約との非互換性: Goでは、特定のOSやアーキテクチャ向けのコードを記述するためにビルド制約(例:
// +build linux
)やファイル名制約(例:_amd64.go
)を使用します。CL 8119049のgo run
の自動ファイル検出機能は、これらの制約を適切に考慮せず、誤ったファイルセットを選択したり、必要なファイルを見落としたりする可能性がありました。- 例えば、
main.go
、main_386.s
、main_arm.s
、main_amd64.s
のような構成の場合、.s
ファイル(アセンブリファイル)はGoのビルドプロセスで特別に扱われるため、go run
がこれらを無視してしまい、ビルドが失敗する可能性がありました。 - また、
main.go
、main_386.go
、main_arm.go
、main_amd64.go
のような構成の場合、go run
がすべての.go
ファイルを無条件に含めてしまい、「多すぎるファイルが含まれている」というエラーを引き起こす可能性がありました。これは、通常、これらのファイルは特定の環境でのみコンパイルされるべきだからです。
- 例えば、
- 将来的な改善の阻害:
go run
が引数なしで実行される場合、理想的にはgo build
やgo test
と同様に、カレントディレクトリのパッケージを特定し、それがpackage main
である場合にそのパッケージを実行するという、より洗練された挙動が望ましいとされました。CL 8119049の単純なファイル検出は、このような将来的な改善の道を閉ざすものであったため、Go 1.1のリリースに間に合わないのであれば、一度元に戻して将来的に適切な実装を行う余地を残すべきだという判断がなされました。
これらの理由から、go run
コマンドの挙動を他のgo
サブコマンドと一貫させ、将来的な拡張性を確保するために、CL 8119049の変更を元に戻すことが決定されました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびGoツールの基本的な概念を理解しておく必要があります。
go run
コマンド:- Go言語のソースファイルをコンパイルし、その実行可能ファイルを一時的に作成して実行するコマンドです。開発中に単一のGoプログラムを素早くテストする際によく使用されます。
- 通常は
go run main.go
のように、実行したいGoソースファイルを明示的に指定します。
go build
コマンド:- Go言語のソースファイルをコンパイルし、実行可能ファイルを生成するコマンドです。引数なしで実行すると、カレントディレクトリのパッケージをビルドします。
go build .
やgo build example.com/my/package
のように、パッケージパスを指定することもできます。
go test
コマンド:- Go言語のテストを実行するコマンドです。引数なしで実行すると、カレントディレクトリのパッケージ内のテストを実行します。
go test ./...
のように、複数のパッケージのテストを実行することも可能です。
package main
:- Go言語において、実行可能なプログラムのエントリポイントとなるパッケージです。
main
パッケージには、プログラムの実行開始点となるmain
関数が含まれている必要があります。
- Go言語において、実行可能なプログラムのエントリポイントとなるパッケージです。
- ビルド制約 (Build Constraints / Build Tags):
- Goのソースファイルに記述される特別なコメント行で、そのファイルが特定のビルド条件(例: OS、アーキテクチャ、Goのバージョンなど)を満たす場合にのみコンパイルされるように指定します。
- 例:
// +build linux,amd64
は、LinuxかつAMD64アーキテクチャの場合にのみこのファイルをコンパイルすることを示します。
- ファイル名制約 (File Name Constraints):
- Goでは、ファイル名に特定のサフィックス(例:
_windows.go
,_amd64.go
,_test.go
)を付けることで、そのファイルが特定のOS、アーキテクチャ、またはテストビルドでのみコンパイルされるように自動的に認識されます。 - 例:
myprogram_windows.go
はWindowsでのみコンパイルされ、myprogram_test.go
はgo test
実行時にのみコンパイルされます。
- Goでは、ファイル名に特定のサフィックス(例:
- Goのパッケージ管理:
- Goのソースコードはパッケージに分割され、関連する機能がまとめられます。
go build
やgo test
は、通常、これらのパッケージを単位として操作します。
- Goのソースコードはパッケージに分割され、関連する機能がまとめられます。
技術的詳細
このコミットは、go run
コマンドの内部ロジックとドキュメントの変更を通じて、その挙動を元に戻しています。
以前のCL 8119049では、go run
にファイルが指定されなかった場合、filepath.Glob("*.go")
を使用してカレントディレクトリ内のすべての.go
ファイルを検索し、_test.go
で終わらないファイルを自動的に実行対象としていました。この挙動は、ユーザーが明示的にファイル名を指定しなくても、単一のGoファイルで構成される簡単なプログラムを素早く実行できるという利点がありました。
しかし、前述の「変更の背景」で述べたように、この自動検出ロジックはGoのビルドシステムが持つビルド制約やファイル名制約の複雑さを考慮していませんでした。結果として、以下のような問題が発生しました。
- 不正確なファイルセットの選択:
main_amd64.go
やmain_arm.go
のように、特定のアーキテクチャ向けのファイルが複数存在する場合、go run
はそれらすべてを無条件に含めてしまい、「多すぎるファイル」としてコンパイルエラーを引き起こす可能性がありました。Goのビルドシステムは、これらのファイルの中から現在の環境に合ったものだけを選択してコンパイルするべきですが、go run
の自動検出はそこまで賢くありませんでした。 - アセンブリファイル(
.s
)の無視: GoプログラムはGoソースファイルだけでなく、アセンブリファイル(.s
)を含むこともあります。filepath.Glob("*.go")
は.s
ファイルを検出しないため、アセンブリコードに依存するプログラムをgo run
で実行しようとすると、必要なファイルが見つからずに失敗しました。 - 他の
go
コマンドとの非一貫性:go build
やgo test
は、引数なしで実行された場合、カレントディレクトリのパッケージ全体を対象とします。これは、ビルド制約やファイル名制約を適切に処理し、依存関係を解決するGoのビルドシステムによって行われます。go run
の自動.go
ファイル検出は、このパッケージベースの処理とは異なり、ファイルベースの単純な処理であったため、Goツールの全体的な設計思想から逸脱していました。
このコミットでは、これらの問題を解決するために、go run
が引数なしで呼び出された場合にエラーを返すように変更されました。これにより、ユーザーは実行したいGoソースファイルを常に明示的に指定する必要があるため、意図しないファイルの選択やビルド制約の無視といった問題が回避されます。また、これによりgo run
の挙動が他のgo
サブコマンドの「引数なしの場合はカレントパッケージを対象とするが、ファイルリストを自動生成しない」という原則に近づき、Goツールの全体的な一貫性が保たれます。
Go 1.1のリリースが迫っていたため、より洗練された「引数なしでカレントパッケージのmain
を実行する」機能の実装は間に合わないと判断され、一時的に機能を元に戻すことで、将来的に適切な実装を行うための選択肢を残すという戦略が取られました。
コアとなるコードの変更箇所
このコミットでは、以下の2つのファイルが変更されています。
src/cmd/go/doc.go
src/cmd/go/run.go
src/cmd/go/doc.go
の変更
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -367,7 +367,7 @@ Compile and run Go program
Usage:
- go run [build flags] [gofiles...] [arguments...]
+ go run [build flags] gofiles... [arguments...]
Run compiles and runs the main package comprising the named Go source files.
If no files are named, it compiles and runs all non-test Go source files.
Usage:
の説明行が変更されています。- 変更前:
go run [build flags] [gofiles...] [arguments...]
- 変更後:
go run [build flags] gofiles... [arguments...]
- 変更前:
If no files are named, it compiles and runs all non-test Go source files.
という説明が削除されています。
src/cmd/go/run.go
の変更
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -8,12 +8,11 @@ import (
"fmt"
"os"
"os/exec"
- "path/filepath"
"strings"
)
var cmdRun = &Command{
- UsageLine: "run [build flags] [gofiles...] [arguments...]"",
+ UsageLine: "run [build flags] gofiles... [arguments...]"",
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
@@ -46,18 +45,7 @@ func runRun(cmd *Command, args []string) {
}\n\tfiles, cmdArgs := args[:i], args[i:]
if len(files) == 0 {
-\t\tallFiles, err := filepath.Glob("*.go")
-\t\tif err != nil {
-\t\t\tfatalf("go run: %s", err)
-\t\t}
-\t\tfor _, file := range allFiles {
-\t\t\tif !strings.HasSuffix(file, "_test.go") {
-\t\t\t\tfiles = append(files, file)
-\t\t\t}
-\t\t}
-\t\tif len(files) == 0 {
-\t\t\tfatalf("go run: no go files found")
-\t\t}
+\t\tfatalf("go run: no go files listed")
}
for _, file := range files {
\tif strings.HasSuffix(file, "_test.go") {
import
文から"path/filepath"
が削除されています。これは、filepath.Glob
が使用されなくなったためです。cmdRun
のUsageLine
フィールドがdoc.go
と同様に変更されています。runRun
関数の内部で、len(files) == 0
(つまり、go run
に引数が指定されなかった場合)の処理が大きく変更されています。- 変更前は、
filepath.Glob("*.go")
を使ってカレントディレクトリのGoファイルを検索し、_test.go
以外のファイルをfiles
スライスに追加していました。そして、それでもファイルが見つからなければ"go run: no go files found"
というエラーを出していました。 - 変更後は、単に
"go run: no go files listed"
というエラーを即座に返すようになっています。
- 変更前は、
コアとなるコードの解説
このコミットの核心は、src/cmd/go/run.go
内の runRun
関数における、引数なしでgo run
が呼び出された際のロジックの変更です。
変更前は、if len(files) == 0
のブロック内で、filepath.Glob("*.go")
を使用してカレントディレクトリ内のすべてのGoソースファイルを検索し、その中からテストファイル(_test.go
)を除外して実行対象としていました。この自動検出機能は、一見便利に見えますが、Goのビルドシステムが持つ複雑なビルド制約やファイル名制約(例: _amd64.go
、_linux.go
など)を適切に処理できませんでした。これにより、意図しないファイルがコンパイル対象に含まれたり、必要なファイルが無視されたりする問題が発生していました。
変更後は、この自動検出ロジックが完全に削除され、if len(files) == 0
の場合は、即座に fatalf("go run: no go files listed")
というエラーメッセージを出力して終了するようになりました。これにより、ユーザーはgo run
コマンドを使用する際に、実行したいGoソースファイルを常に明示的に指定する必要があります。
この変更は、go run
の挙動を他のgo
サブコマンド(例: go build
, go test
)と一貫させることを目的としています。これらのコマンドは、引数なしで実行された場合、カレントディレクトリのパッケージ全体を対象としますが、特定のファイルを自動的に検索してリストアップするような挙動はしません。go run
も同様に、ファイルが明示的に指定されない場合はエラーとするようにすることで、Goツールの全体的な設計原則に沿う形になりました。
また、src/cmd/go/doc.go
と src/cmd/go/run.go
の UsageLine
の変更は、この新しい挙動をユーザーに明確に伝えるためのドキュメント上の修正です。[gofiles...]
から gofiles...
へと変更することで、ファイル指定がオプションではなく必須であることを示しています。さらに、自動検出に関する説明文も削除されています。
このコミットは、Go 1.1のリリースが迫る中で、より洗練されたgo run
の「引数なし」挙動(例えば、カレントディレクトリのmain
パッケージを自動的に実行する)を実装する時間がなかったため、一時的に機能を元に戻し、将来的に適切な形で再実装するための余地を残すという現実的な判断の結果でもあります。
関連リンク
- 元の変更セット(CL 8119049)のレビューログからの引用:
https://golang.org/cl/8797049
参考にした情報源リンク
- コミットメッセージの内容
- Go言語の公式ドキュメント(
go run
,go build
,go test
コマンド、ビルド制約、ファイル名制約に関する一般的な知識) - Go言語のソースコード(
src/cmd/go/doc.go
,src/cmd/go/run.go
)