My External Storage

Sep 21, 2018 - 4 minute read - Comments - go

go listで依存パッケージを一覧する。-tagsで依存パッケージを切り替える #go

今更だがGoでビルドタグで依存パッケージをどう制御できるのか確認した。
また、依存パッケージの一覧を確認するのにはgo listコマンドの使い方を調べた。

TL;DR

  • go list -f '{{join .Deps "\n"}}'コマンドで依存パッケージの情報を取得できる
  • goコマンドはgo build/go testコマンド以外のときもビルドタグに基づいた結果を出力する
  • ビルドタグによってビルドに含まれないコードの依存パッケージはビルドにも含まれない

確認で利用したソースコードは以下。

ソースコードの事前準備

確認では以下の構成のソースコードを作成した。
簡易の構成だが、AWS関係のパッケージに依存したawsパッケージと、GCP関係のパッケージに依存したgcpパッケージがある。

$ tree
.
├── LICENSE
├── README.md
├── aws           # !gcpタグ付きのコード
│   └── setup.go  # AWS SDKに依存しているコード
├── aws.go
├── gcp
│   └── setup.go  # GCP SDKに依存しているコード
├── gcp.go        # gcpタグ付きのコード
├── main.go
└── sandbox-go-cloud

main.goではtest()関数を呼ぶ。

package main

func main() {
    // tags=gcpのときはgcp.goのtest()が呼ばれる
    // tagsがなければaws.goのtest()が呼ばれる
    test()
}

test()関数は./aws.go./gcp.goに定義した。
2つのファイルはビルドタグによって必ず片方しかビルドに含まれないので、同じ関数があっても矛盾しない。

サブパッケージをimportするファイルにビルドタグをつける

Goは他言語の#ifdefのように、ビルドタグを使ってファイル単位でビルドスイッチを定義することができる。
利用するときはファイルの先頭に// +buildから始まるビルドタグを書く。必ず二行目は空行にする必要がある。複数のビルドタグを付けることも可能。

gcp.gogcpタグがついている時にしかビルドに含まれないファイルになる。

// +build gcp

package main

import (
    // go-cloud経由でGCPのSDKに依存しているサブパッケージ
    "github.com/budougumi0617/sandbox-go-cloud/gcp"
)

func test() {
    gcp.Print()
}

ビルドタグの定義には倫理式も使えるので、aws.gogcpタグがついて「いない」時にビルドに含まれるファイルになる。

// +build !gcp

package main

import (
    // AWSのSDKに依存しているサブパッケージ
    "github.com/budougumi0617/sandbox-go-cloud/aws"
)

func test() {
    aws.Print()
}

ビルドタグの定義により、aws.gogcp.goはビルド時にいずれしか含まれない。それぞれ別のサブパッケージに依存している。
また、今回は結果をわかりやすくするため、GCP関係の依存パッケージをローカルから取り除いている。(GCP関係の依存があるとパッケージが見つからずにビルドが失敗する)

go listで依存パッケージの一覧を取得する

go listコマンドはtext/templateのようなコマンドオプションを使うと、出力情報を制御できる。
また、goコマンドはgo buildgo testコマンド以外のときもビルドタグに基づいた結果を出力する。

まず、gcpタグ無しで依存パッケージの一覧を出力する。
go listコマンドではこちらの記事を参考に標準パッケージ以外の依存パッケージを出力するようにしている。
結果はたしかにAWS関係の依存のみ出力された。

$ go list -f '{{join .Deps "\n"}}' | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
github.com/aws/aws-sdk-go/aws
github.com/aws/aws-sdk-go/aws/awserr
...
github.com/google/go-cloud/blob/s3blob

gcpタグをつけてgo listコマンドを実行する。
(GCP関連のパッケージはgo getしていないのでエラーが出ているが、)GCP関連のパッケージへの依存を確認できた。
また、AWS関係の依存はなくなっていた。

$ go list -tags=gcp -f '{{join .Deps "\n"}}' | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
can't load package: package cloud.google.com/go/storage: cannot find package "cloud.google.com/go/storage" in any of:
    /usr/local/opt/go/libexec/src/cloud.google.com/go/storage (from $GOROOT)
    /Users/shimizu-yoichiro/go/src/cloud.google.com/go/storage (from $GOPATH)
...
github.com/google/go-cloud/gcp
github.com/google/go-cloud/wire

go buildして依存を確認する

今度は実際にビルドしてみる。ビルドタグがないときのビルドはAWS関係のパッケージにしか依存関係がないはずなので成功する。

$ go build

ビルドタグを付与してビルドすると、GCP関係のパッケージが見つからずビルドエラーになる。
ビルドタグで依存パッケージを制御することができた。

$ go build -tags=gcp
../../google/go-cloud/blob/gcsblob/gcsblob.go:31:2: cannot find package "cloud.google.com/go/storage" in any of:
    /usr/local/opt/go/libexec/src/cloud.google.com/go/storage (from $GOROOT)
    /Users/shimizu-yoichiro/go/src/cloud.google.com/go/storage (from $GOPATH)
../../google/go-cloud/gcp/gcp.go:24:2: cannot find package "golang.org/x/oauth2" in any of:
    /usr/local/opt/go/libexec/src/golang.org/x/oauth2 (from $GOROOT)
...

終わりに

今回はビルドタグを使って、依存パッケージを切り替えることを確認した。
なぜこんなことをしているかたというと、go-cloudやDependency Injectionの検証をしていたから。この辺は別の場所で公開する。

また、go listはオプションをつけて実行しないと大した情報が出てこないので、使うときは-fオプションをちゃんと使ったほうがよさそう。

参考

関連記事