今日から始めるGradleプラグイン開発 #gadvent
G*Advent Calendar(Groovy,Grails,Gradle,Spock...) Advent Calendar 2014 - Qiitaの17日目です。
本記事では、9日目の記事で紹介したGradleプラグインのテンプレートプロジェクトを使って、新しいGradleプラグインを作る方法を説明します。14日目の記事で紹介したSlashプラグインを題材にして、実際にGradleプラグインを作っていきます。
準備
Gradleプラグインを開発するにはJVMが必要です。IDEはなくても開発できますが、あった方が便利です。ここでは、IntelliJ IDEAを使う前提とします。
適当なフォルダにGradleプラグインのテンプレートプロジェクトを配置します。
git clone https://github.com/int128/gradle-plugin-blank.git gradle-slash
cd gradle-slash
git remote remove origin
まずは、コマンドラインでビルドしてみましょう。テンプレートプロジェクトにはGradle wrapperを同梱しているので、Gradleをインストールする必要はありません。以下のコマンドでビルドを実行できます。
./gradlew build
BUILD SUCCESSFULと表示されれば成功です。
次に、IDEAでプロジェクトを開いてみましょう。最初の画面でImportを選び、build.gradleを開きます。com.exampleのパッケージツリーが表示されれば成功です。
プラグインの名付け
GradleプラグインにはユニークなIDを付ける必要があります。プラグインIDは、ビルドスクリプトで apply plugin
でプラグインを適用する際に指定するIDになります。Javaのパッケージ規則と同様に、自分が所有しているドメインやGitHubアカウントにするとよいでしょう。詳細は、公式の how to submit your plugin を参照してください。
プラグインIDを決めたら gradle.properties
にある以下の項目を修正します。
gradle.plugin.id
- プラグインID。例:org.hidetake.slash
group
- グループ名。例:org.hidetake
description
- プラグインの説明。
ここで、プラグインコードを自動生成します。テンプレートプロジェクトには、プラグインコードを自動生成するスクリプトを同梱しています。以下のように generatePlugin
タスクを実行します。
./gradlew generatePlugin :generatePluginClass Generating plugin class: .../src/main/groovy/org/hidetake/SlashPlugin.groovy :generatePluginMetadata Generating plugin metadata: .../src/main/resources/META-INF/gradle-plugins/org.hidetake.slash.properties :generatePluginTestClass Generating plugin test: .../src/test/groovy/org/hidetake/SlashPluginSpec.groovy :generatePlugin BUILD SUCCESSFUL
テンプレートプロジェクトにはあらかじめHelloPluginというサンプルが入っているので、削除しておきます。
プラグインの開発
プラグインの開発は以下のステップで行うと捗ります。いわゆるREADME駆動開発です。
- READMEを書く
- 受け入れテストを書く
- Spockのテストコードを書く
- プロダクトコードを書く
1. READMEを書く
プラグインの特長を README.md
に書きます。仕様を詳細に書く必要はありません。ユーザはなぜこのプラグインを使いたくなるのか?どんな嬉しいことがあるのか?という視点でREADMEを書きます。
最初にREADMEを書くことで、ユーザ視点で仕様を考えるための準備が整います。たかがGradleプラグインのためにと思うかもしれませんが、ユーザに価値あるプラグインを提供するためにREADMEは重要な役割を果たすのです。
2. 受け入れテストを書く
ここでは、ユーザがGradle上で実際にプラグインを適用した場合の受け入れ条件のチェックを「受け入れテスト」と呼ぶことにします。受け入れテストですべての仕様を網羅する必要はありません。どちらかというとプラグインの使い方を説明する目的で記述するとよいでしょう。
受け入れテストには、ユーザ視点でプラグインの使い方を考える目的に加えて、実際にGradleでプラグインを適用してみないと分からない問題に対処する目的があります。後述するSpockのテストコードにはimport文や変数宣言があるため、実際のビルドスクリプトと動作条件が異なる場合があります。また、他のプラグインと組み合わせた場合の挙動は実際にビルドスクリプトを実行してみないと分かりません。
受け入れテストのコードは acceptance-test/build.gradle
に記述します。
Slashプラグインでは以下を受け入れテストとしています。これは単純すぎるのであまりありがたみを感じませんが、Gradle SSH Pluginではもっと複雑な受け入れテストを行っています。
apply plugin: 'org.hidetake.slash' task test << { assert buildDir / 'tmp' == file("$buildDir/tmp") assert projectDir / 'build' == buildDir assert projectDir / 'build' / 'generated' == buildDir / 'generated' }
受け入れテストは以下のコマンドで実行します。具体的には、ローカルのMavenリポジトリにSNAPSHOTバージョンのプラグインをインストールした上で、受け入れテストプロジェクトを実行します。
./gradlew install ./gradlew -p acceptance-test test
なお、gradlewに -Pversion=0.1
のようにバージョンを渡して実行することで、リリース済みのプラグインに対するテストも実行できます。
3. テストコードを書く
Spockでテストコードを書きます。テストコードは src/test/groovy
に配置します。ここでは、プラグインの仕様テストと、プラグインが依存するドメインクラスの仕様テストを記述します。
典型的なテストコードは、プロジェクトにプラグインを適用して、実行に対する事後条件をチェックする流れです。以下に例を示します。
def "apply() should load the plugin"() { given: def project = ProjectBuilder.builder().build() when: project.with { apply plugin: 'com.example.hello' } then: project.plugins.hasPlugin(HelloPlugin) }
なお、プラグインが単純な場合は、先ほどの受け入れテストを省略して、Spockのテストケースだけにしても構いません。
4. プロダクトコードを書く
プロダクトコードはGroovyやJavaで記述します。Gradle API自体はJavaで書かれているのでプラグインもJavaで書けますが、開発効率を考えるとGroovyがおすすめです。
プラグインのエントリポイントは src/main/resources/META-INF/gradle-plugins/*.properties
に書いてあるクラスです。例えば、Slashプラグインの場合は以下の動作になります。
- ビルドスクリプトで
apply plugin: 'org.hidetake.slash'
が実行される - プラグインJARを検索して
META-INF/gradle-plugins/org.hidetake.slash.properties
を探す META-INF/gradle-plugins/org.hidetake.slash.properties
に書かれているクラスをロードする- 当該クラスをインスタンス化して、
apply
メソッドを実行する
Slashプラグインの中心部分を抜き出すと以下になります。ここではFileクラスのメタクラスを操作して除算演算子を追加しています。
class SlashPlugin implements Plugin<Project> { @Override void apply(Project project) { File.metaClass.mixin(PathCategory) } } @Category(File) class PathCategory { File div(String child) { new File(this as File, child) } }
プラグインでは、プロジェクトに新しいプロパティを追加したり、ビルド前後のフックを追加したりできます。ただし、プロジェクトを無秩序に拡張することはグローバル汚染につながります。このため、プロジェクトを拡張するには project.conventions
ではなく project.extensions
を使うことが推奨されています。
プラグインのコードを書くにあたっては、Gradle DSLのドキュメントが役に立ちます。また、Gradle Plugin Portalで公開されているプラグインのコードが参考になります。
これまで述べた一連の流れを繰り返すことで、プラグインを開発していきます。
プラグインの公開
プラグインを他の人に使ってもらうには、Maven CentralやBintrayなどのリポジトリにリリースする必要があります。プラグインはどこにリリースしてもよいのですが、Gradle Plugin Portalに掲載するにはBintrayにリリースする必要があります。本記事ではBintrayにリリースする前提で話を進めます。
まず、Bintrayにサインアップします。GitHubアカウントで認証できます。
ここでは、初期状態で用意されているmavenリポジトリの下に新しいパッケージを作成します。パッケージ名はGradleのプロジェクト名と同じにします。
最後にAPIキーを取得します。APIキーはユーザ設定のページから取得できます。
ローカルマシンからBintrayにリリースする
ユーザ名とAPIキーをコロンで連結した文字列をローカルマシンの ~/.gradle/gradle.properties
に書きます。
bintray.credential=user:apikey
その後、Bintrayにアップロードするタスクを実行します。
./gradlew -Pversion=0.1 bintrayUpload
Bintrayにアクセスしてアップロードされたファイルを確認します。問題なければ Publish をクリックして公開します。なお、ビルドスクリプトの設定を変更すれば Publish の操作を省略することも可能です。
Travis CIからBintrayにリリースする
プロダクトをリリースする際は、コミットにタグを付けることが多いと思います。タグを付けたタイミングで自動的にリリースが実行されると便利ですよね。そこで、GitHubにタグがpushされたタイミングでTravis CIがリリースを実行する仕組みを実現してみましょう。
Bintrayのユーザ名とAPIキーを連結した文字列を .travis.yml
に追記します。以下のコマンドで暗号化しておきます。
travis encrypt --add -- BINTRAY=user:apikey
.travis.yml
を変更したらコミットしてpushしておきましょう。これで準備は完了です。
vから始まるタグをリモートリポジトリにpushします。
git tag v0.1
git push origin --tags
Travis CIにアクセスしてログを確認します。ビルドの完了後、Bintrayにプラグインがアップロードされていれば成功です。
まとめ
Gradleプラグインのテンプレートプロジェクトを使ったGradleプラグインの作り方を駆け足で紹介しました。
Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築
- 作者: 綿引琢磨,須江信洋,林政利,今井勝信
- 出版社/メーカー: 翔泳社
- 発売日: 2014/11/05
- メディア: 大型本
- この商品を含むブログ (2件) を見る