概要
次のようなディレクトリ構造で、Goによるコーディングを行なっていました。
その際に独自パッケージのインポートでちょっと詰まってしまったので、その際の解決方法を残します。
前提
ディレクトリ構造は次のようにした。
~/
code/
src/
ruby/
swift/
kotlin/
javascript/
...
golang/
repo1/
repo2/
...
repo10/
main.go # エントリーポイントのファイル
sub.go # mainパッケージ内の関数群
readme.md
package1/ # 自作パッケージのディレクトリ
package1.go
package2/ # 自作パッケージのディレクトリ
package2.go
...
各ファイルは次のようにした。
package main
func main() {
doSomething()
}
package main
// 最初に書いた呼び出し方。
// これでは呼び出せない。
import "package1"
func doSomething() {
// 独自パッケージ内の関数を呼び出す
package1.OriginalPackageFunction()
}
package package1
func OriginalPackageFunction() {
// do something.
}
GOPATHは次のように設定した。
$GOPATH = "$HOME/code"
エラー
これで go run mai.go sub.go
を実行すると、次のようにエラーが出た。
sub.go:3:2: cannot find package "package1" in any of:
/PATH/OF/YOUR/GOROOT/src/package1 (from $GOROOT)
/PATH/OF/YOUR/GOPATH/src/package1 (from $GOPATH)
この結果から、Golangでパッケージをimportすると、次のディレクトリを探すとわかった。
-
$GOROOT/src
配下のパッケージ名のディレクトリの中 -
$GOPATH/src
配下のパッケージ名のディレクトリの中
解決のポイント
Golangでのパッケージインポートは、Gopherたちがどのようにパッケージのやり取りをしているかに関係している。
Golang では、外部パッケージの取得に go get
コマンドを使用する。
例えば、githubに公開されているgo-kit/kit
というパッケージを使いたい場合は、
go get github.com/go-kit/kit
というコマンドでパッケージの取得を行える。
具体的には、$GOAPTH/src
内に、指定したレポジトリをクローンする。
つまり、go get github.com/go-kit/kit
を実行すると、次のようなディレクトリ構造になる。
~/
code/
src/
ruby/
swift/
kotlin/
javascript/
...
+ github.com/ # ここにクローンされる
+ go-kit/
+ kit/
+ ...
golang/
repo1/
repo2/
...
repo10/
main.go # エントリーポイントのファイル
sub.go # mainパッケージ内の関数群
readme.md
package1/ # 自作パッケージのディレクトリ
package1.go
package2/ # 自作パッケージのディレクトリ
package2.go
...
[参考] Golang のwikiより引用
Repository integration and creating "go gettable" projects
When fetching a package the go tool looks at the package's import path to discover a URL. For instance, if you attempt to
go get github.com/go-kit/kit
the go tool will get the source from the project hosted at https://github.com/go-kit/kit/. It will clone the repository to
$GOPATH/src/github.com/go-kit/kit
As a result, if (from your repository project) you import a package that is in the same repository, you need to use its "full" import path - the place "go get" puts it. In this example, if something else wants to import the "kit" package, it should import "github.com/go-kit/kit" rather than "kit".
解決策
もっとも良い方法は、$GOPATH/src
以下が、自身のリモートレポジトリのURLと同じになるように作業ディレクトリを変更すること。
例えば、githubでレポジトリを管理しているなら、github.com/username/reponame となるので、
今回の場合だと、
~/
code/
src/
ruby/
swift/
kotlin/
javascript/
...
golang/
src/
+ github.com/
+ username/
repo1/
repo2/
...
repo10/
main.go
sub.go
readme.md
package1/
package1.go
package2/
package2.go
...
とした上で、
$GOPATH="$HOME/code/src/golang/"
と貼り直す。
そして、インポート文を書き直す。
// sub.go
package main
// これで呼び出せる。
import "github.com/username/repo10/package1"
func doSomething() {
// 独自パッケージ内の関数を呼び出す
package1.OriginalPackageFunction()
}
補足 1
他の言語では、外部モジュールを相対パスで指定してインポートすることができます。
Golangでも、相対パスでのimportも可能です。
import "./package1"
この方法は推奨されていません。
特に、 go test
などで相対パスによるimportを行うと、
local import "./package1" in non-local package
となります。
補足 2
一つのpackageディレクトリにまとめることもできるようです。
~/
code/
src/
ruby/
swift/
kotlin/
javascript/
...
golang/
src/
github.com/
username/
repo1/
repo2/
...
repo10/
main.go
sub.go
readme.md
+ original_packages/
+ package1.go
+ package2.go
+ ...
ただし、この場合、import文が少し変わります。
// sub.go
package main
// パスの前にパッケージ名を記述する。
import package1 "github.com/username/repo10/original_package"
func doSomething() {
// 独自パッケージ内の関数を呼び出す
package1.OriginalPackageFunction()
}