Search code examples
kotlingradlegradle-plugingradle-kotlin-dsl

The same Gradle Kotlin `copy.from` api use `it` when called from build file (kt) and `this` when called from Pluign (kt)


I hope someone can explain this to me.

I was moving some code from a Gradle build file into a Gradle plugin. Below are two code snippes calling the same from function (based on Ideas indexing) I noticed that there are some strange differences between how the apis can be used in those two contexts.

I know that Gradle is adding some extra syntax suger around the build files which is why I need to manually cast the task in the Plugin.kt file, but I cannot find anything that explains why from in context of the Build file has this as context where in the plugin the function uses it to access the into function.

It is not just Idea that reports this, running Gradle also shows that it must be like this.

I assume this is something special to Kotlin's way of handling the Action interface in different contexts:

  • kts file (No wrapping class)
  • kt file (with class)

Here are the two samples

hostedStaticFiles is gradle configuration that will be used to configure web frontend from a separate build.

build.gradle.kts

tasks.getByName<ProcessResources>("processResources") {
  this.from(hostedStaticFiles) { 
    [email protected]("static") // <-- Note use of this here
  }
}

Plugin.kt

project.tasks.getByName("processResources").let<Task, ProcessResources> {
  if (it !is ProcessResources) {
    throw IllegalStateException("The processResources task in Project is not of type ${ProcessResources::class.java}")
  }
  it
}.apply {
  dependsOn(hostedStaticFiles)
  [email protected](hostedStaticFiles) { it -> // <-- Note use of it here and below
    it.into("static")
  }
}
dependencies {
  hostedStaticFiles(project("client"))
}

I hobe someone can point me to an explanation or preferably documentation on why this behaves this way :)

Gradle version 7.4.1

###################

After getting the answer from @Joffrey I updated my buildSrc/build.gradle.kts with the below plugin configuration and it all started working as expected.

plugins {
  `java-gradle-plugin`
  `kotlin-dsl`
}

Solution

  • Gradle uses the HasImplicitReceiver annotation on some function types (like Action), so you can use this instead of it. It leverages Kotlin's SAM-with-receiver compiler plugin.

    In Kotlin build scripts (.gradle.kts files) you benefit from this automatically because the Kotlin compiler used to compile your scripts is already properly configured. However, in custom plugin projects, you are in control of the build and you need to apply the kotlin-dsl plugin yourself. As mentioned in the documentation, it does a few things for you, including:

    • Configures the Kotlin compiler with the same settings that are used for Kotlin DSL scripts, ensuring consistency between your build logic and those scripts.