$GOPATH
以外の場所でimportをするとハマることが多い。
それは以下のパターンが有るからだ。
- GOPATHモードとモジュールモードの違い
- GitHub上にあるような外部パッケージと、ディレクトリの中に有る内部パッケージの違い
これらそれぞれについて、importがどう違うか整理する。
(GOのバージョンは1.14を想定している)
ざっくりまとめ
結論の概要を書くと以下のような感じ
- 外部ライブラリ
- GOPATHモード→探す先は
$GOPATH/src
- モジュールモード→探す先は
$GOPATH/pkg/mod
- GOPATHモード→探す先は
- 内部ライブラリ
- GOPATHモード→探す先は相対パス
- モジュールモード→探す先はgo.modにかかれたmodule名+go.modがあるディレクトリからの相対パス
はじめに
環境
- OS: MacOS
- GO言語: go version go1.14.3 darwin/amd64
GOPATHモードとモジュールモードの切り替えについて
GOPATHモードとモジュールモードは、go.mod
ファイルの有無によって切り替わる
- GOPATHモード:
go.mod
ファイルが無い - モジュールモード:
go.mod
ファイルが有る(実行するディレクトリよりも上位の階層にgo.modが有っても有効になる)
外部パッケージのimport
GOPATHモードでの外部パッケージのimport
ディレクトリ構成
.
└── main.go
main.go
package main
import (
"fmt"
"github.com/antonholmquist/jason" // 外部パッケージのインポート
)
func main() {
v, _ := jason.NewObjectFromBytes([]byte(`{"Name": "fetaro"}`))
fmt.Println(v)
}
探しに行く場所はどこか?
$GOPATH/src/
具体的には、$GOPATH/src/github.com/antonholmquist/jason
ここにパッケージがないとどうなるか
cannot find package
エラーになる
main.go:5:2: cannot find package "github.com/antonholmquist/jason" in any of:
/usr/local/Cellar/go/1.14.3/libexec/src/github.com/antonholmquist/jason (from $GOROOT)
/Users/tetsutaro.watanabe/go/src/github.com/antonholmquist/jason (from $GOPATH)
どうやってパッケージを配置するか?
go get
具体的には go get github.com/antonholmquist/jason
モジュールモードでの外部パッケージのimport
ディレクトリ構成
.
├── go.mod ←go.modファイルを置いたのでモジュールモードになる
└── main.go
main.go
package main
import (
"fmt"
"github.com/antonholmquist/jason" // 外部パッケージのインポート
)
func main() {
v, _ := jason.NewObjectFromBytes([]byte(`{"Name": "fetaro"}`))
fmt.Println(v)
}
go.mod
module hoge
go 1.14
探しに行く場所はどこか?
$GOPATH/pkg/mod
具体的には、/Users/tetsutaro.watanabe/go/pkg/mod/github.com/antonholmquist/jason\@v1.0.0/
ここにパッケージがないとどうなるか
勝手にダウンロードされる。
まだダウンロードされていない状態で go run main.go
をすると以下のようにダウンロードされてから実行される
$ go run main.go
go: finding module for package github.com/antonholmquist/jason
go: found github.com/antonholmquist/jason in github.com/antonholmquist/jason v1.0.0 ←ダウンロード結果
{"Name":"fetaro"} ←実行結果
一度ダウンロードされると go.mod
が書き換わる
書き換わったgo.mod
module hoge
go 1.14
require github.com/antonholmquist/jason v1.0.0 // indirect
内部パッケージのimport
GOPATHモードでの内部パッケージのimport
ディレクトリ構成
.
├── main.go
└── mypkg ←内部パッケージ
└── bar.go
main.go
package main
import (
"./mypkg" // 内部パッケージのインポート
)
func main() {
mypkg.PrintBar()
}
bar.go
package mypkg
import "fmt"
func PrintBar(){
fmt.Println("bar")
}
探しに行く場所はどこか
main.goからの相対パスで ./mypkg
ここにパッケージがないとどうなるか
cannot find package
エラーになる
main.go:4:2: cannot find package "." in:
/Users/tetsutaro.watanabe/git/golab3/mypkg
モジュールモードでの内部パッケージのimport
ディレクトリ構成
.
├── go.mod ←go.modがあるからモジュールモード
├── main.go
└── mypkg ←内部ライブラリ
└── bar.go
main.go
package main
import (
"mymodule/mypkg" //←ここをgo.modに書いたモジュール名 + パッケージ名にする
)
func main() {
mypkg.PrintBar()
}
go.mod
module mymodule
go 1.14
bar.go
package mypkg
import "fmt"
func PrintBar(){
fmt.Println("bar")
}
探しに行く場所はどこか
ここが難しい。
モジュールモードではimportに指定した "mymodule/mypkg"
はモジュールとパッケージを連結したものになる。
具体的には、 (go.modに記載したモジュール名) + (go.modのある場所からのパッケージの相対パス)を探しに行くことになる。
今回の例では、mymodule
がモジュールで、 /mypkg
は、「module mymoduleを指定したgo.mod」のあるディレクトリからの相対パスになる。
ここにパッケージがないとどうなるか
エラーになる
main.go:4:2: package mymodule/mypkg is not in GOROOT (/usr/local/Cellar/go/1.14.3/libexec/src/mymodule/mypkg)
探しに行く場所の実験1
go.modがmain.goよりも上位に有るケース
例えば以下のようなディレクトリ構成の場合
.
├── c_pkg
│ ├── d_pkg
│ │ └── bar.go
│ └── main.go
└── go.mod
go.modに module a_module/b_module
を指定すると、
main.goでは import "a_module/b_module/c_pkg/d_pkg"
で読み込める
探しに行く場所の実験2
よくあるcmdとinternalの構成
.
├── cmd
│ └── main.go
├── go.mod
└── internal
└── lib.go
go.mod
module github.com/fetaro/my_module
go 1.14
main.go
package main
import (
"github.com/fetaro/my_module/internal"
)
func main() {
internal.PrintHoge()
}
lib.go
package internal
import "fmt"
func PrintHoge(){
fmt.Println("hoge")
}