Edited at

そろそろ真面目に Golang 開発環境について考える — Internal Packages と Vendoring

More than 3 years have passed since last update.

前回の続き。保留していた Go 1.5 の Vendoring 機能について。

今回の調査も含めて以下のパッケージを作った

spiegel-im-spiegel/gcatcat コマンド相当の機能を持つコマンドラインツールで,内部で spiegel-im-spiegel/gutil パッケージを呼んでいる。 spiegel-im-spiegel/gutil パッケージは雑多な処理を集めたものだが,まだ作りかけ。とりあえず spiegel-im-spiegel/gcat で使うものだけを置いている。

まずはこれを導入してみる。 spiegel-im-spiegel/gutil を submodule にしてるのでちょっとウザいけどご勘弁を。あと,ユーザ名が長いのはホンマごめん。こんなことならもっと短い名前にするんだった。

C:>mkdir C:\workspace\gcat

C:>cd C:\workspace\gcat

C:\workspace\gcat>SET GO15VENDOREXPERIMENT=1

C:\workspace\gcat>SET GOPATH=C:\workspace\gcat

C:\workspace\gcat>go get -d github.com/spiegel-im-spiegel/gcat
# cd .; git --git-dir=C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gcat/.git submodule update --init --recursive
No submodule mapping found in .gitmodules for path 'vendor/github.com/spiegel-im-spiegel/gutil'
package github.com/spiegel-im-spiegel/gcat: exit status 1

C:\workspace\gcat>pushd src\github.com\spiegel-im-spiegel\gcat

C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gcat>git submodule init
Submodule 'vendor/github.com/spiegel-im-spiegel/gutil' (https://github.com/spiegel-im-spiegel/gutil.git) registered for path 'vendor/github.com/spiegel-im-spiegel/gutil'

C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gcat>git submodule update
Cloning into 'vendor/github.com/spiegel-im-spiegel/gutil'...
remote: Counting objects: 25, done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 25 (delta 10), reused 15 (delta 4), pack-reused 0
Unpacking objects: 100% (25/25), done.
Checking connectivity... done.
Submodule path 'vendor/github.com/spiegel-im-spiegel/gutil': checked out '7d271650d9937ef0e7b447aff5a55f410f2c9f89'

C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gcat>popd

C:\workspace\gcat>go install -v ./...
github.com/spiegel-im-spiegel/gcat/vendor/github.com/spiegel-im-spiegel/gutil
github.com/spiegel-im-spiegel/gcat/internal/gcat
github.com/spiegel-im-spiegel/gcat/internal/facade
github.com/spiegel-im-spiegel/gcat

C:\workspace\gcat>echo Take the Go-lang! | bin\gcat.exe
Take the Go-lang!

導入時のフォルダ構成はこうなっている。

C:\workspace\gcat>tree .

C:\WORKSPACE\GCAT
├─bin
├─pkg
│ └─windows_amd64
│ └─github.com
│ └─spiegel-im-spiegel
│ └─gcat
│ ├─internal
│ └─vendor
│ └─github.com
│ └─spiegel-im-spiegel
└─src
└─github.com
└─spiegel-im-spiegel
└─gcat
├─internal
│ ├─facade
│ └─gcat
└─vendor
└─github.com
└─spiegel-im-spiegel
└─gutil

このフォルダ構成に対して main.go の記述は以下のとおりである。


main.go

package main

import (
"os"

"github.com/spiegel-im-spiegel/gcat/internal/facade"
"github.com/spiegel-im-spiegel/gutil"
)

func main() {
cli := &gutil.CliContext{Reader: os.Stdin, Writer: os.Stdout, ErrorWriter: os.Stderr}
facadeCxt := &facade.Context{Cli: cli, CommandName: Name, Version: Version}
os.Exit(facadeCxt.Run(os.Args))
}


ポイントになるのは internal フォルダと vendor フォルダだ。もう少し詳しく見ていこう。


パッケージ外部からの呼び出しを禁止する Internal Packages

Internal Packages の仕組みは 1.4 のころから存在したが, 1.5 から GOPATH 配下のパッケージまで拡張された。

要するに internal フォルダ以下のパッケージは外部から参照できない。これは再利用の難しいビジネスロジックを含むパッケージを配置する場合にはよい仕掛けである。 Internal Packages の制約から外すには internal フォルダの外側にパッケージを再配置すればよい。


Vendoring 機能

環境変数 GO15VENDOREXPERIMENT に 1 をセットすると Go 1.5 の Vendoring 機能が使える。

追記 当初の予告通り Vendoring 機能は 1.6 から既定の機能になった。環境変数 GO15VENDOREXPERIMENT をセットしなくても有効になる)

Vendoring 機能が有効な状態では vendor フォルダが特別な意味を持つ。たとえば mypackage パッケージに対して mypackage/vendor/vpackage と配置した場合, import "vpackage" と記述すれば mypackage/vendor フォルダ以下の vpackage を探してくれる。上述の spiegel-im-spiegel/gcat の場合は github.com/spiegel-im-spiegel/gutil がこれにあたり,実体は github.com/spiegel-im-spiegel/gcat/vendor/github.com/spiegel-im-spiegel/gutil にある。パッケージが見つからない場合は

C:\workspace\gcat>go install -v ./...

src\github.com\spiegel-im-spiegel\gcat\internal\gcat\catenate.go:4:2: cannot find package "github.com/spiegel-im-spiegel/gutil" in any of:
C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gcat\vendor\github.com\spiegel-im-spiegel\gutil (vendor tree)
C:\Go\src\github.com\spiegel-im-spiegel\gutil (from $GOROOT)
C:\workspace\gcat\src\github.com\spiegel-im-spiegel\gutil (from $GOPATH)

という感じのエラーになり, Vendor tree → GOROOTGOPATH の順でパッケージを探していることが分かる。

ただしこの Vendoring 機能は実験段階であり,上手くいけば次かその次のバージョンでは既定で有効になるようだが,当面は様子見というところだろうか。


ブックマーク