Goディストリビューションは、"go"
と呼ばれるコマンドを含んでいます。
これは、Goパッケージやコマンドの自動ダウンロード、ビルド、インストール、テストを行います。
本ドキュメントでは、なぜこの新しいコマンドを作ったのか、これは何で、何ではないのか、どのように使うのかについて話します。
Goの初期の講演でRob Pikeが大規模なGoogleのサーバをコンパイルするのを待っている間に Goのアイディアが生まれたというジョークを聞いたことがあるかもしれません。 Goの本当の動機は、Googleが作り、実行する大規模なソフトウェアのビルドをより良くする言語を作ることです。 このような言語には、コードライブラリ間の依存関係をきちんと表現する方法(パッケージ(package)のグループ化と明示的なインポート(import)ブロック)を提供しなければならないことは最初から分かっていました。 インポートしたコードを表すために好きな書き方をしたいことも明らかでした。インポートパスが文字列リテラルである理由はこのためです。
最初からあるGoの目標は、MakefileやMakefileに変わる様々な現代的な構成ファイルを書く必要をなくし、 ソースコード自体にある情報のみを使ってGoのコードをビルドできるようにすることでした。 もしGoが、プログラムをビルドする方法を記述するために構成ファイルを必要としたなら、Goは失敗していたことでしょう。
最初はGoコンパイラはありませんでしたので、初期の開発ではGoを構築することと、ライブラリを構築することにフォーカスしていました。 便宜上、makeを使い、Makefileを書くことでGoのビルドの自動化を延ばしていました。 Goコンパイラを複数呼び出す単一のパッケージをコンパイルするときにも、自分たちのためにMakefileを書くためのプログラムを使っていました。 リポジトリの歴史を辿る際には、これを見つけることができるでしょう。
新しいGoコマンドの目標は、Goプログラムは必要なインポート文を書くということ以上に開発者側で構成ファイルや追加の作業をせずにコンパイルできるようにする必要がある。という、私たちの理想へ回帰しています。
構成不要なシステム(configuration-free system)のシンプルさを実現するための方法は、規則(convention)を確立することです。
システムはこれらの規則に従うだけで動作します。
Goを最初にローンチしたとき、多くの人は、利用するために、特定の場所から、特定の名前で、特定のビルドツールを使ってインストールしなければならないパッケージを公開しました。
これはよくわかります。というのも、他のほとんどの言語でおこなう方法なのです。
ここ数年、私たちは一貫して
goinstall(現在はgo getに置き換わっています)とその規則を普及させてきました:
今日では、ほとんどのパッケージはこれらの規則に従っています。 結果として、Goのエコシステム(ecosystem)は、シンプルでより強力なものになっています。
パッケージディレクトリ内のMakefileが、ソースコード内に書かれている内容以上のことをする構成を提供することを認めるよう、リクエストされました。 しかし、新しいルールを導入することにしました。 このような要求に応じないことで、goコマンドを作ることができ、makeやほかのビルドシステムの利用を排除できました。
goコマンドは、一般的なビルドツールではないことを理解することが重要です。 設定はできませんし、Goパッケージ以外のものはビルドできません。 これらは、重要な単純さへの仮説で、 それは実装だけでなく、重要なのはツール自体の利用もシンプルになるということです。
goコマンドは、いくつかの重要でよく確立された規則に従っています。
はじめに、インポートパスは、ソースコードのURLから既知の方法で取り出せます。
Bitbucket、GitHub、Google Code、Launchpadは、
リポジトリのメインURLをhttp://なしで識別できます。
サブディレクトリは、そのパスの後ろに付け加えることで名前をつけられます。
たとえば、Goのための補助的なネットワークライブラリは次を実行することで取得できます:
hg clone http://code.google.com/p/go.net
このリポジトリのルートディレクトリのインポートパスは、
"code.google.com/p/go.net"です。
websocketパッケージはサブディレクトリにあるので、インポートパスは、
"code.google.com/p/go.net/websocket"です。
これらのパスは長くなりますが、それと引き換えに、インポートパスでのネームスペースの自動的な管理と、インポートパスを見てソースコードを取得する場所を推定することができ、goコマンドのようなツールのための機能性を得ました。
2つ目に、ローカルのファイルシステム内のソースを格納する場所は、インポートパスから既知の方法で取得できます。
具体的には、最初の選択は$GOPATH/src/<import-path>です。
もし$GOPATHが設定されていなければ、goコマンドは標準のGoパッケージと一緒に$GOROOT/src/pkg/<import-path>へソースコードを保存することになります。
もし$GOPATHが設定されているなら、goコマンドは<dir>/src/<import-path>以下になります。
規則で、トップディレクトリのツリーには以下があります:
bin" : コンパイルした実行ファイルを格納するpkg" : インポート済みのコンパイルしたパッケージを格納するsrc" : パッケージのソースファイルを格納するコンパイルしたものとソースコードが常に近くにあるこの構造にすることで、これらのディレクトリツリーのそれぞれを自己完結して維持することができます。
これらの命名規則は、ディレクトリ名からインポートパスへ逆方向に操作できます。 この対応関係は、goコマンドのサブコマンドの多くで重要になります。以下を参照ください。
3つ目に、ソースツリーの各ディレクトリは、ひとつのパッケージに対応します。 ひとつのパッケージをディレクトリに制限することで、 最初のディレクトリを指定し、そのディレクトリ内にパッケージを指定するといった混合したインポートパスを作る必要はありません。 また、ほとんどのファイル管理ツールとUIは、基本単位としてディレクトリで動きます。 ファイルシステムの構造は、基本的なGoの単位「パッケージ」と結びつき、ファイルシステムツールがGoパッケージツールになることを意味します。 パッケージのコピー、移動、削除は、ディレクトリのコピー、移動、削除に対応するようになります。
4つ目に、各パッケージはソースファイル中にある情報だけでビルドされます。 ツールは、ビルドの環境や条件の変化に適応することができる可能性がより高くなります。 たとえば、コンパイルフラグやコマンドラインのレシピのようなもので追加の設定が許されている場合、 その構成は、ビルドツールが変更されるたびに更新される必要があるでしょう。 それはまた、本質的に特定のツールチェーンの利用に縛られることにもなります。
最後に、Goコードの書き方(最初に読んだほうがいいかもしれません)に書かれている情報を補足するために、goコマンドの使い方のクイックツアーをやりましょう。
書いたGoのソースコードを、Goのディストリビューションのソースツリーとは別に管理したいと仮定すると、
まずはじめに、goコマンドが必要とするグローバルな構成のひとつの$GOPATHを設定します。
$GOPATHはディレクトリのリストを指定訳注1できますが、今のところ最も一般的な利用方法に従い、単一のディレクトリを指定すべきです。
具体的に言うと、プロジェクトごとに$GOPATHを個別に指定する必要はありません。
1つの$GOPATHは、多くのプロジェクトをサポートすることができます。
次は簡単な例です。
$HOME/mygoディレクトリにGoコード保存することを決めたとしましょう。
そのディレクトリを作成し、それに応じて$GOPATHをセットします。
$ mkdir $HOME/mygo $ export GOPATH=$HOME/mygo $
このディレクトリに入り、いくつかソースコードを追加しましょう。
left-leaning red-black treeと一緒にcodesearchプロジェクトのライブラリを使いたいとします。
"go get"サブコマンドで両方インストールしましょう:
$ go get code.google.com/p/codesearch/index $ go get github.com/petar/GoLLRB/llrb $
これらのプロジェクトは、セットした$GOPATHへダウンロードされインストールされます。
ディレクトリツリーには2つのディレクトリ
src/code.google.com/p/codesearch/index/と
src/github.com/petar/GoLLRB/llrb/、
ライブラリをコンパイルしたパッケージ(pkg/に入ります)とそれらへの依存関係が一緒に含まれます。
チェックアウトにはバージョン管理システム(MercurialやGit)を使っています。
ソースツリーには、関連のパッケージとして対応するリポジトリ内の他のファイルが含まれていることがあります。
"go list"サブコマンドは、引数に対応してインポートパスをリストします。
"./..."は現在のディレクトリ("./")以下すべて("...")のパッケージを見つけることができます:
$ go list ./... code.google.com/p/codesearch/cmd/cgrep code.google.com/p/codesearch/cmd/cindex code.google.com/p/codesearch/cmd/csearch code.google.com/p/codesearch/index code.google.com/p/codesearch/regexp code.google.com/p/codesearch/sparse github.com/petar/GoLLRB/example github.com/petar/GoLLRB/llrb $
また、これらのパッケージをテストすることもできます:
$ go test ./... ? code.google.com/p/codesearch/cmd/cgrep [no test files] ? code.google.com/p/codesearch/cmd/cindex [no test files] ? code.google.com/p/codesearch/cmd/csearch [no test files] ok code.google.com/p/codesearch/index 0.239s ok code.google.com/p/codesearch/regexp 0.021s ? code.google.com/p/codesearch/sparse [no test files] ? github.com/petar/GoLLRB/example [no test files] ok github.com/petar/GoLLRB/llrb 0.231s $
goサブコマンドにパスを渡さずに起動した場合、現在のディレクトリに対して操作します:
$ cd $GOPATH/src/code.google.com/p/codesearch/regexp $ go list code.google.com/p/codesearch/regexp $ go test -v === RUN TestNstateEnc --- PASS: TestNstateEnc (0.00 seconds) === RUN TestMatch --- PASS: TestMatch (0.01 seconds) === RUN TestGrep --- PASS: TestGrep (0.00 seconds) PASS ok code.google.com/p/codesearch/regexp 0.021s $ go install $
"go install"サブコマンドは、pkgディレクトリへパッケージの最新版をインストールます。
goコマンドが依存関係を解析することができますので、"go install"も、パッケージのインストールでパッケージインポートが古くなっているパッケージを再帰的にインストールします。
"go install"は、ディレクトリの命名の規則で、現在のディレクトリでパッケージのインポートパスの名前を知ることができたことに注目してください。
普通、このような長い名前にはせず、ソースコードを格納するディレクトリの名前を選べれば便利でしょう。
しかし、そうすればツールに追加の構成と複雑さを必要とします。
多少、追加でディレクトリ名を入れることは、シンプルさと能力を増やすことを思えば大したことはありません。
例で見せたように、ひとつの$GOPATHのディレクトリで、一度に多くの異なるプロジェクトがあってもうまく動作します。
訳注1:ディレクトリのリストを指定とは、GOPATH=/home/gopher/myworkspace,/home/gopher/mygopackageといった形で複数のパスを設定できるということ
これまでに述べたように、goコマンドは汎用的ななビルドツールではありません。
特に、ビルドでGoのソースコードを生成するための機能はありません。
yaccやプロトコルバッファのようなツールを使いたいときは、代わりにMakefile(または、任意のビルドツールの構成ファイル)でGoのファイルを生成し、それらの生成されたソースファイルをコミットするためのリポジトリが必要です。
これは、パッケージの作者にとっては手間がかかりますが、ユーザが追加のツールを利用してビルドすることなく、"go get"だけを利用することができ、ユーザへのメリットになります。
より詳しく見るには、Goコードの書き方と go command documentationを見てみてください。