すきま風

勉強したことのメモとか

build.gradle.ktsに移行する! Tips編

以前記事にしたプロジェクトをbuild.gradleからkotlin-dslに移行します。
build.gradle.ktsにすることでスクリプト内でKotlinを使えるようになるし、IntelliJの補完が賢くなるし、groovyの文法をいちいちググらなくてよくなります。Spring Initializrでのデフォルトもktsになっていますし、やるしかねえぞこれ

で、気合で一気にやりました。変換結果はこちら

以下、自分用のメモとしてTipsを記載しておきます。

変換TIPS

kotlin plugins

// groovy
// plugins {
//     id 'org.jetbrains.kotlin.jvm' version '1.3.50'
//     id 'org.jetbrains.kotlin.plugin.spring' version '1.3.50'
//     id 'org.jetbrains.kotlin.plugin.jpa' version '1.3.50'
// }

// kotlin
plugins {
    kotlin("jvm") version "1.3.50"
    kotlin("plugin.spring") version "1.3.50"
    kotlin("plugin.jpa") version "1.3.50"
}

implementation

カッコで括ってあげる。StringはKotlinなのでダブルクォーテーション
testImplementationとかtestRuntimeOnlyとかも同じ感じ

//groovy
// implementation project(':domain')
// implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9'

// kotlin
implementation(project(":domain"))
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9")

apply

// groovy
// apply plugin: 'kotlin'
// apply from: 'test.gradle'

// kotlin
apply(plugin = "kotlin")
apply(from = "test.gradle.kts")

拡張プロパティ

拡張プロパティに値を設定する場合、以下のようにする。by extraでdelegationを使うのがkotlinっぽくてかっこいい

// groovy
// ext.versions = [
//     'springBoot': '2.2.0.M6'
// ]

// kotlin
extra["versions"] = mapOf(
    "springBoot" to "2.2.0.M6"
)

// または
val versions by extra {
    mapOf(
        "springBoot" to "2.2.0.M6"
    )
}

これで、.gradleのときと同様に、拡張プロパティとproject propertyに値がセットされる。

// 呼び出す
val versions: Map<String, String> by extra

// 拡張プロパティのscopeから外れた場合、projectからも取得可能
// val versions: Map<String, String> by project

implementation("org.springframework.boot:spring-boot-starter-webflux:${versions["springBoot"]}")

他ファイル定義の関数を起動する

kotlinの関数参照を利用します

// test.gradle.kts
fun test() = "my test method"

// 関数参照
val test: () -> String = ::test

// 関数参照を拡張プロパティにセット
val testFunc by extra {
    test
}

// build.gradle.kts
tasks.register("myTask") {
    // 他ファイルを読み込む
    apply(from = "test.gradle.kts")

    doFirst {
        val testFunc: () -> String by project
        println(testFunc()) // "my test method"
    }
}

taskを定義する

いろんな書き方ができて面白い

// tasks.registerを使う
tasks.register("myTask") {
    doFirst {
        println("myTask")
    }
}

// typeを指定する
tasks {
    withType<KotlinCompile> {
        kotlinOptions {
            freeCompilerArgs = listOf("-Xjsr305=strict")
            jvmTarget = "1.8"
        }
    }
}
// 個別定義もできる
tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}
// delegationも使える
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
    jvmTarget = "1.8"
}

val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
    jvmTarget = "1.8"
}
// こんな書き方もOK. classpathはconfigurationsで定義したktlintを指定してもok (下のTips参照)
task("ktlint", JavaExec::class) {
    group = "verification"
    description = "Check Kotlin code style."
    classpath = configurations.getByName("ktlint")
    main = "com.pinterest.ktlint.Main"
    args = listOf("adapters/**/*.kt", "application/**/*.kt", "configuration/**/*.kt", "domain/**/*.kt")
}
// tasks.registerでtypeも指定できる
tasks.register<JavaExec>("ktlint") {
    group = "verification"
    description = "Check Kotlin code style."
    classpath = configurations.getByName("ktlint")
    main = "com.pinterest.ktlint.Main"
    args = listOf("adapters/**/*.kt", "application/**/*.kt", "configuration/**/*.kt", "domain/**/*.kt")
}

taskにdependsOnを追加

// groovy
// task myNextTask(dependsOn: 'myTask') {
//     doFirst {
//         println('nextTask')
//     }
// }

// kotlin
tasks.register("myNextTask") {
    dependsOn("myTask")
    doFirst {
        println("nextTask")
    }
}

定義済みTaskに属性を追加

// check taskに依存を追加する
tasks.named("check") {
    dependsOn(ktlint)
}

// type指定
tasks.getByName<BootJar>("bootJar") {
    mainClassName = "com.example.demo.DemoApplicationKt"
}

configurationsに追加

configurations.creating をdelegate

// groovy
// configurations {
//     ktlint
// }

// dependencies {
//     ktlint "com.pinterest:ktlint:0.34.2"
// }

// kotlin
val ktlint: Configuration by configurations.creating

dependencies {
    ktlint("com.pinterest:ktlint:0.34.2")
}

project propertyの取得

// gradle.properties
foo=bar

// groovy
// def fooValue = hasProperty('foo') ? getProperty('foo') : ''

// kotlin
val fooValue = if (hasProperty("foo")) findProperty("foo") as String else ""

multi-projectでkotlinを適用

https://github.com/gradle/kotlin-dsl-samples/blob/master/samples/multi-kotlin-project-config-injection/build.gradle.kts 参照 configureEachを入れなくても設定されているようにも見えるのでちょっとわからん。

subproject {
    tasks {
        withType<KotlinCompile>().configureEach {
            kotlinOptions {
                freeCompilerArgs = listOf("-Xjsr305=strict")
                jvmTarget = "1.8"
            }
        }
    }
}

その他 (適当に思いついたら記載)

// configurationsの定義とか
// groovy
// configurations {
//     all*.exclude module: 'spring-boot-starter-logging'
// }

// kotlin
configurations {
    // allとかruntimeとかetc..
    all {
        exclude(module = "spring-boot-starter-logging")
    }
}

// project 呼び出しとか
// groovy
// def configuration = project("configuration")

// kotlin
val configuration = project("configuration")

まとめ

困ったら kotlin-dsl-samples を見ると答えが書いてあることが。あとIntelliJの補完が優秀なので、とりあえず候補を出すと解決します。
実際のKotlin codeIntelliJで読むと何しているのか結構わかったりすることがあります。