goコマンドについて

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

動機

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

Goの最初からの明確な目標は、ソース自体にある情報のみを使用してGoコードをビルドできることであり、makefileや多くの最新のmakefileの代替品を作成する必要がないことでした。プログラムをビルドする方法を説明するためにGoに構成ファイルが必要だった場合、Goは失敗したでしょう。

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

新しいgoコマンドの目的は、Goプログラムが必要なインポートステートメントを作成する以外の開発者側の設定や追加の労力なしにコンパイルされるべきであるというこの理想に戻ることです。

設定と規約

設定不要なシステムのシンプルさを実現する方法は、規約を確立することです。システムは、これらの規約が守られている限り機能します。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にダウンロードされ、インストールされます。ここには、src/github.com/google/codesearch/index/src/github.com/petar/GoLLRB/llrb/という2つのディレクトリと、これらのライブラリとその依存関係のコンパイル済みパッケージ(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コマンドのドキュメントを参照してください。