Goを勉強し始めました。
本を使って勉強しているのですが、基本的にバージョンが古いです。
ですので、Go Moduleを使ったファイル構成になっていないのがほとんど。
しかし、今後のバージョンアップでModuleを使う動きになっていく流れになっていくそうです。
ですので、初心者でもModuleを使って開発がしたいと思いまして、イジイジしてたのですが、思ったよりも苦戦しました...
特にローカルで作ったパッケージを、他のファイルにimportする方法がわからず苦戦。
同じエラーで苦しむ人を減らすべく、記事を書こうと思いました。
Go Moduleとは
パッケージ管理ツールです。
Go 1.11以前では$GOPATH/src/
配下にソースコードを置かないとローカルで作ったパッケージなどをうまく扱えなかったのですが、go module
を使えばどこのディレクトリからでも、パッケージ管理ができるようになったらしいです。
nodeのpackage.jsonみたいなものですかね。
なので、環境変数のパスに依存せずにプロジェクトを作ることが可能です。
導入
Version
❯ go version
go version go1.15.3 darwin/amd64
go envでGO111MODULE
はonかautoにしておいてください
自分はonでやっていました。
❯ go env -w GO111MODULE=on
❯ go env
GO111MODULE="on"
ファイル構成はこんな感じです。コード自体はかなりシンプルにしてます。
※ @nobonoboさんにコメントいただいたので修正しました。
各packageに一つgo.mod
が必要というところがハマりました...
go_module/
├ hello/
├ └ hello.go
├ └ hello_test.go
├ └ go.mod
├ goodbye/
│ └ goodbye.go
│ └ go.mod
└ main/
└ main.go
└ go.mod
まず各packageを作っていきます
package hello
package hello
func Hello() string {
return "Hello World"
}
一応テストも作っておきます。
package hello
import (
"testing"
)
func TestHello(t *testing.T) {
got := Hello()
want := "Hello World"
if got != want {
t.Errorf("got %q want %q", got, want)
}
}
package goodbye
package goodbye
func Goodbye() string {
return "Goodbye!!"
}
package main
package main
import (
"fmt"
"goodbye"
"hello"
)
func main() {
fmt.Println(hello.Hello())
fmt.Println(goodbye.Goodbye())
}
これでソースコードはOKなので、mainを起動してみます。
❯ go run main.go
main.go:5:2: package goodbye is not in GOROOT (/usr/local/Cellar/go/1.15.3/libexec/src/goodbye)
main.go:6:2: package hello is not in GOROOT (/usr/local/Cellar/go/1.15.3/libexec/src/hello)
importしているファイルのパッケージがないと怒られますね。
全てのディレクトリにmoduleを作りましょう。
Moduleは各ディレクトリに行かないと作れないっぽいです。
❯ go mod init main
go: creating new go.mod: module main
❯ cd hello && go mod init hello
go: creating new go.mod: module hello
❯ cd ../goodbye && go mod init goodbye
go: creating new go.mod: module goodbye
これで起動してみましょう
❯ go run main.go
main.go:5:2: package goodbye is not in GOROOT (/usr/local/Cellar/go/1.15.3/libexec/src/goodbye)
main.go:6:2: package hello is not in GOROOT (/usr/local/Cellar/go/1.15.3/libexec/src/hello)
まだ怒られますね。
これはmain.goに書いてあるimportが、先ほど作成されたmoduleと紐づいてないからですね。
importをしているファイル内で使用しているパスを各モジュールを定義していく必要があります。
なのでmainのgo.modにhelloモジュールとgoodbyeモジュールを相対パスで定義しましょう。
module main
go 1.15
replace hello => ../hello
replace goodbye => ../goodbye
もう一度mainを起動しましょう
❯ go run main
go: found goodbye in goodbye v0.0.0-00010101000000-000000000000
go: found hello in hello v0.0.0-00010101000000-000000000000
Hello World
Goodbye!!
起動できました!ここでpackage mainのgo.modをみてみましょう。
module main
go 1.15
replace hello => ../hello
replace goodbye => ../goodbye
require (
goodbye v0.0.0-00010101000000-000000000000
hello v0.0.0-00010101000000-000000000000
)
requireが自動追加されました。
これは依存モジュールのバージョンらしく、package.jsonでいうpackage-json.lockと同じですね。
今回はローカルのモジュールですが、github上のモジュールをimportしていると、こんな感じでバージョンまで出てきます。
require github.com/aws/aws-lambda-go v1.13.2
今回はローカルのパッケージをgo moduleで良い感じにimportできるようにしてみました。
もし何か良いやり方あれば教えていただきたいです!
追記
もしパッケージ構成が以下のようになっている場合はgo.modはmainのみで大丈夫です。
この構成だと、mainパッケージにhelloパッケージとgoodbyeパッケージが入っているという状態になります。
go_module/
├ hello/
├ └ hello.go
├ └ hello_test.go
├ goodbye/
│ └ goodbye.go
└ main.go
└ go.mod
ポイントとしてはmain.goでインポートする際に、mainからのパスを記述してください。
package main
import (
"fmt"
"main/goodbye"
"main/hello"
)
func main() {
fmt.Println(hello.Hello())
fmt.Println(goodbye.Goodbye())
こちらでgo run main
が起動できるようになります!
@nobonoboさんありがとうございます!!
参考サイト
https://qiita.com/uchiko/items/64fb3020dd64cf211d4e
https://takeai.silverpigeon.jp/handling-local-packages-with-go-mod/