著者は、 Diversity in Tech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

バージョン1.13で、Goの作成者は、Goモジュールと呼ばれるGoプロジェクトが依存するライブラリを管理する新しい方法を追加しました。 Goモジュールは、開発者がさまざまなバージョンの依存関係を維持しやすくするとともに、開発者がコンピューター上でプロジェクトを整理する方法に柔軟性を追加する必要性の高まりに応えて追加されました。 Goモジュールは通常、1つのプロジェクトまたはライブラリで構成され、Goパッケージのコレクションが含まれています。これらのパッケージは一緒にリリースされます。 Goモジュールは、ユーザーが選択したディレクトリにプロジェクトコードを配置し、各モジュールの依存関係のバージョンを指定できるようにすることで、元のシステムであるGOPATHの多くの問題を解決します。

このチュートリアルでは、独自のパブリックGoモジュールを作成し、新しいモジュールにパッケージを追加します。 さらに、他の誰かのパブリックモジュールを自分のプロジェクトに追加したり、そのモジュールの特定のバージョンをプロジェクトに追加したりします。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Goバージョン1.16以降がインストールされています。これは、シリーズGoのローカルプログラミング環境をインストールおよびセットアップする方法に従って実行できます。
  • Goでパッケージを作成する知識。 詳細については、Goチュートリアルでパッケージを作成する方法に従ってください。

新しいモジュールの作成

一見すると、GoモジュールはGoパッケージに似ています。 モジュールには、パッケージの機能を実装する多数のGoコードファイルがありますが、ルートにはgo.modファイルとgo.sumファイルの2つの追加の重要なファイルもあります。 これらのファイルには、goツールがモジュールの構成を追跡するために使用する情報が含まれており、通常はツールによって維持されるため、その必要はありません。

最初に行うことは、モジュールが存在するディレクトリを決定することです。 Goモジュールの導入により、Goプロジェクトは、Goによって定義された特定のディレクトリだけでなく、ファイルシステム上の任意の場所に配置できるようになりました。 プロジェクト用のディレクトリがすでにある場合がありますが、このチュートリアルでは、projectsというディレクトリを作成し、新しいモジュールはmymoduleという名前になります。 projectsディレクトリは、IDEまたはコマンドラインから作成できます。

コマンドラインを使用している場合は、projectsディレクトリを作成し、そこに移動することから始めます。

  1. mkdir projects
  2. cd projects

次に、モジュールディレクトリ自体を作成します。 通常、モジュールの最上位ディレクトリ名はモジュール名と同じであるため、追跡が容易になります。 projectsディレクトリで、次のコマンドを実行してmymoduleディレクトリを作成します。

  1. mkdir mymodule

モジュールディレクトリを作成すると、ディレクトリ構造は次のようになります。

└── projects
    └── mymodule

次のステップは、mymoduleディレクトリ内にgo.modファイルを作成して、Goモジュール自体を定義することです。 これを行うには、goツールのmod initコマンドを使用して、モジュールの名前(この場合はmymodule)を指定します。 次に、mymoduleディレクトリからgo mod initを実行してモジュールを作成し、モジュール名mymoduleを指定します。

  1. go mod init mymodule

このコマンドは、モジュールの作成時に次の出力を返します。

Output
go: creating new go.mod: module mymodule

モジュールが作成されると、ディレクトリ構造は次のようになります。

└── projects
    └── mymodule
        └── go.mod

モジュールを作成したので、go.modファイルの内部を見て、go mod initコマンドが何をしたかを見てみましょう。

go.modファイルを理解する

goツールを使用してコマンドを実行する場合、go.modファイルはプロセスの非常に重要な部分です。 これは、モジュールの名前と、独自のモジュールが依存する他のモジュールのバージョンを含むファイルです。 また、 replace などの他のディレクティブを含めることもできます。これは、一度に複数のモジュールで開発を行うのに役立ちます。

mymoduleディレクトリで、nanoまたはお気に入りのテキストエディタを使用してgo.modファイルを開きます。

  1. nano go.mod

内容は次のようになりますが、それほど多くはありません。

projects / mymodule / go.mod
module mymodule

go 1.16

最初の行であるmoduleディレクティブは、パッケージ内のimportパスを検索するときに、他の場所でmymoduleを検索しないように、モジュールの名前をGoに通知します。 。 mymoduleの値は、go mod initに渡したパラメーターから取得されます。

module mymodule

この時点でファイル内の他の唯一の行であるgoディレクティブは、モジュールが対象としている言語のバージョンをGoに通知します。 この場合、モジュールはGo 1.16を使用して作成されているため、goディレクティブは1.16と言います。

go 1.16

モジュールにさらに情報が追加されると、このファイルは拡張されますが、依存関係がさらに追加されるにつれてどのように変化するかを今すぐ確認することをお勧めします。

これで、go mod initを使用してGoモジュールを作成し、最初のgo.modファイルに何が含まれているかを確認しましたが、モジュールはまだ何もしていません。 モジュールをさらに進めて、コードを追加します。

モジュールへのGoコードの追加

モジュールが正しく作成されていることを確認し、最初のGoモジュールを実行できるようにコードを追加するには、mymoduleディレクトリ内にmain.goファイルを作成します。 main.goファイルは、プログラムの開始点を通知するためにGoプログラムで一般的に使用されます。 ファイルの名前は、内部のmain関数ほど重要ではありませんが、2つを一致させると、見つけやすくなります。 このチュートリアルでは、main関数を実行すると、Hello, Modules!が出力されます。

ファイルを作成するには、nanoまたはお気に入りのテキストエディタを使用してmain.goファイルを開きます。

  1. nano main.go

main.goファイルに次のコードを追加してmainパッケージを定義し、fmtパッケージをインポートして、Hello, Modules!メッセージをmain関数:

projects / mymodule / main.go
package main

import "fmt"

func main() {
	fmt.Println("Hello, Modules!")
}

Goでは、各ディレクトリは独自のパッケージと見なされ、各ファイルには独自のpackage宣言行があります。 作成したmain.goファイルでは、packageの名前はmainです。 通常、パッケージには任意の名前を付けることができますが、mainパッケージはGoでは特別です。 Goは、パッケージの名前がmainであることを確認すると、そのパッケージはバイナリと見なされ、別のプログラムで使用するように設計されたライブラリではなく、実行可能ファイルにコンパイルされる必要があることを認識します。

packageが定義された後、import宣言は、 fmt パッケージをインポートするように指示し、Println関数を使用して[を印刷できるようにします。 X153X]画面へのメッセージ。

最後に、main関数が定義されます。 main関数は、mainパッケージに関連する、Goのもう1つの特殊なケースです。 Goは、mainという名前のパッケージ内のmainという名前の関数を見ると、main関数が最初に実行する必要がある関数であることがわかります。 これは、プログラムのエントリポイントとして知られています。

main.goファイルを作成すると、モジュールのディレクトリ構造は次のようになります。

└── projects
    └── mymodule
        └── go.mod
        └── main.go

GoとGOPATHの使用に精通している場合、モジュールでコードを実行するのは、GOPATHのディレクトリから実行するのと同じです。 (GOPATHに慣れていなくても心配しないでください。モジュールを使用すると、その使用法が置き換えられます。)

Goで実行可能プログラムを実行する一般的な方法は2つあります。go buildを使用してバイナリをビルドする方法と、go runを使用してファイルを実行する方法です。 このチュートリアルでは、go runを使用して、個別に実行する必要があるバイナリを作成する代わりに、モジュールを直接実行します。

go runで作成したmain.goファイルを実行します。

  1. go run main.go

コマンドを実行すると、コードで定義されているようにHello, Modules!テキストが出力されます。

Output
Hello, Modules!

このセクションでは、Hello, Modules!を出力する最初のmain関数を使用して、main.goファイルをモジュールに追加しました。 この時点では、プログラムはGoモジュールであるというメリットはまだありません。go runで実行されているコンピューター上の任意の場所にあるファイルである可能性があります。 Goモジュールの最初の本当の利点は、GOPATHディレクトリ構造だけでなく、任意のディレクトリのプロジェクトに依存関係を追加できることです。 モジュールにパッケージを追加することもできます。 次のセクションでは、モジュール内に追加のパッケージを作成して、モジュールを拡張します。

モジュールへのパッケージの追加

標準のGoパッケージと同様に、モジュールには任意の数のパッケージとサブパッケージが含まれる場合と、まったく含まれない場合があります。 この例では、mymoduleディレクトリ内にmypackageという名前のパッケージを作成します。

mypackage引数を指定してmymoduleディレクトリ内でmkdirコマンドを実行して、この新しいパッケージを作成します。

  1. mkdir mypackage

これにより、新しいディレクトリmypackagemymoduleディレクトリのサブパッケージとして作成されます。

└── projects
    └── mymodule
        └── mypackage
        └── main.go
        └── go.mod

cdコマンドを使用してディレクトリを新しいmypackageディレクトリに変更し、nanoまたはお気に入りのテキストエディタを使用してmypackage.goファイルを作成します。 このファイルには任意の名前を付けることができますが、パッケージと同じ名前を使用すると、パッケージのプライマリファイルを簡単に見つけることができます。

  1. cd mypackage
  2. nano mypackage.go

mypackage.goファイルに、PrintHelloという関数を追加します。この関数は、呼び出されたときにメッセージHello, Modules! This is mypackage speaking!を出力します。

projects / mymodule / mypackage / mypackage.go
package mypackage

import "fmt"

func PrintHello() {
	fmt.Println("Hello, Modules! This is mypackage speaking!")
}

PrintHello関数を別のパッケージから使用できるようにするため、関数名の大文字のPが重要です。 大文字は、関数がエクスポートされ、外部のプログラムで使用できることを意味します。 Goでのパッケージの可視性の仕組みの詳細については、Goでのパッケージの可視性についての詳細が含まれています。

エクスポートされた関数を使用してmypackageパッケージを作成したので、それを使用するには、mymoduleパッケージからimportする必要があります。 これは、以前のfmtパッケージなどの他のパッケージをインポートする方法と似ていますが、今回はインポートパスの先頭にモジュールの名前を含める点が異なります。 mymoduleディレクトリからmain.goファイルを開き、以下の強調表示された行を追加して、PrintHelloへの呼び出しを追加します。

projects / mymodule / main.go

package main

import (
	"fmt"

	"mymodule/mypackage"
)

func main() {
	fmt.Println("Hello, Modules!")

	mypackage.PrintHello()
}

importステートメントを詳しく見ると、新しいインポートがmymoduleで始まることがわかります。これは、go.modファイルで設定したのと同じモジュール名です。 この後に、パス区切り文字とインポートするパッケージ(この場合はmypackage)が続きます。

"mymodule/mypackage"

将来、mypackage内にパッケージを追加する場合は、同様の方法でインポートパスの最後にパッケージを追加します。 たとえば、mypackage内にextrapackageという別のパッケージがある場合、そのパッケージのインポートパスはmymodule/mypackage/extrapackageになります。

以前のように、mymoduleディレクトリからgo runおよびmain.goを使用して更新されたモジュールを実行します。

  1. go run main.go

モジュールを再度実行すると、以前のHello, Modules!メッセージと、新しいmypackagePrintHello関数から出力された新しいメッセージの両方が表示されます。

Output
Hello, Modules! Hello, Modules! This is mypackage speaking!

これで、PrintHello関数を使用してmypackageというディレクトリを作成し、最初のモジュールに新しいパッケージを追加しました。 ただし、モジュールの機能が拡張されると、他の人のモジュールを自分で使い始めると便利な場合があります。 次のセクションでは、リモートモジュールを依存関係として追加します。

依存関係としてのリモートモジュールの追加

Goモジュールは、バージョン管理リポジトリ、通常はGitリポジトリから配布されます。 独自の依存関係として新しいモジュールを追加する場合は、使用するモジュールを参照する方法としてリポジトリのパスを使用します。 Goは、これらのモジュールのインポートパスを確認すると、このリポジトリパスに基づいてリモートでどこにあるかを推測できます。

この例では、github.com/spf13/cobraライブラリへの依存関係をモジュールに追加します。 Cobraは、コンソールアプリケーションを作成するための一般的なライブラリですが、このチュートリアルでは取り上げません。

mymoduleモジュールを作成したときと同様に、goツールを再度使用します。 ただし、今回はmymoduleディレクトリからgo getコマンドを実行します。 go getを実行し、追加するモジュールを指定します。 この場合、github.com/spf13/cobraを取得します。

  1. go get github.com/spf13/cobra

このコマンドを実行すると、goツールは、指定したパスからCobraリポジトリを検索し、リポジトリのブランチとタグを調べて、最新のCobraのバージョンを判別します。 次に、そのバージョンをダウンロードし、モジュール名とバージョンをgo.modファイルに追加して、後で参照できるようにすることで、選択したバージョンを追跡します。

次に、mymoduleディレクトリのgo.modファイルを開いて、新しい依存関係を追加したときにgoツールがgo.modファイルをどのように更新したかを確認します。 以下の例は、リリースされているCobraの現在のバージョン、または使用しているGoツールのバージョンに応じて変更される可能性がありますが、変更の全体的な構造は類似している必要があります。

projects / mymodule / go.mod
module mymodule

go 1.16

require (
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/spf13/cobra v1.2.1 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
)

requireディレクティブを使用する新しいセクションが追加されました。 このディレクティブは、github.com/spf13/cobraなどの必要なモジュールと、追加したモジュールのバージョンをGoに通知します。 requireディレクティブには、// indirectコメントも含まれる場合があります。 このコメントは、requireディレクティブが追加された時点で、モジュールがモジュールのソースファイルで直接参照されていないことを示しています。 いくつかのrequire行もファイルに追加されました。 これらの行は、Cobraが依存する他のモジュールであり、決定されたGoツールも参照する必要があります。

go runコマンドを実行した後、mymoduleディレクトリに新しいファイルgo.sumが作成されたことにお気づきかもしれません。 これはGoモジュールのもう1つの重要なファイルであり、Goが特定のハッシュと依存関係のバージョンを記録するために使用する情報が含まれています。 これにより、依存関係が別のマシンにインストールされている場合でも、依存関係の一貫性が保証されます。

依存関係をダウンロードしたら、main.goファイルを最小限のCobraコードで更新して、新しい依存関係を使用する必要があります。 新しい依存関係を使用するには、mymoduleディレクトリのmain.goファイルを以下のCobraコードで更新します。

projects / mymodule / main.go
package main

import (
	"fmt"
    
	"github.com/spf13/cobra"

	"mymodule/mypackage"
)

func main() {
	cmd := &cobra.Command{
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("Hello, Modules!")

			mypackage.PrintHello()
		},
	}

	fmt.Println("Calling cmd.Execute()!")
	cmd.Execute()
}

このコードは、既存の「Hello」ステートメントを含むRun関数を使用してcobra.Command構造を作成します。この関数は、cmd.Execute()の呼び出しで実行されます。 次に、更新されたコードを実行します。

  1. go run main.go

次の出力が表示されます。これは、前に表示したものと似ています。 ただし、今回は、Calling cmd.Execute()!行に示されているように、新しい依存関係を使用しています。

Output
Calling cmd.Execute()! Hello, Modules! Hello, Modules! This is mypackage speaking!

go getを使用して、ここにgithub.com/sp13/cobraなどの最新バージョンのリモート依存関係を追加すると、依存関係を最新のバグ修正で更新し続けることが容易になります。 ただし、特定のバージョンのモジュール、リポジトリタグ、またはリポジトリブランチを使用したい場合があります。 次のセクションでは、go getを使用して、そのオプションが必要なときにこれらのバージョンを参照します。

モジュールの特定のバージョンを使用する

Goモジュールはバージョン管理リポジトリから配布されるため、タグ、ブランチ、さらにはコミットなどのバージョン管理機能を使用できます。 モジュールパスの最後にある@記号と、使用するバージョンを使用して、依存関係でこれらを参照できます。 以前は、最新バージョンのCobraをインストールしたときにこの機能を利用していましたが、コマンドに明示的に追加する必要はありませんでした。 goツールは、@を使用して特定のバージョンが提供されていない場合、特別なバージョンlatestを使用する必要があることを認識しています。 latestバージョンは、my-tagmy-branchのように、実際にはリポジトリにありません。 goツールにヘルパーとして組み込まれているため、最新バージョンを自分で検索する必要はありません。

たとえば、最初に依存関係を追加したときに、同じ結果に対して次のコマンドを使用することもできます。

  1. go get github.com/spf13/cobra@latest

ここで、現在開発中のモジュールを使用していると想像してください。 この例では、your_domain/sammy/awesomeと呼びます。 このawesomeモジュールに新しい機能が追加され、new-featureというブランチで作業が行われています。 このブランチを独自のモジュールの依存関係として追加するには、go getにモジュールパス、@シンボル、ブランチ名を指定します。

  1. go get your_domain/sammy/awesome@new-feature

このコマンドを実行すると、goyour_domain/sammy/awesomeリポジトリに接続し、ブランチの現在の最新のコミットでnew-featureブランチをダウンロードし、その情報をgo.modファイル。

ただし、@オプションを使用できる方法はブランチだけではありません。 この構文は、タグやリポジトリへの特定のコミットにも使用できます。 たとえば、使用しているライブラリの最新バージョンでコミットが壊れている場合があります。 このような場合、壊れたコミットの直前にコミットを参照すると便利です。

モジュールのCobra依存関係を例として使用して、github.com/spf13/cobraのcommit07445eaを参照する必要があるとします。これは、必要な変更があり、何らかの理由で別のバージョンを使用できないためです。 この場合、ブランチやタグの場合と同じように、@シンボルの後にコミットハッシュを指定できます。 モジュールとバージョンを指定してmymoduleディレクトリでgo getコマンドを実行し、新しいバージョンをダウンロードします。

  1. go get github.com/spf13/cobra@07445ea

モジュールのgo.modファイルを再度開くと、go getgithub.com/spf13/cobrarequire行を更新して、指定したコミットを参照していることがわかります。

projects / mymodule / go.mod
module mymodule

go 1.16

require (
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/spf13/cobra v1.1.2-0.20210209210842-07445ea179fc // indirect
	github.com/spf13/pflag v1.0.5 // indirect
)

タグやブランチとは異なり、コミットは特定の時点であるため、Goはrequireディレクティブに追加情報を含めて、将来的に正しいバージョンを使用するようにします。 バージョンをよく見ると、提供したコミットハッシュv1.1.2-0.20210209210842-07445ea179fcが含まれていることがわかります。

Goモジュールもこの機能を使用して、モジュールのさまざまなバージョンのリリースをサポートします。 Goモジュールが新しいバージョンをリリースすると、バージョン番号をタグとして新しいタグがリポジトリに追加されます。 特定のバージョンを使用する場合は、リポジトリ内のタグのリストを調べて、探しているバージョンを見つけることができます。 バージョンがすでにわかっている場合は、バージョンタグに一貫した名前が付けられているため、タグを検索する必要がない場合があります。

例としてCobraに戻り、Cobraバージョン1.1.1を使用するとします。 Cobraリポジトリを見ると、v1.1.1などのタグが付いていることがわかります。 このタグ付きバージョンを使用するには、非バージョンのタグまたはブランチを使用するのと同じように、go getコマンドで@シンボルを使用します。 次に、バージョンとしてv1.1.1を指定してgo getコマンドを実行し、Cobra1.1.1を使用するようにモジュールを更新します。

  1. go get github.com/spf13/cobra@v1.1.1

モジュールのgo.modファイルを開くと、go getgithub.com/spf13/cobrarequire行を更新して、指定したバージョンを参照していることがわかります。

projects / mymodule / go.mod
module mymodule

go 1.16

require (
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/spf13/cobra v1.1.1 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
)

最後に、以前の07445eaコミットやv1.1.1など、特定のバージョンのライブラリを使用しているが、最新バージョンの使用を開始したい場合は、次のことが可能です。これを行うには、特別なlatestバージョンを使用します。 モジュールを最新バージョンのCobraに更新するには、モジュールパスとlatestバージョンを指定してgo getを再度実行します。

  1. go get github.com/spf13/cobra@latest

このコマンドが終了すると、go.modファイルは、特定のバージョンのCobraを参照する前と同じように更新されます。 GoのバージョンとCobraの現在の最新バージョンによっては、出力がわずかに異なる場合がありますが、requireセクションのgithub.com/spf13/cobra行が再び最新バージョンに更新されていることを確認してください。 :

module mymodule

go 1.16

require (
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
	github.com/spf13/cobra v1.2.1 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
)

go getコマンドは、go.modファイルを手動で編集しなくても、依存関係を管理するために使用できる強力なツールです。 このセクションで見たように、モジュール名で@文字を使用すると、リリースバージョンから特定のリポジトリコミットまで、モジュールに特定のバージョンを使用できます。 依存関係のlatestバージョンに戻るために使用することもできます。 これらのオプションを組み合わせて使用すると、将来のプログラムの安定性を確保できます。

結論

このチュートリアルでは、サブパッケージを使用してGoモジュールを作成し、そのパッケージをモジュール内で使用しました。 また、依存関係として別のモジュールを追加し、さまざまな方法でモジュールバージョンを参照する方法を検討しました。

Goモジュールの詳細については、Goプロジェクトに一連のブログ投稿があり、Goツールがモジュールとどのように相互作用して理解するかについて説明しています。 Goプロジェクトには、GoモジュールリファレンスにGoモジュールに関する非常に詳細で技術的なリファレンスもあります。

このチュートリアルは、 DigitalOcean How to Code inGoシリーズの一部でもあります。 このシリーズでは、Goの初めてのインストールから、言語自体の使用方法まで、Goに関する多くのトピックを取り上げています。