1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Go Moduleでローカルで作成したパッケージを読み込む

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

hello.go
package hello

func Hello() string {
    return "Hello World"
}

一応テストも作っておきます。

hello_test.go
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

goodbye.go
package goodbye

func Goodbye() string {
    return "Goodbye!!"
}

package main

main.go
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モジュールを相対パスで定義しましょう。

go.mod

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をみてみましょう。

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からのパスを記述してください。

main.go
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/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?