Goを操作する上で基本となるPackageやModule、またそれに関連するコマンドなどについていろいろとまとめていきます。
※前提としてGO111MODULE
が設定されていないGo 1.13〜を基本としているので、それ以前のバージョンの場合は対象外となります
※今回の前提の環境はGo 1.16です
How to Write Go Code - The Go Programming Language
Packageとは
- 同じディレクトリに存在するソースコードファイル群のことです
- ソースコードファイル群は一緒にコンパイルされます
- 同じPackageに所属するソースファイル間では、関数や変数などで共有されます
例えば以下のような2つのファイルがあるとしましょう。
package main import "fmt" func main() { fmt.Println("test1") test2() }
package main import "fmt" func test2() { fmt.Println("test2") }
これらのファイルをコンパイルして実行するとこのようになります。同じpackageなので1つのファイルに収まりましたね。
$ go build . $ ./test test1 test2
ちなみに$ go run test1.go
と実行するとエラーになります。
$ go run test1.go # command-line-arguments ./test1.go:9:2: undefined: test2
これは必要なファイルのうち片方しかコンパイル→実行の処理を行っていないためです。なので以下のようにして両方とも読み込むようにしましょう。
$ go run test1.go test2.go test1 test2 または $ go run . test1 test2
Module(モジュール)とは
一緒にリリースされた関連するGoパッケージの集合体のことです。簡単いうと複数のパッケージの集合体ですね。
一般的にGoリポジトリには1つのGoモジュールがリポジトリの直下に格納されています。例えばgithubにaaaというリポジトリがあったら、そのルートディレクトリにモジュールが格納されているということです。github.com/aaa/hogehoge_moduleといった感じですね。
go.modとは
go.mod
はGoモジュールのパスを書いておくファイルです。モジュールの中に含まれる全てのパッケージインポート用のパスプレフィックスが書かれています。
以下は3つのモジュールを使用しているgo.mod
の中身です。
root@7fb8580f21c0:/go/src/app# cat go.mod module app go 1.16 require ( github.com/Wing924/ltsv v0.3.1 gorm.io/driver/mysql v1.0.5 gorm.io/gorm v1.21.7 )
モジュールの中にはgo.mod
ファイルやパッケージが含まれており、さらにサブディレクトリにいくとまた別のgo.mod
ファイルが含まれていることもあります。
gorm@v1.21.7モジュールの中を確認するとこうなっています。go.mod
ファイルがありますね。
root@7fb8580f21c0:/go/src/app/pkg/mod/gorm.io/gorm@v1.21.7# ls License finisher_api.go model.go README.md go.mod prepare_stmt.go association.go go.sum scan.go callbacks gorm.go schema callbacks.go interfaces.go soft_delete.go chainable_api.go logger statement.go clause migrator statement_test.go errors.go migrator.go utils
各モジュールのパスは、パッケージインポート用のパスプレフィックスとして機能するだけでなく、goコマンドがダウンロードする場所を示しています。
たとえば、モジュール(golang.org/x/tools) をダウンロードするために、goコマンドはhttps://golang.org/x/tools で示されるリポジトリを参照します。
go.sumとは
中を見てみるとこんな感じになっており依存モジュールのチェックサムが記録されています。(sum
はchecksum
のことですかね)
※チェックサムとはあるファイルやデータの一意性を示すハッシュのことです。簡単にいうとファイルの中身やデータごとに全く違うハッシュを生成することができるので、ファイルが改竄されていないことを証明されるときに用いられます
$ cat go.sum github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
このチェックサムを利用してGoでは使っているモジュールの内容に変更があったかどうかを検出しています。
コマンドの意味
go get [import path]
- モジュールのダウンロード及び
go.mod / go.sum
の修正go get golang.org/x/xerrors
go mod tidy
はソースコード内のimportを宣言的なものとして捉えそこに近づけるように追加/削除を行いますが、go get
はユーザが特定のもの指定してインストールします。
参考: Go1.16からの go get と go install について - Qiita
go install [import path]
- インターネット上のツールを
$GOPATH/bin
にインストールする機能go install golang.org/x/tools/gopls@latest
- 作業ディレクトリをビルドして
$GOPATH/bin
に配置する機能go get localhost/xxx/xxx
参考: Go1.16からの go get と go install について - Qiita
go mod init [import path]
現在のディレクトリにおけるモジュールを外部からインポートする時のパスを設定します。基本的にはGitHubや自サイトなどでの公開が一般的となるため、https://[import path]となるようなimport pathを設定しましょう。
$ go mod init example.com/user/hello go: creating new go.mod: module example.com/user/hello $ cat go.mod module example.com/user/hello go 1.16
go mod tidy
以下の機能を持っています。
- コード内でimportしているが
go get
されていないモジュールをダウンロード - ダウンロードされているがコード内でimportされていないモジュールを削除
- 上記2つを実施したあとに
go.mod
とgo.sum
を修正 または 削除
試してみよう
まずは準備します。外部モジュールを使う簡単なコードを用意します。
package main import ( "fmt" "github.com/google/go-cmp/cmp" ) func main() { fmt.Println("hello") fmt.Println(cmp.Diff("Hello World", "Hello Go")) }
必要なパッケージをインストールします。
$ go get github.com/google/go-cmp/cmp go: downloading github.com/google/go-cmp v0.5.5 go get: added github.com/google/go-cmp v0.5.5 $ cat go.mod module example.com/user/hello go 1.16 require github.com/google/go-cmp v0.5.5 // indirect $ go run hello.go hello string( - "Hello World", + "Hello Go", )
先ほどのhello.go
から外部モジュールを外してみます。先ほどgo get
したモジュールが不要になりますね。
package main import ( "fmt" ) func main() { fmt.Println("hello") }
ここでgo mod tidy
を実行してみましょう。これによって使われていない不要なモジュールが削除されます。便利!
$ go mod tidy $ cat go.mod module example.com/user/hello go 1.16 $ cat go. go.mod go.sum $ cat go.sum
また元に戻してみます。要するにモジュールが足りていない状態です。
package main import ( "fmt" "github.com/google/go-cmp/cmp" ) func main() { fmt.Println("hello") fmt.Println(cmp.Diff("Hello World", "Hello Go")) }
ここでgo mod tidy
を叩くとこのように必要なモジュールが勝手にインストールされます。(pythonにおけるpip install -r requirement.txt
に近いですね)
$ go mod tidy go: finding module for package github.com/google/go-cmp/cmp go: found github.com/google/go-cmp/cmp in github.com/google/go-cmp v0.5.5 go: downloading golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
Moduleを作ってインストールしてみる
$ mkdir hello # Alternatively, clone it if it already exists in version control. $ cd hello $ go mod init example.com/user/hello go: creating new go.mod: module example.com/user/hello $ cat go.mod module example.com/user/hello go 1.16
続いてhello.go
を作成します。
package main import "fmt" func main() { fmt.Println("Hello, world.") }
そしたら以下のコマンドを実行して、example.com/user/hello
モジュール使ってhello
コマンドを作成しましょう。
$ go install example.com/user/hello $~/go/bin/hello hello
この時インストール先のディレクトリはGOPATH
やGOBIN
によって管理されるので注意してください。
GOBIN
が定義されている場合:$GOBIN
直下に保存されますGOPATH
が定義されている場合:GOPATHリストの最初のディレクトリのbin配下に保存されます→$GOPATH/bin