LoginSignup
8
9

More than 5 years have passed since last update.

kotlinとkotlin-dslでAndorid用GradlePluginを作成する

Last updated at Posted at 2018-08-08

kotlinとkotlin-dslでAndorid用GradlePluginを作成する

kotlinとkotlin-dslを利用し、Android向けのGradle Pluginを作成したときに調査した内容をメモです。

Gradle Pluginの作成がはじめての場合、下記ドキュメントを読むと全体像が把握できます。
Writing Custom Plugins - Gradle User Manual
DroidKaigi 2018でGradleプラグインを作って開発効率を改善しようって発表をした話 – Yuki Fujisaki / tnj – Medium

kotlinでの実装はkotlin-dslリポジトリにGradlePluginのサンプルがあったので、こちらを参考にしながら進めていきました。
kotlin-dsl/samples/gradle-plugin at master · gradle/kotlin-dsl · GitHub

kotlin-dslを使用できるようにする

build.gradle.ktsへのマイグレーション

下記を参考にしながら、既存のbuild.gradleをbuild.gradle.ktsにマイグレーションをしました。
Migrating build logic from Groovy to Kotlin

最終的にすべて書き換えましたが、最初はkotlin-dslにあったgroovy-interopサンプルを参考に、既存のbuild.gradleと併用しつつ徐々に書き換えていきました。
kotlin-dsl/samples/groovy-interop at master · gradle/kotlin-dsl · GitHub

build.gradle.kts
apply(from = "build.gradle")

また、settings.gradleに下記を追加することで、settings.gradle.ktsへの置き換えも後回しにできます。

settings.gradle
rootProject.buildFileName = 'build.gradle.kts'

Pluginの追加

build.gradle.ktsではいくつかpluginのエイリアスを用意しており、id指定なしで使用することができます。

build.gradle.kts
plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
    `maven-publish`
}

kotlin-dslを追加すると、build.gradle.kts以外のkotlinのコード上でもorg.gradle.kotlin.dslパッケージを使用することができるようになります。

Pluginの実装

Pluginクラスの作成

org.gradle.api.Pluginを継承したクラスを作成します。

class MyPlugin : Plugin<Project> {

    override fun apply(project: Project) {
    }
}

タスクの追加

タスクの作成は下記のように実装することができます。

class MyPlugin : Plugin<Project> {

    override fun apply(project: Project) {
        project.run {
            tasks.create("myTask").doLast {
                println("Hello, myTask")
            }
        }
    }
}

他にも数パターン定義の仕方があり、kotlin-dslリポジトリのtask-dependenciesサンプルのように定義することもできます。
kotlin-dsl/build.gradle.kts at master · gradle/kotlin-dsl · GitHub

import org.gradle.kotlin.dsl.invoke

class MyPlugin : Plugin<Project> {

    override fun apply(project: Project) {
        project.run {
            tasks {
                "myTask" {
                    doLast { println("Hello, myTask") }
                }
            }
        }
    }
}

tasksはorg.gradle.kotlin.dsl.invokeをimportすることで使えるようになります。

Androidのタスクにアクションを追加

例としてassemble後にアクションを実行するplugin作成します。
groovyでは下記のように実装することができます。

class MyPlugin implements Plugin<Project> {
    @Override
    void apply(Project project) {
        project.android.applicationVariants.all { variant ->
            variant.assemble.doLast {
              //do something
            }
        }
    }
}

kotlinでは下記のように記述しました。groovyで書くよりも少々冗長になってしまいました。
また、groovyとの差を少なくするためにextentionを作成しています。

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val android = project.extensions["android"] as AppExtension

        android.applicationVariants.all { variant ->
            variant.assemble.doLast {
                //do something
            }
        }
    }
}

fun <T> DomainObjectCollection<T>.all(action: T.(T) -> Unit) {
    all {
        action(this)
    }
}

詳細を見ていきます。

AppExtensionの取得

groovyコードproject.androidで取得できる値の実体はcom.android.build.gradle.AppExtensionです。
applicationVariantsをはじめ、Androidプロジェクトのbuild.gradleで使用するプロパティが定義されています。

kotlinではproject.extensions["extensionの名前"]で取得することができます。
AppExtensionの名前はandroidです。

また、AppExtensionのプロパティにアクセスできるようにas AppExtensionでキャストをします。

さらにAppExtensionはcom.android.build.gradleパッケージにあるため、dependenciesに追加してkotlinコードから参照できるようにします。

build.gradle.kts
dependencies {
    implementation("com.android.tools.build", "gradle", "X.X.X")
}

Variantの取得

Variant取得部分
android.applicationVariants.all { variant ->
    variant.assemble.doLast {
        //dosomething
    } 
  }
}  
extentionのみ抜粋
fun <T> DomainObjectCollection<T>.all(action: T.(T) -> Unit) {
    all {
        action(this)
    }
}

各Variantの取得にはapplicationVariants.allを使用します。
AppExtension#applicationVariants - Android Plugin DSL Reference

allはorg.gradle.api.DomainObjectCollectionに定義されており、引数にorg.gradle.api.Actionを受け取ります。

しかし、kotlinでは引数のActionがApplicationVariant.() -> Unitとして解釈され、lambdaで引数をもらうことができませんでした。
Kotlin not able to convert gradle's Action class to a lambda - Stack Overflow

allで完結する場合はthisが使用できますが、その後のdoLastの引数もActionなのでthisを区別する必要があります。
そのためApplicationVariant.(ApplicationVariant) -> Unitを受け取るallをextentionとして定義し、メソッドの中でもともと用意されていたallを呼び出しています。

上記のようにgroovyのコードをベースに実装を進めていくと、ところどころ修正が必要な箇所がでてきます。

リリース

java-gradle-pluginとmaven-publish pluginの設定をします。

java-gradle-pluginの設定

java_gradle_pluginを参考に設定する。

build.gradle.kts
gradlePlugin {
    (plugins) {
        "MyPlugin" {
            id = "my.plugin" //Plugin使用側でapply時に指定する値
            implementationClass = "my.plugin.MyPlugin" //Plugin実装クラス
        }
    }
}

maven-publish pluginの設定

Maven Publish Pluginを参考に設定します。
今回は自分でたてたMaven Repositoryにリリースしたかったため以下のように記述。

build.gradle.kts
publishing {
    repositories {
        maven(url = "http://my/repo") {
            credentials {
                username = "my_maven_user"
                password = "my_maven_pass"
            }
        }
    }
}

設定後はkotlin-dslを使用していないときと同様、 publishpublishToMavenLocalでリリースすることができます。
また、使用する側もkotlin-dslで作成されたpluginかを区別することなく使用することができます。

参考資料

gradle pluginの作り方ガイド
Writing Custom Plugins - Gradle User Manual

android用gradle pluginについて説明があり参考になりました。
DroidKaigi 2018でGradleプラグインを作って開発効率を改善しようって発表をした話 – Yuki Fujisaki / tnj – Medium

kotlin-dslのサンプル集。特にGradle Pluginのサンプルが参考になりました。
kotlin-dsl/samples at master · gradle/kotlin-dsl · GitHub

DroidconでのGradle Pluginについての発表資料。本記事では記述していないkotlinでExtensionやTaskクラスを定義する手順についても記載されています。
GitHub - StefMa/Gloc: A Gradle plugin which I used as an example at my talk at the Droidcon Italy 18

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9