Go ModulesのGo依存パッケージ管理
Golang
Lastmod: 2020-10-19

Go 1.11 から導入された Go Modules

Go 1.11 より、Go 依存パッケージの解決のための Go Modules の概念が導入された。簡単に言うと、Go 公式でサポートされた npm のようなパッケージバージョン管理ツール。

Go プロジェクト配下に見受けられる go.mod go.sum と、それにまつわる依存パッケージの話でちょっと混乱してきたので、過去の経緯から一旦整理してまとめてみました。

基本に立ち返って、パッケージのインストール

基本的な Go パッケージのインストールは、 go get で実施する。

$ go get github.com/hoge/fuga

 ローカルに置きたいプロジェクトにせよ依存ライブラリが欲しいにせよ、基本的にはこの方法でパッケージを取得していたし、今もそうする。

従来(Go 1.11 以前)の依存パッケージ管理

Go 1.11 以前では、go get で取得されたパッケージは $GOPATH 配下に置かれていた。コンパイラは、このディレクトリに配置されたライブラリを参照し、パッケージ間の依存関係を解決していた。

しかし、$GOPATH 配下には最新のパッケージしか管理されず、特定のバージョンのパッケージを使用したいときに依存関係を解決できなかった。そこで Go プロジェクト配下に vendor というディレクトリを作り、そこに所望のバージョンの依存パッケージを置くことで解決した。Go コンパイラはコンパイル時、 $GOPATH 配下より vendor 配下のパッケージを優先的に見てくれる。この vendor 配下でのパッケージバージョン管理は vendoring と呼ばれていたようだ。

当時、Go 言語自体がこの vendoring を管理する手段を提供していたわけではなかった。そのため、glidedepといったサードパーティツールが使用されていた。

Go Modules (Go 1.11 以降)におけるパッケージ管理

Go 1.11 になり、依存パッケージのバージョン管理の機能 Go Modules を公式が提供することになった。元々はVersioned Go Command (vgo)と呼ばれるバージョン管理 Go のプロトタイプがあり、Go 公式が 1.11 より Go Modules としてこれをサポートしたもの。

既に依存パッケージのバージョン管理手段があったとはいえ、公式が直々に提供しているのであればそちらを使用したい。 go fmt のような Go の思想の前例もあり、新規の Go プロジェクトでは Go Modules による依存パッケージバージョン管理が多く見られる。

Go Modules の概念

Go Modules では、以下の2つのパッケージ管理モードがある。

  • GOPATH モード
  • module aware モード

これらのモードは環境変数 GO111MODULE によって切り替えられる(詳細は後述)。

GOPATH モード

Go1.11 と同様のパッケージ管理を提供するモード。
go get により $GOPATH 配下にパッケージが置かれ、管理およびビルド時の参照はこのディレクトリに対して行われる。管理されるパッケージのバージョンは最新版のみなので、 vendor によるバージョン管理を要する。

module aware モード

Go Modules の概念に対応したモード。
go get により取得したパッケージを $GOPATH/pkg/mod 配下に置く。module aware モード中は、Go プロジェクトをどこに配置していようが $GOPATH/pkg/mod 配下のパッケージを参照する。

ちなみに、Go Modules では「モジュール」と呼ばれる概念を使用している。標準ライブラリ以外のパッケージをモジュールと呼ぶが、一方でバージョンによって同じパッケージも別のモジュールとして扱う。

module aware モードでの依存解決と、モジュールの作り方

module aware モードでは、パッケージの管理先ディレクトリが変わるだけではなく、Go プロジェクト(モジュール)が必要とするパッケージのダウンロードも自動化してくれる。

その前に、Go プロジェクトをモジュールとして初期化する必要がある。Go プロジェクトをモジュール化するには、 go mod init を実行する。

$ go mod init

するとプロジェクトルートに go.mod ファイルが作成される。ファイルにはモジュール名と、使用する Go ランタイムのバージョンが書かれている。

module github.com/rennnosuke/forblog

go 1.13

このプロジェクトに、外部のモジュールを参照するコードを追加し、ビルドしてみよう。

package main

import (
    "fmt"
    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Hello())
}
$ go build hello.go

すると、なんと勝手に必要となる依存モジュールをダウンロードし始める。

go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: extracting rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: extracting rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: extracting golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

module aware モードでは、ビルドしたモジュールの依存関係から必要な外部モジュールを自動的に割り出し、インストールしてくれる。この依存モジュールは先程の go.mod に記述される。

module github.com/rennnosuke/forblog

go 1.13

require rsc.io/quote v1.5.2

npm で言うところの package.json に依存パッケージが記述された状態で npm i しているようなもの。 Go Modules が導入される前と比較し、格段に依存パッケージを取得しやすくなった。また Go Modules 内ではバージョン別にパッケージが管理されるため、 vendor も不要になる。

この依存パッケージ解決を繰り返していると、不要なパッケージが溜まってくる。
不要なパッケージを削除するには、 go mod tidy を実行する。

$ go mod tidy

GOPATH モード・module aware モードの切り替え

先述の通り、GOPATH モードと module aware モードを切り替えるには、GO111MODULE 変数の値を変更する。 GO111MODULE の値には次の 3 種類がある。

  • on
  • off
  • auto

on

module aware モードを有効にする。

off

GOPATH モードを有効にする。

auto

バージョンによって動作を変える。
Go1.12 以前の場合、カレントディレクトリが $GOPATH 配下であれば GOPATH モードになり、そうでなければ module aware モードになる。
ただし Go1.13 からは、auto の状態でもカレントディレクトリに go.mod が存在する場合には module aware モードとして振る舞う。

Go モジュールのチェックサム

Go Module 内に生成される go.sum には依存モジュールのチェックサムが記録される。このチェックサムを使用して、依存モジュールの内容に変更があったかどうかを検出できる 。これにより、インストールするパッケージが改ざんされたものかどうかをチェックできる。

初回インストール時のパッケージのチェックサム検証はできないが、Go1.13 よりチェックサム DBからチェックサムを参照することで初回パッケージのチェックサムも検証できるようになった。

module aware モードでの go get

module aware モード上で go build して依存関係解決できるからと言って、 go get の重要性は依然変わらない。例えば Go プロジェクト内で新たに外部パッケージを使用したい場合、npm install [package] のように新しく依存関係に追加+パッケージインストールしたくなる。

module aware モードで go get を実行すると、従来どおりパッケージ(モジュール)はインストールされるが、保存先は module aware モード管理上のディレクトリになる。また go.mod go.sum に新しい依存パッケージとして記録される。

Go 1.14 以降における Go Modules への公式見解

Go 1.14 では、モジュールサポートは実稼働で使用できる状態にあると見なされ、すべてのユーザーは他の依存関係管理システムからモジュールに移行することが推奨されます。

来る Go1.14 では、すべての Go ユーザが module aware モードへ移行することが推奨されている。移行が高コストな Go パッケージなどでない限り、Go Modules への移行がどんどん進んでいくものと思われる。

所感、メモ

  • Go プロジェクトに途中から参画したときに、何も考えずに必要なパッケージがすぐインストールできてビルドできるのは本当にありがたい。

  • module aware モードでのモジュール管理先ディレクトリは任意に変更可能らしい、が変え方がわからない。要追記。

参考文献

モジュール・golang / go Wiki・GitHub

Go 言語の依存モジュール管理ツール Modules の使い方 | MMM ブログ

Go 1.13 に向けて知っておきたい Go Modules とそれを取り巻くエコシステム

go1.11 の modules の使い方について

Go と vendoring

Glide: Vendor Package Management for Golang

GitHub - golang/dep: Go dependency management tool