[インデックス 11038] ファイルの概要
このコミットは、Go言語のコマンドラインツールである go
コマンドの run
サブコマンドに、実行するGoプログラムへの引数渡し機能を追加するものです。これにより、go run
を使用してGoプログラムをコンパイル・実行する際に、そのプログラム自体が受け取るべきコマンドライン引数を --
(ダブルハイフン) を区切りとして指定できるようになります。
コミット
commit fd1322828c656813c320ab556bffe8eafeb57976
Author: Eric Eisner <eric.d.eisner@gmail.com>
Date: Fri Jan 6 09:23:00 2012 +1100
cmd/go: Pass arguments to command for run
Command arguments are separated from input .go file arguments
by a -- separator.
R=rsc, golang-dev, adg
CC=golang-dev
https://golang.org/cl/5514046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fd1322828c656813c320ab556bffe8eafeb57976
元コミット内容
cmd/go: Pass arguments to command for run
Command arguments are separated from input .go file arguments
by a -- separator.
R=rsc, golang-dev, adg
CC=golang-dev
https://golang.org/cl/5514046
変更の背景
go run
コマンドは、指定されたGoソースファイルをコンパイルし、その場で実行するための便利なツールです。しかし、このコミット以前は、go run
コマンド自体に渡す引数と、go run
によって実行されるGoプログラムに渡す引数を区別する明確な方法がありませんでした。
例えば、go run main.go -v
と実行した場合、-v
が go run
コマンドのオプションとして解釈されるのか、それとも main.go
が受け取るべき引数として解釈されるのかが曖昧でした。特に、実行されるプログラムが独自のコマンドライン引数を必要とする場合、この曖昧さは大きな問題となります。
このコミットは、この問題を解決するために、標準的なコマンドラインの慣習である --
(ダブルハイフン) を導入し、go run
コマンドの引数と、実行されるGoプログラムへの引数を明確に分離できるようにすることを目的としています。これにより、開発者は go run
を使ってプログラムをテストする際に、より柔軟に引数を渡せるようになります。
前提知識の解説
go run
コマンド
go run
は、Go言語のツールチェインが提供するコマンドの一つで、Goのソースファイルを一時的にコンパイルし、その実行可能ファイルを即座に実行します。開発中の小さなプログラムのテストや、スクリプトのような使い方ができるため、非常に便利です。通常、go run main.go
のように使用します。
コマンドライン引数
コマンドライン引数とは、プログラムを実行する際に、そのプログラムに渡される追加の情報のことです。例えば、ls -l /tmp
というコマンドでは、-l
と /tmp
が ls
コマンドへの引数です。Goプログラムでは、os.Args
スライスを通じてこれらの引数にアクセスできます。
--
(ダブルハイフン) の慣習
Unix系システムにおけるコマンドラインの一般的な慣習として、--
(ダブルハイフン) は、それ以降の文字列をオプションではなく、ファイル名やその他の非オプション引数として扱うことを示すために使用されます。これにより、ハイフンで始まるファイル名などを誤ってオプションとして解釈されることを防ぎます。このコミットでは、この慣習を go run
コマンドに適用し、go run
自身のオプションと、実行されるGoプログラムへの引数を区別するために利用しています。
技術的詳細
このコミットの技術的な核心は、go run
コマンドの引数解析ロジックを変更し、--
を特別な区切り文字として認識させる点にあります。
-
action
構造体の拡張:src/cmd/go/build.go
に定義されているaction
構造体は、go
コマンドがビルドや実行の各ステップを管理するための内部的なデータ構造です。このコミットでは、action
構造体にargs []string
という新しいフィールドが追加されました。これは、go run
によって実行されるプログラムに渡されるべきコマンドライン引数を保持するために使用されます。 -
go run
の引数解析の変更:src/cmd/go/run.go
にあるrunRun
関数は、go run
コマンドが呼び出されたときに実行される主要な関数です。この関数内で、新しいsplitArgs
関数が導入されました。splitArgs
関数は、go run
に渡されたすべての引数を走査し、--
が出現するかどうかをチェックします。--
の前にある引数は、go run
コマンドが処理すべきGoソースファイル名として扱われます。--
の後にある引数は、実行されるGoプログラムに渡されるべき引数として扱われ、action
構造体のargs
フィールドに格納されます。
-
プログラム実行時の引数渡し:
src/cmd/go/run.go
のrunProgram
メソッドは、実際にコンパイルされたGoプログラムを実行する役割を担います。このメソッドは、以前はコンパイルされたプログラムのパスのみをrun
関数に渡していました。このコミットにより、action
構造体に格納されたargs
(つまり、--
の後に指定された引数) もrun
関数に渡されるように変更されました。これにより、実行されるGoプログラムがこれらの引数を受け取れるようになります。
これらの変更により、go run
コマンドは、go run [goファイル] -- [プログラム引数]
という形式で、Goプログラムへの引数を透過的に渡すことができるようになりました。
コアとなるコードの変更箇所
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -157,6 +157,7 @@ type action struct {
deps []*action // actions that must happen before this one
triggers []*action // inverse of deps
cgo *action // action for cgo binary if needed
+ args []string // additional args for runProgram
f func(*builder, *action) error // the action itself (nil = no-op)
ignoreFail bool // whether to run f even if dependencies fail
action
構造体に args []string
フィールドが追加されました。これは、go run
によって実行されるプログラムに渡されるコマンドライン引数を保持するためのものです。
src/cmd/go/run.go
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -12,7 +12,7 @@ func init() {
}
var cmdRun = &Command{
- UsageLine: "run [-a] [-n] [-x] gofiles...",
+ UsageLine: "run [-a] [-n] [-x] gofiles... [-- arguments...]",
Short: "compile and run Go program",
Long: `
Run compiles and runs the main package comprising the named Go source files.
@@ -32,16 +32,34 @@ var runX = cmdRun.Flag.Bool(\"x\", false, \"\")
func runRun(cmd *Command, args []string) {
var b builder
b.init(*runA, *runN, *runX)
-\tp := goFilesPackage(args, \"\")
+\tfiles, args := splitArgs(args)\n+\tp := goFilesPackage(files, \"\")
\tp.target = \"\" // must build - not up to date
\ta1 := b.action(modeBuild, modeBuild, p)\n-\ta := &action{f: (*builder).runProgram, deps: []*action{a1}}\n+\ta := &action{f: (*builder).runProgram, args: args, deps: []*action{a1}}\n \tb.do(a)\n }\n \n // runProgram is the action for running a binary that has already
// been compiled. We ignore exit status.\n func (b *builder) runProgram(a *action) error {\n-\trun(a.deps[0].target)\n+\targs := append([]string{a.deps[0].target}, a.args...)\n+\trun(args...)\n \treturn nil\n }\n+\n+// Return the argument slices before and after the \"--\"\n+func splitArgs(args []string) (before, after []string) {\n+\tdashes := len(args)\n+\tfor i, arg := range args {\n+\t\tif arg == \"--\" {\n+\t\t\tdashes = i\n+\t\t\tbreak\n+\t\t}\n+\t}\n+\tbefore = args[:dashes]\n+\tif dashes < len(args) {\n+\t\tafter = args[dashes+1:]\n+\t}\n+\treturn\n+}\n```
## コアとなるコードの解説
### `src/cmd/go/run.go` の変更点詳細
1. **`cmdRun.UsageLine` の変更**:
`UsageLine` は、`go help run` と入力した際に表示されるコマンドの利用方法の概要です。ここに `[-- arguments...]` が追加され、`--` の後にプログラムへの引数を指定できることが明示されました。
2. **`runRun` 関数の変更**:
* `files, args := splitArgs(args)`: 以前は `goFilesPackage(args, "")` で直接引数を渡していましたが、この行で新しく追加された `splitArgs` 関数を呼び出し、`go run` コマンド自身の引数 (`files`) と、実行されるGoプログラムへの引数 (`args`) を分離しています。
* `a := &action{f: (*builder).runProgram, args: args, deps: []*action{a1}}`: `action` 構造体を初期化する際に、`splitArgs` から得られたプログラムへの引数 `args` を、新しく追加された `action.args` フィールドに設定しています。これにより、プログラム実行時にこれらの引数が利用可能になります。
3. **`runProgram` メソッドの変更**:
* `args := append([]string{a.deps[0].target}, a.args...)`: 以前は `run(a.deps[0].target)` のように、コンパイルされたプログラムのパスのみを `run` 関数に渡していました。変更後、`a.deps[0].target` (実行可能ファイルのパス) に加えて、`action` 構造体に格納されている `a.args` (プログラムへの引数) を結合し、新しい引数スライスを作成しています。
* `run(args...)`: 作成された引数スライスを `run` 関数に渡すことで、Goプログラムがこれらの引数を受け取って実行されるようになります。
4. **`splitArgs` 関数の新規追加**:
この関数は、`go run` に渡された引数スライス `args` を受け取り、`--` を区切りとして2つのスライスに分割します。
* `dashes := len(args)`: まず、`--` が見つからなかった場合のデフォルトとして、すべての引数を `before` スライスに含めるように `dashes` を初期化します。
* `for i, arg := range args`: 引数をループで走査します。
* `if arg == "--"`: `--` が見つかった場合、そのインデックスを `dashes` に設定し、ループを終了します。
* `before = args[:dashes]`: `--` の前にある引数を `before` スライスに格納します。
* `if dashes < len(args)`: `--` が存在した場合、`--` の次の要素から最後までを `after` スライスに格納します。
* `return`: `before` と `after` の2つのスライスを返します。
これらの変更により、`go run` コマンドは、Goプログラムへの引数をより直感的かつ標準的な方法で受け取ることができるようになり、開発の利便性が向上しました。
## 関連リンク
* Go Gerrit Code Review: [https://golang.org/cl/5514046](https://golang.org/cl/5514046)
## 参考にした情報源リンク
* Go Command Documentation: [https://go.dev/cmd/go/](https://go.dev/cmd/go/)
* Go `os.Args` documentation: [https://pkg.go.dev/os#Args](https://pkg.go.dev/os#Args)
* Unix/Linux Command Line Arguments (General Concept): [https://en.wikipedia.org/wiki/Command-line_interface#Arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
* The `--` argument: [https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html)I have generated the detailed explanation of the commit as requested, following all the specified sections and formatting. The output is in Markdown format and printed to standard output.