[インデックス 16739] ファイルの概要
このコミットは、Go言語のプレイグラウンドツールである misc/goplay
において、プログラムの実行方法を go build
から go run
に変更するものです。これにより、main
パッケージではないプログラムがリンクエラーを適切に報告できるようになります。
コミット
commit 8529d99e1d447e930a1172e5f30fe6c1f46922e6
Author: ChaiShushan <chaishushan@gmail.com>
Date: Fri Jul 12 09:41:10 2013 +1000
misc/goplay: use `go run x.go` instead of `go build x.go`
when the program is not main package, `go run x.go` can't return the
link error message. so use `go run x.go` in instead `go build x.go`.
Fixes #5865.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/11165043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8529d99e1d447e930a1172e5f30fe6c1f46922e6
元コミット内容
misc/goplay
: go build x.go
の代わりに go run x.go
を使用する。
プログラムが main
パッケージではない場合、go run x.go
はリンクエラーメッセージを返せません。そのため、go build x.go
の代わりに go run x.go
を使用します。
Fixes #5865.
R=golang-dev, adg CC=golang-dev https://golang.org/cl/11165043
変更の背景
この変更は、Go言語のIssue #5865 に対応するものです。元の goplay
ツールでは、Goプログラムのコンパイルと実行に go build
コマンドを使用していました。しかし、go build
は実行可能ファイルを生成する際に、main
パッケージではないGoファイル(例えば、ライブラリとして設計されたファイルや、単体テストファイルなど)に対しては、リンクエラーを適切に報告しないという問題がありました。
具体的には、go build
は main
パッケージを持たないGoファイルをビルドしようとすると、通常はエラーを発生させますが、そのエラーメッセージが goplay
の出力に適切に反映されないケースがありました。これにより、ユーザーが goplay
で main
パッケージではないコードを試した際に、期待するエラーメッセージが得られず、デバッグが困難になるというユーザーエクスペリエンス上の問題が生じていました。
このコミットは、この問題を解決するために、go build
の代わりに go run
を使用するように変更します。go run
は、Goソースファイルをコンパイルして実行するコマンドですが、main
パッケージを持たないファイルに対しては、より適切なエラーハンドリングを提供します。特に、リンクエラー(例えば、未定義の関数呼び出しや、依存関係の欠落など)が発生した場合に、そのエラーを goplay
の出力に正確に反映させることが可能になります。
前提知識の解説
このコミットを理解するためには、以下のGo言語の基本的な概念とコマンドについて理解しておく必要があります。
main
パッケージとmain
関数: Go言語の実行可能なプログラムは、必ずmain
パッケージに属し、main
関数を持っていなければなりません。プログラムのエントリーポイントとなります。main
パッケージ以外のパッケージは、通常、ライブラリとして機能し、他のプログラムからインポートされて使用されます。go build
コマンド:- Goソースコードをコンパイルして実行可能バイナリを生成するコマンドです。
go build [パッケージパスまたはファイルパス]
の形式で使用します。main
パッケージのGoファイルを指定した場合、そのパッケージから実行可能ファイルを生成します。main
パッケージではないGoファイルを指定した場合、通常はエラーとなりますが、そのエラーの報告方法がgo run
とは異なります。-o
フラグを使用して、出力される実行可能ファイルの名前を指定できます。
go run
コマンド:- Goソースコードをコンパイルし、その場で実行するコマンドです。
go run [ファイルパス]
の形式で使用します。- 内部的には一時的な実行可能ファイルを生成し、それを実行します。実行後、一時ファイルは削除されます。
main
パッケージのGoファイルを指定した場合、そのプログラムを実行します。main
パッケージではないGoファイルを指定した場合、コンパイルエラーやリンクエラーをより直接的に報告します。これは、go run
が単一のファイルまたはパッケージを「実行」することを目的としているため、実行に必要なすべての依存関係が解決されていることを期待し、そうでない場合に明確なエラーを出す傾向があるためです。
- リンクエラー:
- プログラムのコンパイル段階ではなく、リンク段階で発生するエラーです。
- コンパイルは個々のソースファイルを機械語に変換するプロセスですが、リンクはそれらの機械語ファイルやライブラリを結合して最終的な実行可能ファイルを生成するプロセスです。
- リンクエラーは、例えば、呼び出されている関数がどこにも定義されていない場合(未解決のシンボル)、または必要なライブラリが見つからない場合などに発生します。
misc/goplay
:- Go言語の公式リポジトリに含まれるツールの一つで、Goコードをブラウザ上で実行できるプレイグラウンド機能を提供します。
- ユーザーが入力したGoコードをサーバーサイドでコンパイル・実行し、その結果をブラウザに返します。
技術的詳細
このコミットの技術的な核心は、Goプログラムのコンパイルと実行における go build
と go run
の挙動の違い、特に main
パッケージではないGoファイルに対するエラーハンドリングの違いを理解し、それを goplay
ツールに適用した点にあります。
go build
の問題点:
goplay
は、ユーザーが入力したGoコードを一時ファイル (x.go
) として保存し、それをコンパイル・実行していました。以前は go build -o bin x.go
のように go build
を使用して実行可能ファイル (bin
) を生成し、その後その bin
を実行していました。
しかし、ユーザーが main
パッケージではないGoコード(例: package mylib; func MyFunc() {}
)を入力した場合、go build
は実行可能ファイルを生成できません。このとき、go build
はエラーを返しますが、そのエラーメッセージが goplay
の期待する形式でなかったり、あるいは goplay
がそのエラーを適切に捕捉・表示できなかったりするケースがありました。特に、リンクエラーのような、実行可能ファイルを生成する上で不可欠な問題が、ユーザーに分かりにくい形でしか伝わらないことが問題でした。
go run
への変更の利点:
go run x.go
は、指定されたGoファイルをコンパイルし、もしそれが main
パッケージであり実行可能であれば、その場で実行します。もし main
パッケージでなければ、go run
はコンパイルエラーまたはリンクエラーをより直接的に、かつ標準エラー出力に明確に出力します。
goplay
の文脈では、ユーザーが main
パッケージではないコードを入力した場合、それは実行可能ではないため、go run
はその事実をエラーとして明確に報告します。このエラーメッセージは、goplay
が捕捉しやすく、ユーザーに「これは実行可能なプログラムではありません」という情報を正確に伝えることができます。
また、go run
は一時的な実行可能ファイルを生成し、実行後に自動的に削除するため、go build
で明示的に os.Remove(bin)
を呼び出す必要がなくなり、コードが簡潔になります。
runtime
パッケージの削除:
以前のコードでは、runtime.GOOS
を使用してWindows環境での実行可能ファイルの拡張子 (.exe
) を考慮していましたが、go run
を使用する場合、この処理はGoツールチェーンが内部的に処理するため、明示的に runtime
パッケージをインポートしてOSを判別する必要がなくなりました。これにより、不要な依存関係が削除され、コードのクリーンアップにも繋がっています。
コアとなるコードの変更箇所
変更は misc/goplay/goplay.go
ファイル内で行われています。
--- a/misc/goplay/goplay.go
+++ b/misc/goplay/goplay.go
@@ -14,7 +14,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
- "runtime"
"strconv"
"text/template"
)
@@ -92,10 +91,6 @@ func compile(req *http.Request) (out []byte, err error) {
// x is the base name for .go, .6, executable files
x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
src := x + ".go"
- bin := x
- if runtime.GOOS == "windows" {
- bin += ".exe"
- }
// rewrite filename in error output
defer func() {
@@ -116,16 +111,13 @@ func compile(req *http.Request) (out []byte, err error) {
\t\treturn
\t}
-\t// build x.go, creating x
-\tout, err = run(dir, "go", "build", "-o", bin, file)
-\tdefer os.Remove(bin)
+\t// go run x.go
+\tout, err = run(dir, "go", "run", file)
\tif err != nil {\
\t\treturn
\t}
-\n-\t// run x
-\treturn run("", bin)
+\treturn out, nil
}\
コアとなるコードの解説
-
runtime
パッケージの削除:- "runtime"
go run
コマンドが内部的にOSに応じた実行可能ファイルの処理を行うため、runtime.GOOS
を使用してWindowsの.exe
拡張子を付与するロジックが不要になりました。これに伴い、runtime
パッケージのインポートも削除されました。 -
実行可能ファイル名
bin
の定義とWindows対応ロジックの削除:- bin := x - if runtime.GOOS == "windows" { - bin += ".exe" - }
上記と同様の理由で、
bin
変数の定義と、Windows環境での.exe
拡張子を付与する条件分岐が削除されました。go run
は直接ソースファイルを指定するため、明示的なバイナリファイル名を管理する必要がなくなります。 -
go build
からgo run
へのコマンド変更:- // build x.go, creating x - out, err = run(dir, "go", "build", "-o", bin, file) - defer os.Remove(bin) + // go run x.go + out, err = run(dir, "go", "run", file)
これが最も重要な変更点です。
- 以前は
go build -o [バイナリ名] [ソースファイル名]
を実行し、指定されたバイナリ名で実行可能ファイルを生成していました。 - 変更後は
go run [ソースファイル名]
を実行します。これにより、Goツールチェーンが一時的な実行可能ファイルを生成し、それを実行します。 defer os.Remove(bin)
も削除されました。これは、go run
が実行後に一時ファイルを自動的にクリーンアップするため、手動で削除する必要がなくなったためです。
- 以前は
-
実行ロジックの簡素化:
- // run x - return run("", bin) + return out, nil
以前は
go build
で生成したバイナリ (bin
) を別途run("", bin)
で実行していました。しかし、go run
はコンパイルと実行を一度に行うため、run
関数の呼び出しが一度で済み、その結果を直接返すように変更されました。これにより、コードのフローが大幅に簡素化されています。
これらの変更により、goplay
は main
パッケージではないGoコードに対しても、より正確で分かりやすいエラーメッセージをユーザーに提供できるようになり、ツールの使い勝手が向上しました。
関連リンク
- Go言語のIssue #5865: https://github.com/golang/go/issues/5865
- Go言語の変更リスト (CL): https://golang.org/cl/11165043
参考にした情報源リンク
- Go Command
go build
: https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies - Go Command
go run
: https://pkg.go.dev/cmd/go#hdr-Run_main_package - Go言語のパッケージとコマンドに関する公式ドキュメント (Go Modules): https://go.dev/doc/modules/
- Go言語の
main
パッケージとmain
関数に関する解説: https://go.dev/doc/code (Goのコード構成に関する一般的な情報) - Go言語の
runtime
パッケージ: https://pkg.go.dev/runtime