動機

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

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

最初はGoコンパイラは存在せず、初期の開発はそれを構築し、そのためのライブラリを構築することに焦点を当てていました。便宜上、私たちはMakeを使用してGoコードを構築する自動化を延期しました。単一のパッケージをコンパイルするのにGoコンパイラを複数回呼び出す必要があったため、私たちはMakefileを自動生成するプログラムを使用しました。それはリポジトリの履歴を掘り下げると見つけることができます。

新しいgoコマンドの目的は、Goプログラムが開発者が必要なインポート文を書く以外の設定や追加の努力なしにコンパイルできるという理想に戻ることです。

設定対慣習

設定のないシステムのシンプルさを達成する方法は、慣習を確立することです。そのシステムは、これらの慣習が守られる限り機能します。Goを最初に立ち上げたとき、多くの人々が特定の場所に、特定の名前の下で、特定のビルドツールを使用してインストールしなければならないパッケージを公開しました。それは理解できます:それが他のほとんどの言語での動作方法です。過去数年にわたり、私たちは一貫して人々にgoinstallコマンド(現在はgo getに置き換えられました)とその慣習について思い出させました。まず、インポートパスはソースコードのURLから既知の方法で導出されます。次に、ローカルファイルシステムにソースを保存する場所はインポートパスから既知の方法で導出されます。第三に、ソースツリー内の各ディレクトリは単一のパッケージに対応します。第四に、パッケージはソースコード内の情報のみを使用して構築されます。今日、圧倒的多数のパッケージがこれらの慣習に従っています。その結果、Goエコシステムはよりシンプルで強力になりました。

私たちは、パッケージディレクトリにMakefileを許可して、ソースコードにあるものを超えた少しの追加設定を提供するように多くのリクエストを受けました。しかし、それは新しいルールを導入することになります。そのようなリクエストに応じなかったため、私たちはgoコマンドを書くことができ、Makeや他のビルドシステムの使用を排除することができました。

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

Goの慣習

  1. まず、インポートパスはソースコードのURLから既知の方法で導出されます。BitbucketGitHubGoogle CodeLaunchpadの場合、リポジトリのルートディレクトリはリポジトリのメインURLによって識別され、`````https://`````プレフィックスはありません。サブディレクトリはそのパスに追加することで命名されます。たとえば、Googleのロギングパッケージ`````glog`````のソースコードは、次のコマンドを実行することで取得されます。
  2. ``````bash
  3. git clone https://github.com/golang/glog
  4. `

したがって、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」サブコマンドを使用して両方をインストールできます:

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

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

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

  1. $ cd $HOME/go/src
  2. $ go list ./...
  3. github.com/google/codesearch/cmd/cgrep
  4. github.com/google/codesearch/cmd/cindex
  5. github.com/google/codesearch/cmd/csearch
  6. github.com/google/codesearch/index
  7. github.com/google/codesearch/regexp
  8. github.com/google/codesearch/sparse
  9. github.com/petar/GoLLRB/example
  10. github.com/petar/GoLLRB/llrb
  11. $

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

  1. $ go test ./...
  2. ? github.com/google/codesearch/cmd/cgrep [no test files]
  3. ? github.com/google/codesearch/cmd/cindex [no test files]
  4. ? github.com/google/codesearch/cmd/csearch [no test files]
  5. ok github.com/google/codesearch/index 0.203s
  6. ok github.com/google/codesearch/regexp 0.017s
  7. ? github.com/google/codesearch/sparse [no test files]
  8. ? github.com/petar/GoLLRB/example [no test files]
  9. ok github.com/petar/GoLLRB/llrb 0.231s
  10. $

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

  1. $ cd github.com/google/codesearch/regexp
  2. $ go list
  3. github.com/google/codesearch/regexp
  4. $ go test -v
  5. === RUN TestNstateEnc
  6. --- PASS: TestNstateEnc (0.00s)
  7. === RUN TestMatch
  8. --- PASS: TestMatch (0.00s)
  9. === RUN TestGrep
  10. --- PASS: TestGrep (0.00s)
  11. PASS
  12. ok github.com/google/codesearch/regexp 0.018s
  13. $ go install
  14. $

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

go install」は、ディレクトリ命名の慣習のおかげで、現在のディレクトリのパッケージのインポートパスの名前を特定できました。ソースコードを保持するディレクトリの名前を選択できれば少し便利ですが、おそらくそんなに長い名前は選ばないでしょうが、その能力はツールに追加の設定と複雑さを要求します。余分なディレクトリ名を1つか2つ入力することは、シンプルさと力を高めるための小さな代償です。

制限

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

詳細情報

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