理系学生日記

おまえはいつまで学生気分なのか

Golangでバイナリを配布するとき、go-licensesとgoxzを使って利用モジュールのLICENSE、NOTICEを同梱する

Golangで作ったプロダクトを公開・配布するとき、課題となるのが利用するモジュールのライセンスです。 MITライセンスであれ、MPLライセンスであれ、利用するモジュールのLICENSEやNOTICEファイルを同梱することになるでしょう。

goxzとgo-licensesを使うと、クロスビルドした実行バイナリと利用モジュールのLICENSE等を含め頒布物を作成できるようになります。

最終イメージ

最終イメージですが、以下のようなMakefileを用意しました。 プロダクトに設定したライセンスで利用できないモジュールがあるかどうかをlicense-checkで確認した後、cross-build.shを呼び出します。

.PHONY: license-check
license-check:
  go-licenses check .

.PHONY: build
build:
  go build -ldflags=$(LDFLAGS) -o $(NAME) .

.PHONY: cross-build
cross-build: license-check build
  NAME=$(NAME) VERSION=$(VERSION) LDFLAGS=$(LDFLAGS) ./build/package/cross-build.sh

cross-build.shはおおよそ以下のようなシェルスクリプトになります。 go-licensesの挙動に戸惑ったりもしましたが、現状はこれでうまくいっています。

#!/bin/bash -eu

# workdir=$(mktemp -d) とすると goxz でエラーが発生してしまう
workdir=dist-licenses 
rm -rf ${workdir}
mkdir -p ${workdir}
go-licenses save . --force --save_path ${workdir} --alsologtostderr

# go-licenses でソースをダウンロードすることになる場合、当該ファイルのパーミッションが
# 0666 になっており、結果として再実行ができなくなるというバグがある。
#   see: https://github.com/google/go-licenses/issues/11
# 暫定対応として、強制的に書き込み権限を付与する
chmod +w -R ${workdir}
goxz \
    -d dist \
    -n ${NAME} \
    -pv ${VERSION} \
    -build-ldflags \'"${LDFLAGS}"\' \
    -include "$(find ${workdir} -type f)"

同梱されていることの確認

$ unzip -l /Users/kiririmode/src/gitlab.com/myorgs/my-product/dist/my-product_v0.0.1_darwin_amd64.zip
Archive:  /Users/kiririmode/src/gitlab.com/myorgs/my-product/dist/my-product_v0.0.1_darwin_amd64.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/
    11338  02-11-2021 15:22   my-product_v0.0.1_darwin_amd64/LICENSE
      210  02-11-2021 14:26   my-product_v0.0.1_darwin_amd64/README.md
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gitlab.com/
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gitlab.com/myorgs/
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gitlab.com/myorgs/my-product/
    11338  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gitlab.com/myorgs/my-product/LICENSE
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gopkg.in/
        0  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gopkg.in/yaml.v2/
    11357  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gopkg.in/yaml.v2/LICENSE
      560  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/dist-licenses/gopkg.in/yaml.v2/NOTICE
  4716008  02-11-2021 18:51   my-product_v0.0.1_darwin_amd64/my-product
---------                     -------
  4750811                     13 files

go-licenses

go-licensesはGoogle謹製のプロダクトで、パッケージや実行バイナリから依存モジュールを解析し、 各モジュールのライセンスを取得してくれます。

いくつかサブコマンドがあるのですが、saveを使うと依存モジュールのLICENSENOTICEをかき集めてくれます。かき集めるディレクトリは--save_pathで指定しましょう。

$ go-licenses save . --force --save_path dist-licenses --alsologtostderr

$ find dist-licenses -type f
dist-licenses/gopkg.in/yaml.v2/LICENSE
dist-licenses/gopkg.in/yaml.v2/NOTICE
dist-licenses/gitlab.com/myorgs/my-product/LICENSE

ハマりポイント

じつは、このsaveコマンドでかき集めたファイルのパーミッションはread-only(0666)になります。 make cleanで消すこともできず、なかなか扱いづらい。

$ find dist-licenses -type f -ls
 30520975     12 -r--r--r--   1 kiririmode staff       11357  2 11 19:18 dist-licenses/gopkg.in/yaml.v2/LICENSE
 30520976      4 -r--r--r--   1 kiririmode staff         560  2 11 19:18 dist-licenses/gopkg.in/yaml.v2/NOTICE
 30520972     12 -rw-r--r--   1 kiririmode staff       11338  2 11 19:18 dist-licenses/gitlab.com/myorg/my-product/LICENSE

これはissueとして登録されているのですが、対応されていません。

このため、強制的に書き込みbitを付与する対応をします。

$ chmod +w -R dist-licenses

goxz

goxzは、クロスビルドとパッケージングのための軽量なツールです。 各プラットフォーム用にクロスビルドを行い、README等を含めた形でパッケージングしてくれます。

goxzには-includeオプションがあり、ここにはパッケージング時に同梱するファイルを指定できます。 複数指定するときにはどうするのだろうとソースを見ると、空白区切りで指定できるようです。

このため、ファイルをぶちこむようにしました。 シェルの解釈できる長さが気になるところですが、当面はこれでなんとかなるでしょう。

    -include "$(find dist-licenses -type f)"

まとめ

ライセンス等の同梱を行わない配布はライセンス違反の可能性が高くなります。 一方で、たくさんのモジュールに依存したときはライセンスファイルの扱いは煩雑です。

様々なツールを使うことで、このあたりの煩雑さを解消していけると良いですね。