Gradle:プラグインを作り、既存プラグインの動作を変更する
多数のプロジェクトで同じようなbuild.gradleがあるとき、その処理を共通化したい。これを行うには、既存のプラグインの動作を変更しなければならない。例えば、Gradleのコンベンションに従わない以下のような状況があるとする。
apply plugin: 'java'
sourceSets {
main {
java {
srcDir 'src_a'
srcDir 'src_b'
srcDir 'src_c'
}
resources {
srcDir 'src_a'
srcDir 'src_b'
srcDir 'src_c'
}
}
}
これではいかにも面倒だ。ここでは最終的に次のように記述できることを見ていく。
apply from 'sample.gradle'
apply plugin: JavaExPlugin
sourceSetsEx {
main 'src_a', 'src_b', 'src_c'
}
ここで、apply fromされるsample.gradleはすべてのbuild.gradleの共通処理を入れるものとする。
プラグインの作成
まず初めにはプラグインを作成してみる。これは簡単だ。
sample.gradle
ext.JavaExPlugin = JavaExPlugin
class JavaExPlugin implements Plugin<Project> {
def void apply(Project project) {
project.apply(plugin:'java')
}
}
build.gradle
apply from: 'sample.gradle'
apply plugin: JavaExPlugin
sourceSets {
main {
java {
srcDir 'src_a'
srcDir 'src_b'
srcDir 'src_c'
}
resources {
srcDir 'src_a'
srcDir 'src_b'
srcDir 'src_c'
}
}
}
sample.gradleの中でJavaExPluginを定義し、そこで’java’プラグインを呼びだす。だから、build.gradleの方では、sample.gradleの取り込みと、JavaExPluginの呼び出しだけでよい。’java’プラグインが呼び出されるので、普通にsourceSetsを使うことができる。
※特に注意「ext.JavaExPlugin = JavaExPlugin」がないとbuild.gradleからJavaExPluginを参照できなくなる。これはスコープの問題だ。
プロジェクトを拡張する
やりたいことは、以下のように簡単に記述することだ。
sourceSetsEx {
main 'src_a', 'src_b', 'src_c'
}
このためには、プロジェクトに拡張を行ってやる。javaExPluginを以下に変更する。
class JavaExPlugin implements Plugin<Project> {
def void apply(Project project) {
project.apply(plugin:'java')
// プロジェクトに"sourceSetsEx"という名前の拡張を追加する
project.extensions.create("sourceSetsEx", SourceSetsEx, project)
}
}
その上でSourceSetsExというクラスを記述してやるのだが、最終的なsample.gradleは次のようになる。
ext.JavaExPlugin = JavaExPlugin
class JavaExPlugin implements Plugin<Project> {
def void apply(Project project) {
project.apply(plugin:'java')
project.extensions.create("sourceSetsEx", SourceSetsEx, project)
}
}
class SourceSetsEx {
Project project
SourceSetsEx(Project project) {
this.project = project
}
def methodMissing(String name, args) {
String s = "x.sourceSets { ${name} {"
s += "java {";
args.each {
s += "srcDir('${it}');"
}
s += "}"
s += "resources{"
args.each {
s += "srcDir('${it}');"
}
s += "}" + "}}"
println s
Eval.x(project, s)
}
}
build.gradleは以下だ
apply from: 'sample.gradle'
apply plugin: JavaExPlugin
sourceSetsEx {
main 'src_a', 'src_b', 'src_c'
}
SourceSetsExにおいてmethodMissingで処理している理由は、元々のsourceSetsに記述されるものがmainだけではなく、任意の名称がかけてしまうからだ。つまり、
sourceSetsEx {
server 'src_a'
terminal 'src_b', 'src_c'
}
などといきなり書けてしまったりする。これに対処するため、すべてを「不明なメソッド」として扱うようにしている。
また、Eval.xによって作成した文字列をコードとして評価するのだが、ここでは実行する文字列の他にprojectオブジェクトを与えなければならないので、Eval.meではなく、Eval.xを使用している。
mainだけのサポートの場合
mainしかサポートしない場合は簡単になる。
sample.gradle
ext.JavaExPlugin = JavaExPlugin
class JavaExPlugin implements Plugin<Project> {
def void apply(Project project) {
project.apply(plugin:'java')
project.extensions.create("sourceSetsEx", SourceSetsEx, project)
}
}
class SourceSetsEx {
Project project
SourceSetsEx(Project project) {
this.project = project
}
def main(String...args) {
args.each{
project.sourceSets.main.java.srcDir(it)
project.sourceSets.main.resources.srcDir(it)
}
}
}