Goモジュールの使用方法
著者は、 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
ディレクトリを作成し、そこに移動することから始めます。
- mkdir projects
- cd projects
次に、モジュールディレクトリ自体を作成します。 通常、モジュールの最上位ディレクトリ名はモジュール名と同じであるため、追跡が容易になります。 projects
ディレクトリで、次のコマンドを実行してmymodule
ディレクトリを作成します。
- mkdir mymodule
モジュールディレクトリを作成すると、ディレクトリ構造は次のようになります。
└── projects
└── mymodule
次のステップは、mymodule
ディレクトリ内にgo.mod
ファイルを作成して、Goモジュール自体を定義することです。 これを行うには、go
ツールのmod init
コマンドを使用して、モジュールの名前(この場合はmymodule
)を指定します。 次に、mymodule
ディレクトリからgo mod init
を実行してモジュールを作成し、モジュール名mymodule
を指定します。
- go mod init mymodule
このコマンドは、モジュールの作成時に次の出力を返します。
Outputgo: 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
ファイルを開きます。
- nano 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
ファイルを開きます。
- nano main.go
main.go
ファイルに次のコードを追加してmain
パッケージを定義し、fmt
パッケージをインポートして、Hello, Modules!
メッセージをmain
関数:
package main
import "fmt"
func main() {
fmt.Println("Hello, Modules!")
}
Goでは、各ディレクトリは独自のパッケージと見なされ、各ファイルには独自のpackage
宣言行があります。 作成したmain.go
ファイルでは、package
の名前はmain
です。 通常、パッケージには任意の名前を付けることができますが、main
パッケージはGoでは特別です。 Goは、パッケージの名前がmain
であることを確認すると、そのパッケージはバイナリと見なされ、別のプログラムで使用するように設計されたライブラリではなく、実行可能ファイルにコンパイルされる必要があることを認識します。
package
が定義された後、import
宣言は、 fmt パッケージをインポートするように指示し、Println
関数を使用して
最後に、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
ファイルを実行します。
- go run main.go
コマンドを実行すると、コードで定義されているようにHello, Modules!
テキストが出力されます。
OutputHello, Modules!
このセクションでは、Hello, Modules!
を出力する最初のmain
関数を使用して、main.go
ファイルをモジュールに追加しました。 この時点では、プログラムはGoモジュールであるというメリットはまだありません。go run
で実行されているコンピューター上の任意の場所にあるファイルである可能性があります。 Goモジュールの最初の本当の利点は、GOPATH
ディレクトリ構造だけでなく、任意のディレクトリのプロジェクトに依存関係を追加できることです。 モジュールにパッケージを追加することもできます。 次のセクションでは、モジュール内に追加のパッケージを作成して、モジュールを拡張します。
モジュールへのパッケージの追加
標準のGoパッケージと同様に、モジュールには任意の数のパッケージとサブパッケージが含まれる場合と、まったく含まれない場合があります。 この例では、mymodule
ディレクトリ内にmypackage
という名前のパッケージを作成します。
mypackage
引数を指定してmymodule
ディレクトリ内でmkdir
コマンドを実行して、この新しいパッケージを作成します。
- mkdir mypackage
これにより、新しいディレクトリmypackage
がmymodule
ディレクトリのサブパッケージとして作成されます。
└── projects
└── mymodule
└── mypackage
└── main.go
└── go.mod
cd
コマンドを使用してディレクトリを新しいmypackage
ディレクトリに変更し、nano
またはお気に入りのテキストエディタを使用してmypackage.go
ファイルを作成します。 このファイルには任意の名前を付けることができますが、パッケージと同じ名前を使用すると、パッケージのプライマリファイルを簡単に見つけることができます。
- cd mypackage
- nano mypackage.go
mypackage.go
ファイルに、PrintHello
という関数を追加します。この関数は、呼び出されたときにメッセージHello, Modules! This is mypackage speaking!
を出力します。
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
への呼び出しを追加します。
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
を使用して更新されたモジュールを実行します。
- go run main.go
モジュールを再度実行すると、以前のHello, Modules!
メッセージと、新しいmypackage
のPrintHello
関数から出力された新しいメッセージの両方が表示されます。
OutputHello, 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
を取得します。
- go get github.com/spf13/cobra
このコマンドを実行すると、go
ツールは、指定したパスからCobraリポジトリを検索し、リポジトリのブランチとタグを調べて、最新のCobraのバージョンを判別します。 次に、そのバージョンをダウンロードし、モジュール名とバージョンをgo.mod
ファイルに追加して、後で参照できるようにすることで、選択したバージョンを追跡します。
次に、mymodule
ディレクトリのgo.mod
ファイルを開いて、新しい依存関係を追加したときにgo
ツールがgo.mod
ファイルをどのように更新したかを確認します。 以下の例は、リリースされているCobraの現在のバージョン、または使用しているGoツールのバージョンに応じて変更される可能性がありますが、変更の全体的な構造は類似している必要があります。
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コードで更新します。
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()
の呼び出しで実行されます。 次に、更新されたコードを実行します。
- go run main.go
次の出力が表示されます。これは、前に表示したものと似ています。 ただし、今回は、Calling cmd.Execute()!
行に示されているように、新しい依存関係を使用しています。
OutputCalling cmd.Execute()!
Hello, Modules!
Hello, Modules! This is mypackage speaking!
go get
を使用して、ここにgithub.com/sp13/cobra
などの最新バージョンのリモート依存関係を追加すると、依存関係を最新のバグ修正で更新し続けることが容易になります。 ただし、特定のバージョンのモジュール、リポジトリタグ、またはリポジトリブランチを使用したい場合があります。 次のセクションでは、go get
を使用して、そのオプションが必要なときにこれらのバージョンを参照します。
モジュールの特定のバージョンを使用する
Goモジュールはバージョン管理リポジトリから配布されるため、タグ、ブランチ、さらにはコミットなどのバージョン管理機能を使用できます。 モジュールパスの最後にある@
記号と、使用するバージョンを使用して、依存関係でこれらを参照できます。 以前は、最新バージョンのCobraをインストールしたときにこの機能を利用していましたが、コマンドに明示的に追加する必要はありませんでした。 go
ツールは、@
を使用して特定のバージョンが提供されていない場合、特別なバージョンlatest
を使用する必要があることを認識しています。 latest
バージョンは、my-tag
やmy-branch
のように、実際にはリポジトリにありません。 go
ツールにヘルパーとして組み込まれているため、最新バージョンを自分で検索する必要はありません。
たとえば、最初に依存関係を追加したときに、同じ結果に対して次のコマンドを使用することもできます。
- go get github.com/spf13/cobra@latest
ここで、現在開発中のモジュールを使用していると想像してください。 この例では、your_domain/sammy/awesome
と呼びます。 このawesome
モジュールに新しい機能が追加され、new-feature
というブランチで作業が行われています。 このブランチを独自のモジュールの依存関係として追加するには、go get
にモジュールパス、@
シンボル、ブランチ名を指定します。
- go get your_domain/sammy/awesome@new-feature
このコマンドを実行すると、go
がyour_domain/sammy/awesome
リポジトリに接続し、ブランチの現在の最新のコミットでnew-feature
ブランチをダウンロードし、その情報をgo.mod
ファイル。
ただし、@
オプションを使用できる方法はブランチだけではありません。 この構文は、タグやリポジトリへの特定のコミットにも使用できます。 たとえば、使用しているライブラリの最新バージョンでコミットが壊れている場合があります。 このような場合、壊れたコミットの直前にコミットを参照すると便利です。
モジュールのCobra依存関係を例として使用して、github.com/spf13/cobra
のcommit07445ea
を参照する必要があるとします。これは、必要な変更があり、何らかの理由で別のバージョンを使用できないためです。 この場合、ブランチやタグの場合と同じように、@
シンボルの後にコミットハッシュを指定できます。 モジュールとバージョンを指定してmymodule
ディレクトリでgo get
コマンドを実行し、新しいバージョンをダウンロードします。
- go get github.com/spf13/cobra@07445ea
モジュールのgo.mod
ファイルを再度開くと、go get
がgithub.com/spf13/cobra
のrequire
行を更新して、指定したコミットを参照していることがわかります。
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を使用するようにモジュールを更新します。
- go get github.com/spf13/cobra@v1.1.1
モジュールのgo.mod
ファイルを開くと、go get
がgithub.com/spf13/cobra
のrequire
行を更新して、指定したバージョンを参照していることがわかります。
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
を再度実行します。
- 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に関する多くのトピックを取り上げています。