【Golang】独自パッケージのインポート

## 概要

次のようなディレクトリ構造で、Goによるコーディングを行なっていました。

その際に独自パッケージのインポートでちょっと詰まってしまったので、その際の解決方法を残します。

## 前提

ディレクトリ構造は次のようにした。

```
~/
  code/

    src/

      ruby/
      swift/
      kotlin/
      javascript/
      ...
      golang/

        repo1/
        repo2/
        ...
        repo10/

          main.go             # エントリーポイントのファイル
          sub.go              # mainパッケージ内の関数群
          readme.md
          package1/           # 自作パッケージのディレクトリ
            package1.go 
          package2/           # 自作パッケージのディレクトリ
            package2.go
          ...
```

各ファイルは次のようにした。

```go:main.go

package main

func main() {
  doSomething()
}
```

```go:sub.go

package main

// 最初に書いた呼び出し方。
// これでは呼び出せない。
import "package1"

func doSomething() {
  // 独自パッケージ内の関数を呼び出す
  package1.OriginalPackageFunction()
}
```

```go:package1.go

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
          ...
```



<details> <summary>[参考] Golang のwikiより引用</summary><div>

> ## 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".
</div></details>

## 解決策

もっとも良い方法は、`$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/"
```

と貼り直す。

そして、インポート文を書き直す。

```go
// 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文が少し変わります。

```go
// sub.go

package main

// パスの前にパッケージ名を記述する。
import package1 "github.com/username/repo10/original_package"

func doSomething() {
  // 独自パッケージ内の関数を呼び出す
  package1.OriginalPackageFunction()
}
```