読者です 読者をやめる 読者になる 読者になる

オープンソースライセンスの管理を楽にする -Android アプリ編

こんにちは、投稿推進部の吉田です。
オープンソースライセンスの管理はアプリ開発における悩み事の一つですよね。今回はこの煩雑な作業をgradleプラグインを使って自動化する話をします。 本稿におけるライセンスの管理とは、OSSライブラリの著作権者とライセンス文の管理に限定されることを予めご了承下さい。

紹介するgradleプラグイン

license-tools-pluginが提供する機能

  • yamlを使ったオープンソースライセンスの管理
  • ライセンス追記漏れのチェック
  • ライセンス一覧のhtmlの作成

license-tools-pluginの利用方法

複雑な設定は必要なく、3ステップでライセンス一覧を管理することが出来ます。

  1. プロジェクトにlicense-tools-pluginプラグインを導入する
  2. build.gradleから依存ライブラリ情報を抜き出してyamlファイルを作成
  3. yamlからライセンス一覧のhtml作成

導入後はPull Request毎にCIでyamlファイルが適切な状態かチェックすると良さそうですね。

1.プラグインの導入

build.gradleに以下の内容を記述します。

apply plugin: 'com.cookpad.android.licensetools'

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    // 2016-04-28時点の最新バージョンは0.12ですが、最新のものを利用して下さい
    classpath 'com.cookpad.android.licensetools:license-tools-plugin:0.12.0'
  }
}

license-tools-pluginはgradle ver.2.10以上を要求するので、古いバージョンを利用している場合は、gradle/wrapper/gradle-wrapper.propertiesのdistributionUrlを更新します。

distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

導入手順は以上です。

2.yamlファイルの作成

license-tools-pluginにはyamlファイルを生成するためのコマンドはありません。 代わりにcheckLicensesタスクを実行することで既存のyamlファイルとの差分を出力出来ます。 初回利用時はcheckLicensesタスクが全てのライセンス情報をyaml形式でコンソールに出力するので、 app/licenses.ymlというファイルを作成して出力内容をコピペします。

- artifact: com.squareup.okhttp:okhttp:+
  name: OkHttp
  copyrightHolder: #COPYRIGHT_HOLDER#
  license: #LICENSE#
- artifact: com.squareup.okio:okio:+
  name: Okio
  copyrightHolder: #COPYRIGHT_HOLDER#
  license: #LICENSE#

出力されたyamlでは、情報を取得できない部分がプレースホルダになるので適宜修正して下さい。上記のyamlの場合は#COPYRIGHT_HOLDER##LICENSE#の部分を調べて手作業で修正する事になります。
ここまでの作業が完了したら、もう一度checkLicensesを実行しましょう。先程は失敗したタスクが正常に終了するようになるはずです。 また社内ライブラリやGoole Play Servicesなど一覧に表示する必要のないライセンス情報はskip: trueで一覧から除外することが出来ます。

- artifact: com.google.android.gms:play-services-basement:+
  name: play-services-basement
  skip: true  

3.ライセンス一覧の作成

ライセンスの表示方法はいくつか考えられます。例えばyamlを直接読みListViewなどのネイティブUIで表示する事も可能です。 この場合、license-tools-pluginはUIパーツの提供はしていないので、他のライブラリを利用するか自分で実装することになります。
UIパーツを提供しない代わりにgenerateLicensePageというタスクが用意されていて、yamlからフォーマットされたhtmlを作成する事が可能です。 一例としてgenerateLicensePageで生成したhtmlとwebviewを利用したサンプルコードを貼りました。下のスクリーンショットのようなスタイルのhtmlが表示されていれば成功です。

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstancestate);
  setContentView(R.layout.activity_main);
  WebView webview = findViewById(R.id.web_view);
  webView.loadUrl("file:///android_asset/licenses.html");
}

おわりに

license-tools-pluginはまだリリースされたばかりで機能はそれほど多くないですが、ライブラリのライセンス管理を劇的に楽にすることが出来ます。 Cookpadではこのようなチョットしたものでもライブラリ化して公開するようにしています。Githubのレポジトリ上でIssueやPull Requestをいつでもお待ちしているので気軽に意見をお聞かせ下さい。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/