go コマンドについて

Go ディストリビューションには、"go" というコマンドが含まれており、Go パッケージとコマンドのダウンロード、ビルド、インストール、テストを自動化します。このドキュメントでは、なぜ新しいコマンドを作成したのか、それが何なのか、何ではないのか、そしてどのように使用するかについて説明します。

動機

Rob Pike が、Google の巨大なサーバーのコンパイルを待っている間に Go のアイデアが生まれたと冗談を言っている初期の Go の講演を見たことがあるかもしれません。それはまさに Go の動機でした。Google が記述し、実行する巨大なソフトウェアの構築に適した言語を構築することです。最初から、そのような言語はコードライブラリ間の依存関係を明確に表現する方法を提供しなければならないことは明らかでした。そのため、パッケージのグループ化と明示的なインポートブロックがあります。また、インポートされるコードを記述するための任意の構文が必要になる場合があることも最初から明らかでした。そのため、インポートパスは文字列リテラルです。

最初から Go の明示的な目標は、makefile や makefile の多くの現代的な代替品を書く必要がなく、ソース自体にある情報だけを使用して Go コードをビルドできるようにすることでした。Go がプログラムのビルド方法を説明するための設定ファイルを必要とする場合、Go は失敗したことになります。

最初は Go コンパイラはなく、初期の開発はコンパイラの構築と、それに対応するライブラリの構築に焦点を当てていました。便宜上、make を使用して makefile を記述することで、Go コードのビルドの自動化を延期しました。単一のパッケージのコンパイルに Go コンパイラの複数回の呼び出しが含まれる場合、makefile を記述するためのプログラムを使用することさえありました。リポジトリの履歴を掘り下げれば、それを見つけることができます。

新しい go コマンドの目的は、この理想、つまり Go プログラムは、開発者が必要な import 文を書く以上の設定や追加の労力なしにコンパイルできるべきであるという理想への回帰です。

設定と規約

設定不要のシステムのシンプルさを実現する方法は、規約を確立することです。システムは、それらの規約が守られている範囲でのみ機能します。Go を最初に立ち上げたとき、多くの人が、使用するためには特定の場所に、特定の名前で、特定のビルドツールを使用してインストールする必要があるパッケージを公開しました。それは理解できます。それはほとんどの他の言語でそれが機能する方法です。過去数年間、私たちは一貫して人々に goinstall コマンド(現在は go get に置き換えられています)とその規約を思い出させました。第一に、インポートパスはソースコードの URL から既知の方法で派生されること。第二に、ローカルファイルシステムにソースを格納する場所はインポートパスから既知の方法で派生されること。第三に、ソースツリー内の各ディレクトリは単一のパッケージに対応すること。第四に、パッケージはソースコード内の情報のみを使用してビルドされること。今日、大多数のパッケージはこれらの規約に従っています。その結果、Go エコシステムはよりシンプルで強力になりました。

パッケージディレクトリに makefile を許可して、ソースコードに含まれる以上の設定を少しだけ追加できるようにしてほしいという多くのリクエストを受け取りました。しかし、それは新しいルールを導入していただろうからです。そのようなリクエストに応じなかったため、go コマンドを作成し、make や他のビルドシステムの使用を排除することができました。

go コマンドは汎用ビルドツールではないことを理解することが重要です。設定することはできず、Go パッケージ以外のものをビルドしようとはしません。これらは重要な単純化の仮定です。実装だけでなく、さらに重要なことに、ツール自体の使用を簡素化します。

Go の規約

go コマンドでは、コードがいくつかの重要な、確立された規約に準拠している必要があります。

第一に、インポートパスはソースコードの URL から既知の方法で派生されます。Bitbucket、GitHub、Google Code、Launchpad の場合、リポジトリのルートディレクトリは、https:// プレフィックスのないリポジトリのメイン URL によって識別されます。サブディレクトリは、そのパスに追加することによって名前が付けられます。たとえば、Google ロギングパッケージ glog のソースコードは、次を実行することで取得されます。

git clone https://github.com/golang/glog
そのため、glog パッケージのインポートパスは "github.com/golang/glog" です。

これらのパスは長いですが、その代わりに、インポートパスのための自動的に管理される名前空間と、go コマンドのようなツールが不慣れなインポートパスを見てソースコードを取得する場所を推測する機能が得られます。

第二に、ローカルファイルシステムにソースを格納する場所は、インポートパス、具体的には $GOPATH/src/<import-path> から既知の方法で派生されます。設定されていない場合、$GOPATH はデフォルトでユーザーのホームディレクトリにある go という名前のサブディレクトリになります。$GOPATH がパスのリストに設定されている場合、go コマンドはそのリストの各ディレクトリに対して <dir>/src/<import-path> を試します。

これらのツリーのそれぞれには、慣例により、コンパイルされた実行可能ファイルを保持するための "bin" という名前のトップレベルディレクトリと、インポート可能なコンパイルされたパッケージを保持するための "pkg" という名前のトップレベルディレクトリ、およびパッケージソースファイルを保持するための "src" ディレクトリが含まれています。この構造を課すことで、これらのディレクトリツリーのそれぞれを自己完結させることができます。コンパイルされた形式とソースは常に互いに近くにあります。

これらの命名規則により、ディレクトリ名からインポートパスへと逆方向に作業することもできます。このマッピングは、以下で説明するように、go コマンドの多くのサブコマンドにとって重要です。

第三に、ソースツリー内の各ディレクトリは単一のパッケージに対応します。ディレクトリを単一のパッケージに制限することで、最初にディレクトリを指定し、次にそのディレクトリ内のパッケージを指定するハイブリッドインポートパスを作成する必要はありません。また、ほとんどのファイル管理ツールと UI は、ディレクトリを基本単位として扱います。基本的な Go 単位(パッケージ)をファイルシステム構造に結び付けるということは、ファイルシステムツールが Go パッケージツールになることを意味します。パッケージのコピー、移動、または削除は、ディレクトリのコピー、移動、または削除に対応します。

第四に、各パッケージはソースファイルに存在する情報のみを使用してビルドされます。これにより、ツールが変化するビルド環境と条件に適応できる可能性が高くなります。たとえば、コンパイラフラグやコマンドラインレシピなどの追加の設定を許可した場合、その設定はビルドツールが変更されるたびに更新する必要があります。また、特定のツールチェーンの使用に本質的に結び付けられます。

go コマンドを使い始める

最後に、go コマンドの使用方法の簡単なツアーです。前述のように、Unix のデフォルトの $GOPATH$HOME/go です。プログラムはそこに保存します。別の場所を使用するには、$GOPATH を設定します。詳細は Go コードの書き方 を参照してください。

最初にソースコードを追加します。codesearch プロジェクトのインデックスライブラリと左傾レッドブラックツリーを使用するとします。どちらも "go get" サブコマンドでインストールできます

$ go get github.com/google/codesearch/index
$ go get github.com/petar/GoLLRB/llrb
$

これらのプロジェクトは両方ともダウンロードされ、$HOME/go にインストールされます。これには、2 つのディレクトリ src/github.com/google/codesearch/index/src/github.com/petar/GoLLRB/llrb/、およびこれらのライブラリとその依存関係のコンパイル済みパッケージ(pkg/ 内)が含まれています。

バージョン管理システム(Mercurial と Git)を使用してソースをチェックアウトしたため、ソースツリーには、関連パッケージなど、対応するリポジトリ内の他のファイルも含まれています。"go list" サブコマンドは、引数に対応するインポートパスをリストし、パターン "./..." は現在のディレクトリ("./")から開始し、そのディレクトリ以下のすべてのパッケージ("...")を見つけることを意味します

$ cd $HOME/go/src
$ go list ./...
github.com/google/codesearch/cmd/cgrep
github.com/google/codesearch/cmd/cindex
github.com/google/codesearch/cmd/csearch
github.com/google/codesearch/index
github.com/google/codesearch/regexp
github.com/google/codesearch/sparse
github.com/petar/GoLLRB/example
github.com/petar/GoLLRB/llrb
$

これらのパッケージをテストすることもできます

$ go test ./...
?   	github.com/google/codesearch/cmd/cgrep	[no test files]
?   	github.com/google/codesearch/cmd/cindex	[no test files]
?   	github.com/google/codesearch/cmd/csearch	[no test files]
ok  	github.com/google/codesearch/index	0.203s
ok  	github.com/google/codesearch/regexp	0.017s
?   	github.com/google/codesearch/sparse	[no test files]
?       github.com/petar/GoLLRB/example          [no test files]
ok      github.com/petar/GoLLRB/llrb             0.231s
$

go サブコマンドがパスを指定せずに呼び出された場合、現在のディレクトリで動作します

$ cd github.com/google/codesearch/regexp
$ go list
github.com/google/codesearch/regexp
$ go test -v
=== RUN   TestNstateEnc
--- PASS: TestNstateEnc (0.00s)
=== RUN   TestMatch
--- PASS: TestMatch (0.00s)
=== RUN   TestGrep
--- PASS: TestGrep (0.00s)
PASS
ok  	github.com/google/codesearch/regexp	0.018s
$ go install
$

その "go install" サブコマンドは、パッケージの最新のコピーを pkg ディレクトリにインストールします。go コマンドは依存関係グラフを分析できるため、"go install" はこのパッケージがインポートするが古くなっているパッケージも再帰的にインストールします。

ディレクトリ命名の規則により、"go install" は現在のディレクトリのパッケージのインポートパスの名前を決定できたことに注意してください。ソースコードを保持するディレクトリの名前を選択できればもう少し便利になり、おそらくそれほど長い名前は選択しないでしょうが、その機能を実現するには、ツールに追加の設定と複雑さが必要になります。1つか2つの追加のディレクトリ名を入力することは、シンプルさとパワーの向上に対する小さな代償です。

制限事項

上記で述べたように、goコマンドは汎用のビルドツールではありません。特に、ビルド中にGoのソースファイルを生成する機能は備えていません。ただし、ビルド前にGoファイルの作成を自動化できるgo generateは提供されています。より高度なビルド設定を行う場合は、Goファイルを生成するツールを実行し、生成されたソースファイルをリポジトリにチェックインするために、makefile(または任意のビルドツールの設定ファイル)を作成する必要がある場合があります。これはパッケージの作成者にとってはより多くの作業になりますが、ユーザーにとっては、追加のツールを取得してビルドする必要なく "go get" を使用できるため、作業が大幅に軽減されます。

詳細情報

詳細については、Goコードの書き方を読み、goコマンドのドキュメントを参照してください。