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

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

このコミットは、Go言語のコマンドラインツール go run のエラーメッセージを改善し、よりユーザーフレンドリーにするための変更です。具体的には、go run コマンドにGoファイルが指定されなかった場合と、main パッケージではないGoファイルを指定した場合のエラーメッセージを修正しています。

コミット

commit 85ae6a18b5878a3fecd62ffc6887906e0c8c4d15
Author: Russ Cox <rsc@golang.org>
Date:   Wed Mar 7 00:01:57 2012 -0500

    cmd/go: fix run errors
    
    $ go run
    go run: no go files listed
    $ go run ../../pkg/math/bits.go
    go run: cannot run non-main package
    $
    
    Fixes #3168.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5755064

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

https://github.com/golang/go/commit/85ae6a18b5878a3fecd62ffc6887906e0c8c4d15

元コミット内容

cmd/go: fix run errors

このコミットは、go run コマンドが以下のような状況で出力するエラーメッセージを修正します。

  1. go run の後にGoファイルが指定されなかった場合。 変更前: (おそらく一般的なGoツールのエラーメッセージ) 変更後: go run: no go files listed
  2. main パッケージではないGoファイルを go run で実行しようとした場合。 変更前: cannot run non-main package 変更後: go run: cannot run non-main package

この変更は、Issue #3168 を修正するものです。

変更の背景

go run コマンドは、Go言語のソースファイルをコンパイルして実行するための便利なツールです。しかし、ユーザーが誤った使い方をした場合、出力されるエラーメッセージが必ずしも明確ではないことがありました。

特に、以下の2つのケースが問題でした。

  1. 引数なしでの実行: go run とだけ入力して実行した場合、ユーザーはGoファイルを指定し忘れたことに気づきにくい可能性がありました。より具体的なエラーメッセージを提供することで、ユーザーがすぐに問題を特定し、修正できるようにすることが目的です。
  2. main パッケージの実行: go run は実行可能なプログラム(つまり main パッケージ)を対象としています。ライブラリパッケージなどを誤って go run で実行しようとした場合、既存のエラーメッセージ cannot run non-main package は正しいものの、どのコマンドがそのエラーを出力しているのかが不明瞭でした。go run: というプレフィックスを追加することで、エラーの発生源を明確にし、ユーザーが go run の正しい使い方を理解する手助けとなります。

これらの改善は、Goツールの全体的なユーザーエクスペリエンスを向上させ、開発者がより効率的に作業できるようにすることを目的としています。

前提知識の解説

Go言語のパッケージと main パッケージ

Go言語のプログラムは「パッケージ」という単位で構成されます。パッケージは関連するGoソースファイルの集まりです。 特別なパッケージとして main パッケージがあります。main パッケージは、実行可能なプログラムのエントリポイント(main 関数)を含むパッケージであり、Goプログラムとして単独で実行できるのは main パッケージのみです。ライブラリとして機能するパッケージは main パッケージではありません。

go run コマンド

go run コマンドは、Goソースファイルをコンパイルし、その場で実行するためのGoツールチェーンのコマンドです。通常、開発中に小さなプログラムをテストしたり、スクリプトのようにGoプログラムを実行したりする際に使用されます。 go run は、指定されたGoファイルを一時的にコンパイルし、生成されたバイナリを実行します。この際、指定されたファイルが main パッケージの一部であり、main 関数を含んでいる必要があります。

fatalf 関数

Go言語のツールや内部処理でよく使われるエラー報告関数の一つです。fmt.Errorf のようにフォーマットされた文字列を生成し、通常はプログラムを終了させる(os.Exit(1) を呼び出すなど)動作を伴います。このコミットでは、ユーザーにエラーメッセージを表示し、プログラムの実行を停止するために使用されています。

技術的詳細

このコミットは、src/cmd/go/run.go ファイル内の runRun 関数を変更しています。runRun 関数は go run コマンドの実際の処理を担う部分です。

変更点は大きく2つあります。

  1. Goファイルが指定されなかった場合のエラーハンドリングの追加: 変更前は、go run の後にファイルが指定されなかった場合、files スライスが空になります。この場合、goFilesPackage(files) が呼び出され、その結果としてエラーが発生する可能性はありますが、具体的な「ファイルが指定されていない」というメッセージは出ませんでした。 変更後では、files スライスの長さが0であるかを明示的にチェックし、もし0であれば fatalf("go run: no go files listed") を呼び出して、より明確なエラーメッセージを出力するようにしました。

  2. main パッケージ実行時のエラーメッセージの改善: p.Name != "main" の条件は、指定されたGoファイルが main パッケージではないことを検出します。 変更前は fatalf("cannot run non-main package") と出力していました。 変更後では fatalf("go run: cannot run non-main package") と変更し、エラーメッセージの先頭に go run: というプレフィックスを追加することで、このエラーが go run コマンドによって出力されたものであることを明確にしています。

これらの変更は、go run コマンドの堅牢性を高め、ユーザーがコマンドの誤用から生じる問題をより迅速に診断できるようにすることを目的としています。

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

src/cmd/go/run.go ファイルの runRun 関数内。

--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -42,12 +42,15 @@ func runRun(cmd *Command, args []string) {
 		i++
 	}
 	files, cmdArgs := args[:i], args[i:]
+	if len(files) == 0 {
+		fatalf("go run: no go files listed")
+	}
 	p := goFilesPackage(files)
 	if p.Error != nil {
 		fatalf("%s", p.Error)
 	}
 	if p.Name != "main" {
-		fatalf("cannot run non-main package")
+		fatalf("go run: cannot run non-main package")
 	}
 	p.target = "" // must build - not up to date
 	a1 := b.action(modeBuild, modeBuild, p)

コアとなるコードの解説

変更点1: if len(files) == 0 の追加

	files, cmdArgs := args[:i], args[i:]
	if len(files) == 0 {
		fatalf("go run: no go files listed")
	}

このコードブロックは、go run コマンドに渡された引数からGoファイルとコマンドライン引数を分離した後、Goファイルが一つも指定されていない場合に実行されます。 len(files) == 0 は、files スライス(go run で実行しようとしているGoファイルのリスト)が空であるかどうかをチェックします。 もし空であれば、fatalf("go run: no go files listed") が呼び出され、go run: no go files listed というエラーメッセージが表示されてプログラムが終了します。これにより、ユーザーはファイル指定の不足を即座に理解できます。

変更点2: fatalf メッセージの変更

 	if p.Name != "main" {
-		fatalf("cannot run non-main package")
+		fatalf("go run: cannot run non-main package")
 	}

この部分は、goFilesPackage(files) によって解析されたパッケージ p の名前が "main" ではない場合に実行されます。 p.Name != "main" は、指定されたGoファイルが実行可能な main パッケージではないことを意味します。 以前は fatalf("cannot run non-main package") とだけ出力していましたが、このコミットで fatalf("go run: cannot run non-main package") に変更されました。 この変更により、エラーメッセージの冒頭に go run: というプレフィックスが追加され、このエラーが go run コマンドの制約によるものであることが明確になります。これにより、ユーザーはエラーの原因をより正確に把握し、適切な対応(例えば、go run ではなく go buildgo install を使用する、あるいは main パッケージのファイルを指定する)を取ることができます。

関連リンク

参考にした情報源リンク

  • コミットメッセージに記載されているGoのコードレビューシステムへのリンク: https://golang.org/cl/5755064
  • コミットメッセージに記載されているIssue #3168: このIssueは、コミット日付(2012年3月7日)から判断すると、現在のGitHubリポジトリのIssueトラッカーで検索される最近のIssue(例: golang/vscode-go のIssue #3168)とは異なる、Goプロジェクトの初期のIssueトラッカー(おそらくGoogle Codeなど)に存在していたものと考えられます。直接的なリンクは現在のGitHubリポジトリからは見つかりませんでしたが、コミット内容から go run コマンドのエラーメッセージに関する改善要求であったと推測されます。