Need to understand this Kotlin code in buildSrc Gradle project

Viewed 411

I'm a newbie in Kotlin and I'm currently learning how to integrate Gradle with Kotlin. Looking up in the internet I saw this code.

import org.gradle.api.Project

import org.gradle.kotlin.dsl.*

/**
 * Configures the current project as a Kotlin project by adding the Kotlin `stdlib` as a dependency.
 */
fun Project.kotlinProject() {
    dependencies {
        "compile"(kotlin("stdlib"))
    }
}

This is saved as buildSrc/src/main/kotlin/Dependencies.kt. Because Kotlin has so many syntactic sugars I have a bit of a trouble understanding that piece of code. The "compile"() part, that is an invoke() convention? Can anyone provide an explanation for this code and the Gradle buildSrc, as to why the code looks like that?

Here is the buildSrc/build.gradle.kts file

plugins {
    `kotlin-dsl`
}

repositories {
    jcenter()
}

Here is another build.gradle.kts in the root directory

plugins {
    base
    kotlin("jvm") version "1.3.72" apply false
}

allprojects {

    group = "com.example.test"

    version = "1.0"

    repositories {
        jcenter()
    }
}

dependencies {
    // Make the root project archives configuration depend on every sub-project
//    subprojects.forEach {
//        archives(it)
//    }
}```
2 Answers

Yep they are actually operator invoke. When you do () on a string kotlin calls the operator fun String.invoke() defined in that context.

Example are better than words:

// top level context
operator fun String.invoke(a: String, b: Int, c: () -> Unit) {
    println(this)
    println(a)
    println(b)
    c()
}

// call it like this
"test"("parameter a", 27) { println("parameter c") }

// Results:
// test
// parameter a
// 27
// parameter c

And here is the the code which does the same but inside a receiver, i.e. instead of top-level function it calls the invoke function defined inside of the class. Play with the code yourself

Here are more examples from kotlin docs

You may want to start with reading https://docs.gradle.org/current/userguide/kotlin_dsl.html.

In particular, it links to the Kotlin DSL API docs and says org.gradle.kotlin.dsl.* and org.gradle.kotlin.dsl.plugins.dsl.* are automatically imported (so that import line can probably be removed). Looking at them, we can indeed find invoke operators. This one is inside dependencies { ... }, so we can look at DependencyHandlerScope:

operator fun String.invoke(dependencyNotation: Any): Dependency?
operator fun String.invoke(dependencyNotation: String, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
operator fun String.invoke(group: String, name: String, version: String? = null, configuration: String? = null, classifier: String? = null, ext: String? = null): ExternalModuleDependency
operator fun String.invoke(group: String, name: String, version: String? = null, configuration: String? = null, classifier: String? = null, ext: String? = null, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
operator fun <T : ModuleDependency> String.invoke(dependency: T, dependencyConfiguration: T.() -> Unit): T
operator fun Configuration.invoke(dependencyNotation: Any): Dependency?
operator fun Configuration.invoke(dependencyNotation: String, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
operator fun Configuration.invoke(group: String, name: String, version: String? = null, configuration: String? = null, classifier: String? = null, ext: String? = null): ExternalModuleDependency
operator fun Configuration.invoke(group: String, name: String, version: String? = null, configuration: String? = null, classifier: String? = null, ext: String? = null, dependencyConfiguration: ExternalModuleDependency.() -> Unit): ExternalModuleDependency
operator fun <T : ModuleDependency> Configuration.invoke(dependency: T, dependencyConfiguration: T.() -> Unit): T
operator fun invoke(configuration: DependencyHandlerScope.() -> Unit): Unit

and the first one is clearly applicable.

Unfortunately, there is an index but it lists only types and not functions, there doesn't seem to be a way to see e.g. all invoke methods in Kotlin DSL :(

Looking at the code in IDEA (or Android Studio) should show you the types involved, which will be helpful.

Related