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なのでsetter
とgetter
だけを記述します。プロパティとして用いることができるのは次の型だけのようです。
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}]" } }
いくつかの制約があります。
- メソッドには次のアノテーションのいずれかを付与しないと実行されない
@Model
@Defaults
@Mutate
@Finalize
@Validate
- メソッドは上記のアノテーションの上から順番に実行される
- メソッドはスタティックメソッドでもインスタンスメソッドでも構わない
@Model
が付与されたメソッドを除いて、戻り値はvoid
でなければならない。かつ、必ずパラメーターを持たなければならない@Model
が付与されたメソッドは対象のオブジェクト(@Managed
が付与されたクラスのインスタンス)を返すか、あるいはそのオブジェクトをパラメーターとして取らなければならない- コンストラクターは作ってはいけない
- 多段継承してはならない(
RuleSource
を直接継承したクラスでなければならない) - インスタンス変数を持ってはならない
static final
である定数は持っても良い
- メソッドのオーバーロードをしてはならない
- タイプパラメーターを使ってはいけない(クラスに対しても、メソッドに対しても)
ルールクラスは状態を持たないように実装すればよいというだけの話ですね。
プラグイン化
ルールは通常の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
メソッドに付与していますので、DSLはsampleModel
となります。
sampleModel
のブロックではモデルに与える値を設定できます。GroovyではPOJOのsetter
呼び出しを=
で記述できるので、上記のように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日間、人にあってないので日本語がおかしいです。多分。