89
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[GO言語]GOPATH以外の場所でのimportの挙動について整理

Last updated at Posted at 2020-11-10

$GOPATH以外の場所でimportをするとハマることが多い。
それは以下のパターンが有るからだ。

  • GOPATHモードとモジュールモードの違い
  • GitHub上にあるような外部パッケージと、ディレクトリの中に有る内部パッケージの違い

これらそれぞれについて、importがどう違うか整理する。
(GOのバージョンは1.14を想定している)

ざっくりまとめ

結論の概要を書くと以下のような感じ

  • 外部ライブラリ
    • GOPATHモード→探す先は$GOPATH/src
    • モジュールモード→探す先は$GOPATH/pkg/mod
  • 内部ライブラリ
    • 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")
}
89
69
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
89
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?