dshimizu/blog/alpha

とりとめのないITブログ

Debian 11 に Go 1.20 をインストールしてチュートリアルとドキュメントを見ながら Go Modules の機能を浅く確認してみた

はじめに

Debian 11 を使い始めていて、Go を apt でインストールしたのですが、バージョンが 1.15.15 と古いものでした。

% go version
go version go1.15.15 linux/amd64

なので新しいバージョンのものをインストールしてみることにしました。 goenv とかを使う手もありますが、特にこだわりもないのでソースコードからインストールしてみました。

環境

環境は以下のようなものです。

% lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:    11
Codename:   bullseye
% uname -vos
Linux #1 SMP Debian 5.10.136-1 (2022-08-13) GNU/Linux

インストール

ソースを取得して /tmp/ に配置します。

% wget https://go.dev/dl/go1.20.linux-amd64.tar.gz -P /tmp/

ローカルにインストールするので、ホームディレクトリに .local ディレクトリを作成します。

% mkdir $HOME/.local

取得したソースを ~/.local に展開します。

% tar -C $HOME/.local -xzf go1.20.linux-amd64.tar.gz

~/.local/go/bin にバイナリが配置されるので、PATH を通します。

% export PATH=$PATH:$HOME/.local/go/bin

これで go コマンドが実行可能になりました。

% go version
go version go1.20 linux/amd64

Go 関係の環境変数の設定

Go 関係の環境変数がいくつかあります。 go env コマンドで確認できます。

% go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/hogehoge/.cache/go-build"
GOENV="/home/hogehoge/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/hogehoge/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/hogehoge/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/hogehoge/.local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/hogehoge/.local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="0"
GOMOD="/home/hogehoge/workspcace/go-tutorial/hello/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build308324398=/tmp/go-build -gno-record-gcc-switches"

メジャーなものを見てみます。 まず `GOROOT`は go のインストール先の PATH で、今回のやり方だと以下のようになっています。

% go env GOROOT
/home/hogehoge/.local/go

GOPATH は外部モジュール等が配置されるディレクトリです。 ドキュメントを見ると Workspace だという記述がありますが、詳しくはわかっていませんが、古いバージョンの Go の環境だと $GOPATH/src 配下にモジュールパッケージとなるディレクトリを配置して開発するというような形だったようです。 今は Go Modules がよしなにやってくれるようになり、外部モジュールも $GOPATH/pkg が使われ、 $GOPATH/src はなくても良いようなので、あまり意識せずデフォルト値を用いるので良さそうです。

デフォルトでは $HOME/go です。

% go env GOPATH
/home/hogehoge/go

チュートリアルをやってGo Modules を学ぶ

以下のチュートリアルをやって Go Modules の理解を深めてみます。

モジュールとパッケージ、バイナリ関連

雰囲気で使ってましたが、Go Modules は Go 1.16 から導入され始めた新しいモジュール管理ツールで、 go.modgo.sum を用いて依存しているモジュールの管理を行う機能のようです。

上記のドキュメントによると、モジュールは配布されるパッケージ群で、バージョン管理リポジトリまたはモジュール プロキシ サーバーから直接ダウンロードできるものを指すようで、パッケージはモジュール内のソースコードのファイル群を指すようです。

元々 go get でモジュールの管理もバイナリインストールも行われていたようですが、機能が分割され、今のところ Go Modulesの管理のための go get、ツールなどのバイナリインストールの go install という使い分けになっているようです。

go get でダウンロードされたモジュール類はモジュールキャッシュと呼ばれるディレクトリに配置されるようで、デフォルトでは $GOPATH/go/pkg/mod に配置されます。 コードの中の import 文で読み込まれるモジュールはこのディレクトリ上のファイルを見ているようです。

go install で取得されたものは $GOPATH/go/bin に配置され、これらは実行ファイルとして利用できます。

モジュールの作成

Go のインストールは終わりました。このままでも簡単なコードであれば動かすことはできますが、標準パッケージを用いたものしか動かせません。 外部モジュールのパッケージを利用する場合は、モジュールを作成して、外部モジュールのパッケージを読み込めるようにする必要があります。 パッケージ管理ツールはかつてはいろいろあったようですが、今は Go Modules を使うのが一般的なようです。

適当な作業ディレクトリを作って移動します。ここでは workspaces という名前のディレクトリにしています。

% mkdir workspaces && cd workspaces

workspaces 以下のディレクトリを作成します。

% mkdir greetings && cd greetings

モジュールの初期化を行います。 localhost/greetings の部分はモジュールパスと呼ばれる、モジュールの名前を表すもので、通常はソースコードが保持されるリポジトリの場所を指定するようです。 ここでは手元で試す分には何でも良いので localhost/greetingsとしています。

外部公開する場合はそのソースコードを配置するリポジトリの場所を指定します。GitHub に公開する場合は github.com/aaaaといった感じで、独自ドメインの場所で公開する場合はそのドメイン名とパスを記載し、その名前を用いてダウンロード可能となるようにしておく必要があるようです。

% go mod init localhost/greetings

go.mod というファイルが作成されます。

% ls
go.mod

初期状態の go.mod の中身を見るとこんな感じです。

module localhost/greetings

go 1.20

一旦 localhost/greetings のモジュールはこの状態にしておきます。

別モジュールからのコードの呼び出し

別のモジュール localhost/hello を作って localhost/greetings のモジュールを呼び出してみます。

一旦作業ディレクトリへ移動します。

% pwd
/path/to/workspaces

hello というディレクトリを作成します。

% mkdir hello 

この時点で、以下のようなディレクトリ・ファイル構成になります。

% tree -L 2

├── greetings
│   ├── go.mod
│   └── greetings.go
└── hello

2 directories, 2 files

hello ディレクトリへ移動します。

% cd hello

モジュールの初期化を行います。 今度は localhost/hello というモジュールパスにします。

% go mod init localhost/hello

go.mod はこんな感じになります。

module localhost/hello

go 1.20

以下のようなコードを配置します。 greetings.Hello(...)greetings モジュール内の関数です。 import 文で "localhost/greetings" を指定してあります。

package main

import (
    "fmt"

    "localhost/greetings"
)

func main() {
    // Get a greeting message and print it.
    message := greetings.Hello("Gopher")
    fmt.Println(message)
}

この状態で実行すると greetings モジュールのパッケージが見つからないというエラーになります。

% go run main.go
main.go:6:2: package localhost/greetings is not in GOROOT (/path/to/.local/go/src/localhost/greetings)

localhost/greetings モジュールを参照できるよう、 go.mod を編集します。 go mod edit で編集できるので、 localhost/greetings のモジュールを hello モジュールから見た相対パス ../greetings になるように指定します。

% go mod edit -replace localhost/greetings=../greetings

この状態で go.mod は以下のようになります。

module localhost/hello

go 1.20

replace localhost/greetings => ../greetings

この状態で実行してもまだエラーになります。

% go run main.go
main.go:6:2: module localhost/greetings provides package localhost/greetings and is replaced but not required; to add it:
    go get localhost/greetings

go get しろと言われますが、go mod tidy で調整します。 go mod tidy は、簡単に言うとソースコードで使われているモジュールと go.mod に記載されているファイルが同じになるよう追加・削除してくれます。

% go mod tidy
go: found localhost/greetings in localhost/greetings v0.0.0-00010101000000-000000000000

この状態で go.mod は以下のようになります。

module localhost/hello

go 1.20

replace localhost/greetings => ../greetings

require localhost/greetings v0.0.0-00010101000000-000000000000

これで実行できるようになりました。

% go run main.go
Hi, Gopher. Welcome!

まとめ

Go 1.20 のインストールと Go Modules 周りを設定しながらドキュメントを眺めて浅く理解するように努めました。 Go Modulesは公式ドキュメントがあるものの、ネットで検索すると他の情報が上位にヒットしてなかなか辿り着けなかったので、改めてドキュメントも確認できてよかったです。

参考