go mod tidyをもっと詳しく調べてみた

「バットマンGopherくん」
Powered by Gopherize.me - A Gopher pic that's as unique as you
The Gopher character is based on the Go mascot designed by Renée French

はじめに

こんにちは!!!!ライクル事業部エンジニアの黒田(@knkurokuro7)です。

今日もGoについてブログを書こうと思います!

背景

Goを使っている中で、go mod tidyコマンドを使うことがよくあります。
具体的には何をしているのかということが気になったので、深ぼってみることにしました!

ちなみに、ちょうど昨日部内で少しだけ話題になっていたのですが、「go mod tidy」の読み方は「ごーもっどたいでぃ」ですね!
「tidy」を「てぃーでぃ」と読みがちなのはあるあるですね。。。

前提

Goのバージョンは1.18を想定しています。

go mod の概要

まずそもそもgo modとはなんなのかというところを整理します。

go modファイルについて、こちらのチュートリアルに以下のような説明があります。

go.dev

When your code imports packages contained in other modules, you manage those dependencies through your code's own module. 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.
To enable dependency tracking for your code by creating a go.mod file, run the go mod init command, giving it the name of the module your code will be in. The name is the module's module path.

翻訳

あなたのコードが他のモジュールに含まれるパッケージをインポートする場合、あなたのコード自体のモジュールを通して、それらの依存関係を管理します。そのモジュールは、それらのパッケージを提供するモジュールを追跡する、 go.mod ファイルによって定義されます。そのgo.modファイルは、ソースコードリポジトリを含めつつ、あなたのコードと一緒に残ります。
go.modファイルを作成してコードの依存性の追跡を有効にするには、go mod initコマンドを実行し、あなたのコードが入るモジュールの名前を指定します。この名前はそのモジュールのモジュールパスです。

つまり、go modファイルとは

  • パッケージの依存関係を管理するためのもの。
  • go mod initコマンドで作成する。

ということがわかります。

go mod tidyについて

さて、go mod tidy について詳しくみてみます! こちらのドキュメントを参考にしました!

go.dev

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.

翻訳

go mod tidy は go.mod ファイルがモジュール内のソースコードに合っていることを確認します。現在のモジュールのパッケージと依存関係をビルドするために必要な、不足しているモジュールの要求を追加します。そして、関連するパッケージを提供しないモジュールの要求を削除します。また、go.sum に不足しているエントリを追加し、不要なエントリを削除します。

このように、go mod tidyは

  • go.mod ファイルがソースコードに合っているかを確認する。
  • ビルドするために必要なモジュールを追加したり、削除したり整理してくれる。
  • go.sumにその情報を記載する。

ということがわかります。

ここまでは、なんとなく知っていたのですが、それ以降は知らないことが多かったので、いくつかの点に注目してまとめていきます。

require ディレクティブについて

依存先のパッケージやバージョンが記載されるところです。

こちらのソースコードに対してgo mod initを実行して、

package main

import (
    "fmt"

    "rsc.io/quote"
)

func main() {
    fmt.Println(quote.Go())
}

試しに作ってみたgo.modファイルの中身は以下のようになっていました。

module hello

go 1.18

require rsc.io/quote v1.5.2

require (
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
    rsc.io/sampler v1.3.0 // indirect
)

このようにrequireディレクティブの部分に、具体的なパッケージが記載されていました。

上と下の二箇所にrequireディレクティブが分かれているのは、Go1.17以降に変更された部分です。
上の方に、直接的な依存先
下の方に、間接的な依存先が書かれています。
また、間接的な依存先の後には、// indirectのコメントがついています。

フラグについて

go mod tidyを実行するときに、フラグをつけることができます。 フラグをつけることで何ができるのかを調べてみました。

-eフラグ

パッケージの読み込み中にエラーが起こってもgo mod tidyの処理を継続します。

-vフラグ

削除されたパッケージに関する情報を標準エラーに出力します。

試しに、"rsc.io/quote"というパッケージをソースコードから削除した後に、go mod tidyを実行すると、

-vあり

$ go mod tidy

-vなし

$ go mod tidy -v
unused golang.org/x/text
unused rsc.io/quote
unused rsc.io/sampler

このように削除されたパッケージの情報が出力されました。

-goフラグ

$ go mod tidy -go=1.19のようにバージョンを指定すると、そのバージョン指定のgoディレクティブを更新します。
goディレクティブとは、go.modファイルで指定されている有効なGoのリリースバージョンのことです。

go help mod tidyコマンドを実行すると、詳しい説明がありました。

The -go flag causes tidy to update the 'go' directive in the go.mod file to the given version, which may change which module dependencies are retained as explicit requirements in the go.mod file.

翻訳

go フラグは、tidy に go.mod ファイル内の 'go' ディレクティブを定められたバージョンに更新させます。これは、どのモジュールの依存がgo.mod ファイル内の明示的な要件として保持されるか、を変更します。

とあり、

  • goディレクティブを書き換える。
  • モジュールの依存の情報を書き換える。

ということがわかります。

-compatフラグ

$ go mod tidy -compat=1.19のようにバージョンを指定すると、そのバージョン指定のgoディレクティブを更新はされないですが、指定されたバージョンの仕様で処理されます。

こちらも、go help mod tidyコマンドを実行すると、

The -compat flag preserves any additional checksums needed for the 'go' command from the indicated major Go release to successfully load the module graph, and causes tidy to error out if that version of the 'go' command would load any imported package from a different module version. By default, tidy acts as if the -compat flag were set to the version prior to the one indicated by the 'go' directive in the go.mod file.

翻訳

compat フラグは、モジュールグラフを正常にロードするために、示されたメジャーな Go リリースからの 'go' コマンドに必要な追加のチェックサムを保持します。また、そのバージョンの 'go' コマンドが、異なるモジュールバージョンからインポートされたパッケージをロードしようとすると tidy にエラーを発生させます。デフォルトでは、tidy は -compat フラグが go.mod ファイル中の 'go' ディレクティブによって示されるバージョンより、前のバージョンに設定されているかのように動作します。

とあり、

  • 指定されたGoのバージョン以外のパッケージをロードしようとすると、エラーになる。
  • デフォルトでは、go mod tidy はgo modファイルに指定されているgoディレクティブのGoのバージョンより前のバージョンに設定されているかのような動作をする。

ということがわかります。

終わりに

普段使っているコマンドでも調べてみると知らないことがたくさんあるなぁと思いました!
時間のある時に、他のgoコマンドの仕様についても調べてみたいです。

もしこの記事の内容についてご指摘等ございましたら、お気軽に教えていただけると嬉しいです!!

ここまで読んでいただいて本当にありがとうございます。

参考にさせていただいた記事

Go Modules Reference - The Go Programming Language

Tutorial: Get started with Go - The Go Programming Language

go mod完全に理解した

Go のモジュール管理【バージョン 1.17 改訂版】

go.modとgo.sumの読み方