Goプログラムでのパッケージ探索方法やモジュール管理についてざっくりと調べてみたので解説する。
GOPATHモードとモジュールモード
Goには、GOPATHモードとモジュールモードが存在しており、go.mod
が存在するか否かでGoが自動的に判断する。
-
go.mod
が有る:モジュールモード -
go.mod
が無い:GOPATHモード
モジュールモードは、Go 1.16以降で利用可能になったモードで、GOPATH変数を使用しないように設計されている。
GOPATH変数の代わりにGoモジュールという仕組みを使用して依存関係を管理している。
GOPATHモードでは、GoパッケージはGOPATHディレクトリ以下に配置され、importパスの一部として指定していたが、モジュールモードではモジュールごとにgo.mod(モジュールファイル)が配置されることで管理している。
パッケージの探索手順
モジュールモードにおける、パッケージの探索は以下の手順で実行される。
- カレントディレクトリのvendorディレクトリ内
- モジュールキャッシュ
- モジュールのダウンロード先
まず現在のディレクトリにvendorディレクトリがある場合、その中にパッケージが存在するかを探索する。
次に、モジュールキャッシュ内にパッケージがあるかを確認して、それでもなければモジュールのダウンロード先にあるかを探索する。
モジュールキャッシュとは、モジュールをダウンロードしてビルドした結果を保存する場所で、デフォルトで$GOPATH/pkg/modに保存される。
GOPATHモードの場合のパッケージの探索は以下の通り
- カレントディレクトリのvendorディレクトリ内
- $GOPATH/srcディレクトリ内
- $GOROOT/srcディレクトリ内
GOROOT/srcはGoのインストールディレクトリを指定する環境変数のこと。GOROOTにGoの標準パッケージやコンパイラ、開発ツールが含まれる。
「パッケージ」について
Goコンパイラの処理単位を表している。ディレクトリ内の複数のGoファイルをパッケージとして処理している。
パッケージはそれぞれimportすることができ、絶対パスや相対パスを指定する。
import "github.com/myuser/mypackage"
go getまたはgo mod tidy コマンドを使って外部パッケージをダウンロードすることで参照可能となる。
また、Goの標準ライブラリには、fmt、os、io、netなどのパッケージが含まれており、それらのパッケージをimportして使用可能。
「モジュール」について
モジュールモードでは、標準ライブラリを除くパッケージを「モジュール」として管理している。Go1.11 以降はモジュールという考え方が導入され、Go1.10以前のGOPATHによるコードの一元管理から移行が進み、プロジェクト単位でディレクトリを自由な場所に置けるようになっている。
パッケージが一つのディレクトリを指しているのに対して、モジュールはgo.modファイルのあるディレクトリ以下の全てのパッケージがモジュール配下となる。
GOPATHが担う役割
GOPATHとはGoプログラムをビルドする際に、必要なパッケージやライブラリを保存する場所を指定する環境変数のこと。
goコマンドによってパッケージのソースコードをGOPATH/src配下から探し出し、パッケージをビルドして、バイナリをGOPATH/bin配下にインストールする。
このGOPATH配下には以下のようなディレクトリが存在する。
ディレクトリ構造
$GOPATH/
|-src/
|-pkg/
|-bin/
- ・$GOPATH/src:
- Goのソースコードが格納されるディレクトリ。Go modulesを無効としている場合は、importされたpackageの探索先として使用される。このディレクトリはGo modulesを使用することで不要になる。
- ・$GOPATH/pkg:
- Goプログラムをビルドした際にソースコードからコンパイルされたオブジェクトが生成され、そのバイナリファイルが格納されるディレクトリ。
- ・$GOPATH/bin:
- Goコマンドによりインストールしたパッケージのバイナリが格納されるディレクトリ。
Go modulesについて
Go Modulesを使って依存関係管理が可能。
このGo Modulesを利用する為には、環境変数のGO111MODULE
を設定する必要がある。
Go Modules | 役割 |
---|---|
auto | Goモジュールが有効になっている場合、モジュールモードでビルドが行われる。無効な場合は、GOPATHモードでビルドが行われる。 |
on | 常にモジュールモードでビルドが行われる。 |
off | 常にGOPATHモードでビルドが行われる。 |
Go modulesを有効化しておくことで、packageの探索先として GOPATH/srcを使用する必要がなくどこでも開発が可能となる。
go modulesを有効にする為には.zshrcファイルを開いて、GO111MODULE=on
にすれば設定可能。
open ~/.zshrc
export GO111MODULE=on
export GO111MODULE=on
go.modについて
既に何度も登場しているgo.modについて解説する。
go.modはモジュールを追跡し、依存関係を管理するためのもの。
That module is defined by a go.mod file that tracks the modules that provide those packages. That go.mod file stays with your code, including in your source code repository.
Tutorial: Get started with Go
go.modの作成
go.mod init コマンドを使用してgo.modファイルを作成する。
go mod init example/hello
go: creating new go.mod: module example/hello
go mod tidy
モジュール管理を行う中で使用しなくなったパッケージを削除するためのコマンド。
go mod tidy ensures that the go.mod file matches the source code in the module. It adds any missing module requirements necessary to build the current module’s packages and dependencies, and it removes requirements on modules that don’t provide any relevant packages. It also adds any missing entries to go.sum and removes unnecessary entries.
Tutorial: Get started with Go
go mod tidy
go: finding module for package rsc.io/quote
go: found rsc.io/quote in rsc.io/quote v1.5.2
go mod tidy -vで削除されたパッケージの情報が表示される。
go mod tidy -v
unused rsc.io/quote
モジュールモードで外部パッケージをimportする場合
モジュールモードの場合、go.modファイルが置かれる。
├── go.mod
└── main.go
package main
import (
"os"
"fmt"
"gopkg.in/ini.v1" // 外部パッケージをインポート
)
goは$GOPATH/pkg/mod
内にパッケージがあるかどうかを確認しにいく。
module go-sample
go 1.19
require gopkg.in/ini.v1 v1.67.0 // indirect
モジュールモードで内部パッケージをimportする場合
内部パッケージをimportは以下の通り
├── go.mod
└── main.go
└── config.ini // 内部ライブラリ
└── utils
└── logging.go // 内部ライブラリ
package main
import (
"fmt"
"./config"
"./utils"
)
func main() {
utils.LoggingSettings(config.Config.LogFile)
fmt.Println(config.Config.ApiKey)
}
■ 参考文献