[インデックス 11650] ファイルの概要
コミット
commit 98257750f483238f2895d6077bf42e7c91cd8f3d
Author: Olivier Duperray <duperray.olivier@gmail.com>
Date: Mon Feb 6 12:10:49 2012 -0500
misc/goplay: use go tool "run"
Fixes #2872
R=andybalholm, rsc
CC=golang-dev
https://golang.org/cl/5608056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/98257750f483238f2895d6077bf42e7c91cd8f3d
元コミット内容
このコミットは、Go言語のmisc/goplay
ツールにおいて、プログラムのコンパイルと実行方法を従来のgomake install
および手動でのコンパイル・リンク(5g
/6g
/8g
と5l
/6l
/8l
コマンド)から、go tool "run"
コマンドを使用するように変更するものです。これにより、goplay
のセットアップと実行が簡素化され、Goツールチェーンの進化に合わせた改善が図られています。
変更の背景
この変更の背景には、Go言語のツールチェーンの成熟と、開発者体験の向上という目的があります。初期のGo言語では、プログラムのコンパイルやリンクには5g
(Go 1.5以降はgo tool compile
)、6g
、8g
といったアーキテクチャ固有のコンパイラコマンドや、5l
(Go 1.5以降はgo tool link
)、6l
、8l
といったリンカコマンドを直接使用する必要がありました。しかし、Go 1のリリースに向けて、より統合されたgo
コマンドが導入され、そのサブコマンドとしてgo run
が提供されるようになりました。
go run
コマンドは、Goソースファイルを直接実行するための便利なコマンドであり、内部的にはコンパイルとリンクを自動的に行い、一時的な実行可能ファイルを生成して実行します。これにより、開発者は手動でコンパイルやリンクのステップを踏む必要がなくなり、より迅速にコードのテストや実行ができるようになりました。
goplay
は、Go PlaygroundのようなWebベースのGoコード実行環境を提供するツールであり、その性質上、ユーザーが入力したコードを動的にコンパイル・実行する必要があります。このコミットは、goplay
が古い手動コンパイル・リンクの仕組みに依存していたのを、よりモダンで簡潔なgo run
コマンドに移行することで、コードベースの保守性を高め、将来的なGoツールチェーンの変更にも対応しやすくすることを目的としています。また、Fixes #2872
とあるように、特定のバグや問題の解決にも寄与しています。
前提知識の解説
Go言語のツールチェーン
Go言語には、コードのビルド、テスト、実行などを支援する強力なツールチェーンが付属しています。その中心となるのがgo
コマンドです。
go build
: Goソースコードをコンパイルして実行可能ファイルを生成します。go run
: Goソースファイルをコンパイルし、生成された実行可能ファイルを一時的に実行します。開発中のスクリプトや簡単なプログラムの実行に便利です。内部的にはgo build
と実行を組み合わせたものです。go install
: パッケージをコンパイルし、その結果をGOPATH/bin
(またはGOBIN
)にインストールします。go tool
: Goツールチェーンに含まれる低レベルのツール(コンパイラ、リンカなど)を直接実行するためのコマンドです。例えば、go tool compile
やgo tool link
などがあります。このコミットで変更される前のgoplay
は、これらの低レベルツールを直接呼び出していました。
Go Playground (golang.org/doc/play/
)
Go Playgroundは、Go言語の公式ウェブサイトで提供されているオンラインのGoコード実行環境です。ユーザーはブラウザ上でGoコードを記述し、サーバーサイドでコンパイル・実行された結果を即座に確認できます。misc/goplay
は、このGo Playgroundのローカル版のような位置づけのツールです。
os/exec
パッケージ
Go言語の標準ライブラリに含まれるos/exec
パッケージは、外部コマンドを実行するための機能を提供します。このコミットでは、goplay
がユーザーのGoコードをコンパイル・実行するために、このパッケージを使用してgo
コマンド(または以前は5g
/6g
/8g
や5l
/6l
/8l
)を呼び出しています。
os.TempDir()
と一時ファイル
os.TempDir()
関数は、システムの一時ディレクトリのパスを返します。goplay
は、ユーザーが入力したGoコードを一時ファイルとして保存し、それをコンパイル・実行するための一時的な実行可能ファイルもこのディレクトリに生成します。プログラムの実行後、これらのファイルは削除されます。
技術的詳細
このコミットの主要な技術的変更点は、Goプログラムのコンパイルと実行のワークフローを、手動でのコンパイラ/リンカ呼び出しからgo run
コマンドへの移行です。
変更前:
- ユーザーのGoコードを一時ファイル(例:
/tmp/compileXXXX.go
)に保存。 5g
/6g
/8g
(コンパイラ)を使って.go
ファイルをコンパイルし、オブジェクトファイル(例:/tmp/compileXXXX.6
)を生成。run(archChar+"g", "-o", obj, src)
5l
/6l
/8l
(リンカ)を使ってオブジェクトファイルをリンクし、実行可能ファイル(例:/tmp/compileXXXX
)を生成。run(archChar+"l", "-o", bin, obj)
- 生成された実行可能ファイルを実行。
run(bin)
- 一時ファイル(
.go
,.6
, 実行可能ファイル)を削除。
このアプローチでは、Goの実行環境のアーキテクチャ(GOARCH
)に応じて適切なコンパイラ/リンカ(5g
, 6g
, 8g
など)を選択する必要があり、runtime
パッケージを使用してGOARCH
を判定し、archChar
変数に格納していました。また、Windows環境では実行可能ファイルに.exe
拡張子を付加する処理も必要でした。
変更後:
- ユーザーのGoコードを一時ファイル(例:
/tmp/goplayXXXX.go
)に保存。 go run
コマンドを使って、この一時ファイルを直接実行。run("go", "run", x)
go run
が内部的にコンパイル、リンク、実行を処理するため、明示的なコンパイラ/リンカの呼び出しは不要になる。- 一時ファイル(
.go
ファイル)を削除。
この変更により、以下のメリットがもたらされます。
- コードの簡素化:
archChar
の判定ロジックや、コンパイル・リンクの各ステップを明示的に呼び出すコードが不要になり、goplay.go
のコード量が大幅に削減されました。 - 保守性の向上: Goツールチェーンの内部実装(コンパイラやリンカのコマンド名、引数など)に直接依存しなくなるため、将来的なGoのバージョンアップによるツールチェーンの変更に対して、
goplay
が影響を受けにくくなります。 - クロスプラットフォーム対応の簡素化:
go run
コマンドがプラットフォーム固有の実行可能ファイルの命名規則(例: Windowsの.exe
)や、アーキテクチャ固有のコンパイラ/リンカの選択を自動的に処理するため、goplay
側でこれらのロジックを記述する必要がなくなります。 - エラーハンドリングの簡素化:
go run
がコンパイル、リンク、実行の全てをラップするため、エラーハンドリングのポイントが一つに集約されます。
また、os.Chdir(os.TempDir())
の追加により、goplay
の実行ディレクトリを一時ディレクトリに変更しています。これは、go run
がカレントディレクトリを基準に動作する場合があるため、一時ファイルを生成する場所と実行コンテキストを一致させるための措置と考えられます。
コアとなるコードの変更箇所
misc/goplay/doc.go
--- a/misc/goplay/doc.go
+++ b/misc/goplay/doc.go
@@ -5,11 +5,9 @@
// Goplay is a web interface for experimenting with Go code.
// It is similar to the Go Playground: http://golang.org/doc/play/
//
-// To use goplay, first build and install it:
+// To use goplay:
// $ cd $GOROOT/misc/goplay
-// $ gomake install
-// Then, run it:
-// $ goplay
+// $ go run goplay.go
// and load http://localhost:3999/ in a web browser.
//
// You should see a Hello World program, which you can compile and run by
doc.go
では、goplay
の実行方法に関するドキュメントが更新されています。
- 変更前:
gomake install
でビルド・インストールし、goplay
を実行。 - 変更後:
go run goplay.go
で直接実行。
misc/goplay/goplay.go
--- a/misc/goplay/goplay.go
+++ b/misc/goplay/goplay.go
@@ -12,7 +12,6 @@ import (
"net/http"
"os"
"os/exec"
-\t"runtime"
"strconv"
"text/template"
)
@@ -25,25 +24,11 @@ var (
var (
// a source of numbers, for naming temporary files
uniq = make(chan int)
-\t// the architecture-identifying character of the tool chain, 5, 6, or 8
-\tarchChar string
)
func main() {\n flag.Parse()\n \n-\t// set archChar
-\tswitch runtime.GOARCH {\n-\tcase "arm":\n-\t\tarchChar = "5"\n-\tcase "amd64":\n-\t\tarchChar = "6"\n-\tcase "386":\n-\t\tarchChar = "8"\n-\tdefault:\n-\t\tlog.Fatalln("unrecognized GOARCH:", runtime.GOARCH)\n-\t}\n-\n \t// source of unique numbers
\tgo func() {\n \t\tfor i := 0; ; i++ {\n@@ -51,6 +36,12 @@ func main() {\n \t\t}\n \t}()\n \n+\t// go to TempDir
+\terr := os.Chdir(os.TempDir())\n+\tif err != nil {\n+\t\tlog.Fatal(err)\n+\t}\n+\n \thttp.HandleFunc("/", FrontPage)\n \thttp.HandleFunc("/compile", Compile)\n \tlog.Fatal(http.ListenAndServe(*httpListen, nil))\
@@ -69,25 +60,19 @@ func FrontPage(w http.ResponseWriter, req *http.Request) {\n }\n \n // Compile is an HTTP handler that reads Go source code from the request,\n-// compiles and links the code (returning any errors), runs the program, \n+// runs the program (returning any errors),\n // and sends the program\'s output as the HTTP response.\n func Compile(w http.ResponseWriter, req *http.Request) {\n-\t// x is the base name for .go, .6, executable files
-\tx := os.TempDir() + "/compile" + strconv.Itoa(<-uniq)\n-\tsrc := x + ".go"\n-\tobj := x + "." + archChar\n-\tbin := x\n-\tif runtime.GOOS == "windows" {\n-\t\tbin += ".exe"\n-\t}\n+\t// x is the base name for .go files
+\tx := "goplay" + strconv.Itoa(<-uniq) + ".go"\n \n \t// write request Body to x.go
-\tf, err := os.Create(src)\n+\tf, err := os.Create(x)\n \tif err != nil {\n \t\terror_(w, nil, err)\n \t\treturn\n \t}\n-\tdefer os.Remove(src)\n+\tdefer os.Remove(x)\n \tdefer f.Close()\n \t_, err = io.Copy(f, req.Body)\n \tif err != nil {\n@@ -96,26 +81,11 @@ func Compile(w http.ResponseWriter, req *http.Request) {\n \t}\n \tf.Close()\n \n-\t// build x.go, creating x.6
-\tout, err := run(archChar+"g", "-o", obj, src)\n-\tdefer os.Remove(obj)\n-\tif err != nil {\n-\t\terror_(w, out, err)\n-\t\treturn\n-\t}\n-\n-\t// link x.6, creating x (the program binary)
-\tout, err = run(archChar+"l", "-o", bin, obj)\n-\tdefer os.Remove(bin)\n-\tif err != nil {\n-\t\terror_(w, out, err)\n-\t\treturn\n-\t}\n-\n \t// run x
-\tout, err = run(bin)\n+\tout, err := run("go", "run", x)\n \tif err != nil {\n \t\terror_(w, out, err)\n+\t\treturn\n \t}\n \n \t// write the output of x as the http response
コアとなるコードの解説
goplay.go
の変更点詳細
-
runtime
パッケージの削除とarchChar
変数の廃止:- 変更前は、
runtime.GOARCH
を使用して現在のアーキテクチャ(arm
,amd64
,386
)を判定し、それに対応するコンパイラ/リンカのサフィックス(5
,6
,8
)をarchChar
に設定していました。 go run
コマンドを使用することで、このアーキテクチャ判定とそれに基づくツール選択のロジックが不要になったため、runtime
パッケージのインポートとarchChar
変数の定義、およびその初期化ロジックが削除されました。
- 変更前は、
-
main
関数でのos.Chdir(os.TempDir())
の追加:main
関数の冒頭で、カレントディレクトリをシステムの一時ディレクトリ(os.TempDir()
が返すパス)に変更しています。- これは、
go run
コマンドが相対パスで指定されたソースファイルを処理する際に、一時ファイルが生成される場所と実行コンテキストを一致させるためと考えられます。これにより、go run
が一時ファイルを正しく見つけ、処理できるようになります。
-
Compile
関数内のコンパイル・リンク処理の簡素化:- 一時ファイル名の変更: 以前は
compileXXXX.go
、compileXXXX.6
、compileXXXX
といった形式で一時ファイルを生成していましたが、go run
を使用するにあたり、単にgoplayXXXX.go
という形式の一時ファイル名に変更されました。 - コンパイル・リンクステップの削除:
- 変更前は、
run(archChar+"g", ...)
でコンパイル、run(archChar+"l", ...)
でリンクを明示的に行っていました。 - これらのステップが完全に削除され、代わりに
run("go", "run", x)
という単一のコマンド呼び出しに置き換えられました。 - これにより、
obj
(オブジェクトファイル)やbin
(実行可能ファイル)といった中間ファイルに関する処理(作成、削除)も不要になりました。
- 変更前は、
- Windows向け
.exe
拡張子処理の削除: 以前はruntime.GOOS == "windows"
の場合に実行可能ファイルに.exe
拡張子を付加するロジックがありましたが、go run
がこれを自動的に処理するため、このロジックも削除されました。 - エラーハンドリングの調整:
go run
の呼び出しでエラーが発生した場合の処理が、以前のコンパイル・リンク・実行の各ステップでのエラーハンドリングと統合されました。
- 一時ファイル名の変更: 以前は
これらの変更により、goplay
はGoツールチェーンのより高レベルな抽象化を利用するようになり、コードの可読性、保守性、そして将来的な互換性が向上しました。
関連リンク
- Go言語公式ウェブサイト: https://golang.org/
- Go Playground: https://golang.org/doc/play/
- Goコマンドのドキュメント: https://golang.org/cmd/go/
os/exec
パッケージのドキュメント: https://golang.org/pkg/os/exec/os
パッケージのドキュメント: https://golang.org/pkg/os/
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/98257750f483238f2895d6077bf42e7c91cd8f3d
- Go issue #2872: https://golang.org/issue/2872 (コミットメッセージに記載されているFixesのリンク)
- Go CL 5608056: https://golang.org/cl/5608056 (コミットメッセージに記載されているコードレビューのリンク)
- Go 1 Release Notes (特にツールチェーンに関する変更点): https://golang.org/doc/go1 (Go 1のリリースは2012年3月であり、このコミットはその直前の変更であるため、Go 1のツールチェーン統合の文脈で理解できる)
- Go toolchain documentation (e.g.,
go help build
,go help run
): https://golang.org/cmd/go/ - Go compiler and linker (e.g.,
go tool compile
,go tool link
): https://golang.org/cmd/go/#hdr-Go_tool_commands