golang におけるパッケージパスへの苦情

今日は、ただの愚痴です。

golang で開発する上で、「パッケージパス」からは逃げられない。

  • 全てのファイルは、パッケージに所属していなければいけない。
  • パッケージパスは、フォルダ構造に依存する。

そして、パッケージパスは、悲しいことに、「インターネット」から逃げられない。

  • 外部パッケージのパッケージパスは、Web の URI に依存する。

これをちょっと整理して、苦情を述べる。 (主に3つ目に対して)

結論

パッケージパスは自由に決めさせてくれ!

パッケージパスとフォルダ構造

まず最初に、全てのファイルはパッケージに所属していなければいけない。
・・・が、これはさほど問題ではない。

package main

func main() {
  // hello ...
}

問題になるのは、パッケージパスである。
以下が、標準(?)的なgolangのフォルダ構造である。

$GOPATH
+ src/
   + hoge.com/
      + mycmd/
         + libA/
         |  + lib_a1.go
         |  + lib_a2.go
         + libB/
         |  + lib_b.go
         + main.go

まず、全てのファイルは $GOPATH/src 配下にあることを期待される。 *1

次に、「同じフォルダに属するファイルは、全て同じパッケージ名」でないといけない。
つまり、パッケージ名を分けたい場合は、フォルダを分けなければいけない。

// mycmd/libA/lib_a1.go
package libA1

// mycmd/libA/lib_a2.go
package libA2  // ERROR!!

また、別パッケージを取り込む場合は、子孫フォルダにあるファイルでも、絶対パス でないといけない。 *2

// mycmd/main.go

import (
  "hoge.com/mycmd/libA" // 自分の内部パッケージを使うのに、
  "hoge.com/mycmd/libB" // なんで絶対パスなんだよ・・・
)

パッケージパスとWeb URI

そして、ここが最悪なことに、パッケージパスは「Webでアクセス可能な場所」であることを要求される。
実例で言うと、上記の mycmd の場合、以下のコマンドが動くことを要求されるのである。

  go get hoge.com/mycmd

なぜこうなったか予想だけど (文献サーチ5秒じゃ、ちゃんと見つからなかった)

  • 外部パッケージパスの衝突を防げる
  • ビルド時に、必要なパッケージを発見&入手を自動化できる

という利点があるからだろう。
かなり大きな利点である。

しかし、これが大きな弊害にもなる。

  • 自分の内部パッケージを使うのに、Web URI が必要になる。
  • fork した際に、内部パッケージの パッケージパス を変更する必要がある。
  • 開発リポジトリを複数持ちたいけど、パッケージパスは 1つしか入らない。

1つ目が、個人的に一番嫌い。
個人的なリポジトリで開発してるから、開発初期は、適当なパッケージパス hoge.com/mycmd を入れておく。
後日、github で公開すると決めた場合、そのパッケージパスを github.com/hidez8891/mycmd に書き換える必要がある。
しかし、git のログには hoge.com/mycmd が残ったままになる・・・大変かっこ悪いのである!!

2つ目も、だいたい同じ理由。
ライブラリの修正のために fork して別リポジトリにした場合 (githubでよくある)、 ビルドを通すためには、内部パッケージのパッケージパスを (いちいち) 書き換える必要がある。

3つ目は、公開と非公開リポジトリ運用をしてる場合に出会う。
たとえば、github と bitbucket で開発してる場合とか。

全て突き詰めると 「パッケージパスと配布場所を紐付ける」 ことに問題があることになる。

まとめ

結局、まとめると パッケージパスは自由に決めさせてくれ! に尽きる。

パッケージは自動取得は、go.mod があるんだから、もうそっちでいいじゃんと。
go 1.12 以降で、ここらへん、変わってくれないかなー・・・

*1: go mod で不要になるという話だったのに、未だ怒られるんだよねぇ・・・

*2: 相対パスも可能ではあるが、go.mod 経由の go build が出来ないなど、不具合が多発する