こんな感じに書くとプロジェクトのrepositoriesを変更するが出来ます。
行儀が良さは一旦置いておきましょう。
class SamplePlugin : Plugin<Project> { override fun apply(project: Project) { project.repositories(closureOf<RepositoryHandler> { add(maven { repository -> repository.url = URI("https://maven.google.com") }) }) } }
背景
このネタを考えるきっかけはWantedlyさんのテックブログです。カッコいいですね。
記事を読んでもらうとわかるのですが、上記のエントリではkotlinの拡張関数を使ってwantedly()
を実現されています。つまりbuild.gradleをkotlinで書く制約があります。
残念なことに私が関わっているプロジェクの多くはgroovyで設定ファイルを書いています。そこでgroovy版のbuild.gradleではこういったおしゃれコードが書けないのかと調べてみるとそれっぽいレポジトリを見つけました。
このレポジトリのコードを読んでみるとプラグインのコードからmetaclass
というものを使っています。これはgroovyがメタプログラミング用に用意している仕組みです。動的にRepositoryHandlerにメソッドを生やすことで任意のレポジトリのエイリアスを作っているようでした。つまりbuild.gradleをkotlinで書いてしまうとみえないと思います。惜しいですが残念ですね。
RepositoryHandlerというのGradleの内部実装のクラスです。
Gradle実装のこの辺り読むとわかるんですが、RepositoryHandler interfaceに定義されてないものは基本的にrepositories
DSLの中には書けないんですよね。そういうことならプラグインの中で追加してしまえばbuild.gradleがgroovyだろうがkotlinだろうが解決できるなと思って試してみたら出来たという話でした。
大切な補足
実は冒頭のコードあれだけでは動かなくてclosureOfという拡張関数を生やす必要があります。closureOfの出典元はgradle/gradleです。
fun <T> Any.closureOf(action: T.() -> Unit): Closure<Any?> = KotlinClosure1(action, this, this) class KotlinClosure1<in T : Any?, V : Any>( val function: T.() -> V?, owner: Any? = null, thisObject: Any? = null ) : Closure<V?>(owner, thisObject) { @Suppress("unused") // to be called dynamically by Groovy fun doCall(it: T): V? = it.function() }