mike-neckのブログ

Java or Groovy or Swift or Golang

gradle2.4のRule based model configurationがユーザーにとって地味に嬉しい

gradle2.4のリリースノートを先日眺めていたらRule based model configuration improvementsという記述があって、そのユーザーガイドのコードを試してみました。


モデル

モデル(POJO)をあらわすインターフェース。Managedアノテーションを付与することによってモデルであることをあらわす。このアノテーションが付与できるのはインターフェースかabstractクラスでないといけないらしい。というのも、gradleが場面場面でオブジェクトの可変性・不可変性を管理しているからのようです。

package sample;

import org.gradle.model.Managed;

@Managed
public interface SampleModel {

    void setFrom(String from);
    String getFrom();

    void setInto(String into);
    String getInto();
}

POJOなのでsettergetterだけを記述します。プロパティとして用いることができるのは次の型だけのようです。

  • String
  • Boolean
  • Character
  • Integer
  • Long
  • Double
  • BigInteger
  • BigDecimal

なお、サポートしているコレクションはManagedSetのみのようです。

ルール

モデルをタスクに反映させるクラス。

package sample

import org.gradle.api.Task
import org.gradle.model.Defaults
import org.gradle.model.Model
import org.gradle.model.Mutate
import org.gradle.model.RuleSource
import org.gradle.model.collection.CollectionBuilder

class SampleRule extends RuleSource {

    @Model
    void sampleModel(SampleModel sm) {
        println "@Model method[from: ${sm.from}, into: ${sm.into}]"
    }

    @Defaults
    void defaultsMethod(SampleModel sm) {
        println "@Defaults method[from: ${sm.from}, into: ${sm.into}]"
    }

    @Mutate
    void mutateMethod(SampleModel sm) {
        println "@Mutate method[from: ${sm.from}, into: ${sm.into}]"
    }

    @Mutate
    void create(CollectionBuilder<Task> tasks, SampleModel sm) {
        println "@Mutate create[from: ${sm.from}, into: ${sm.into}]"
        tasks.create('sampleRule') {
            group = 'sample-rule'
            doLast {
                println "from [${sm.from}] into [${sm.into}]"
            }
        }
    }

    @Finalize
    void finalizeMethod(SampleModel sm) {
        println "@Finalize method[from: ${sm.from}, into: ${sm.into}]"
    }

    @Validate
    void validateMethod(SampleModel sm) {
        println "@Validate method[from: ${sm.from}, into: ${sm.into}]"
    }
}

いくつかの制約があります。

ルールクラスは状態を持たないように実装すればよいというだけの話ですね。

プラグイン

ルールは通常のPluginインターフェースを使った場合と同じ方法でプラグインとして登録することが可能です。

src/main/resources/META-INF/gradle-plugins/sample-rule.properties

implementation-class=sample.SampleRule

DSL

プラグインのモデルにアクセスするためにmodel{}ブロックを用います。なお、元々model {}ブロックはネイティブビルド用にあったものです。

apply plugin: 'sample-rule'

model {
    sampleModel {
        from = 'here'
        into = 'there'
    }
}

modelのブロックの次のブロックを形成する部分の名前はルールクラスで@Modelアノテーションを付与したメソッドの名前になります。上記の例ではルールクラスで@ModelアノテーションsampleModelメソッドに付与していますので、DSLsampleModelとなります。

sampleModelのブロックではモデルに与える値を設定できます。GroovyではPOJOsetter呼び出しを=で記述できるので、上記のようにfrom = 'here'のように記述しています。

実行

ルールクラスでsampleRuleという名前でタスクを追加していますので、sampleRuleというタスクで起動することができます。

$ gradle sampleRule
:buildSrc:compileJava
:buildSrc:compileGroovy
:buildSrc:processResources
:buildSrc:classes
:buildSrc:jar
:buildSrc:assemble
:buildSrc:compileTestJava
:buildSrc:compileTestGroovy
:buildSrc:processTestResources
:buildSrc:testClasses
:buildSrc:test
:buildSrc:check
:buildSrc:build
@Defaults method[from: null, into: null]
@Model method[from: null, into: null]
@Mutate method[from: null, into: null]
@Finalize method[from: here, into: there]
@Validate method[from: here, into: there]
@Mutate create[from: here, into: there]
:sampleRule
from [here] into [there]

BUILD SUCCESSFUL

Total time: 6.357 secs

This build could be faster, please consider using the Gradle Daemon: http://gradle.org/docs/2.4/userguide/gradle_daemon.html

Defaultsドキュメントと異なってDefaultsの方が先に実行されていますね…多分ドキュメントの間違いでしょう…

なお、Rule based model configurationの改善にともなって、modelというタスクが追加されています。このタスクを使うと、どのような値が設定できるのかを見ることができます。

$ gradle model
…一部省略…
------------------------------------------------------------
Root project
------------------------------------------------------------

model
    sampleModel
        from
        into
    tasks
        assemble
        build
        check
        clean
        components
        dependencies
        dependencyInsight
        help
        init
        model
        projects
        properties
        sampleRule
        tasks
        wrapper

今回作成したプラグインで設定できる名前が表示されているのがわかると思います。プラグインを使うときにドキュメント化されていなくて、どう設定するべきかわからない設定項目があったりしますが、Rule based model configuration形式でプラグインを実装すれば、ユーザーにとって何が設定できるのかがわかりやすくなるのがよいですね。


まとめ

  • ユーザーにわかりやすいプラグインが作れるようになる
  • incubatingな機能なので、この後のgradleでも使えるかどうかはわからない

なお、この3日間、人にあってないので日本語がおかしいです。多分。