Search code examples
javagradlekotlingradle-kotlin-dsl

Include scripts with Gradle Kotlin DSL


I'm trying to start using Kotlin DSL with gradle in the project with the following restrictions:

  • Project has different modules (moreover: sometimes these modules use different plugins, however if two projects uses the same plugin then version of the plugins are the same).
  • Project has internal corporate repositories only (e.g. we don't use jcenter directly, we use proxy for it).

What we have with Groovy:

  • Some common configurations items are excluded to the separate scripts. Please check the example below.
  • Gradle modules include these files.

As a result (just based on my example):

  • We don't need to add the same code lines into the each module.
  • The most of projects have difference just with dependency list.

I tried to reproduce the same with Gralde KTS and received the following difficulties:

  • I'm unable to apply plugin in the include file and use it in the module. In this case I receive compilation error (because plugin types are not added into the module script).
  • I'm unable to extract constants to the something common to use them in the each scripts (root build.gradle.kts inclusive). With Groovy I can just use variable like springBootVersion, however with Kotlin Script I have to create the same property in the each module file.
  • Precompiled script plugins does not work without public repositories access (e.g. I'm unable to configure common script file with idea "just use default embedded Kotlin Script version, download all dependencies from these urls: ...".

Include file sample:

apply plugin: 'kotlin'

compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Gradle module sample:

apply from: "${rootDir}/gradle/include/kotlin-common-include.gradle"

dependencies {
    compile project(':my.project.libraries.common') 

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
}

Questions:

  • How can I put all common constants (such as dependency versions) to the separate file to include them just by using something like springBootVersion or Constants.springBootVersion with compile-time checks?
  • How can I extract plugin applying to the include scripts (to avoid Gradle module scripts overload)?
  • How can I use precompiled script plugins without public global repositories access?

Additional links:


Solution

  • There are limitations in Kotlin DSL currently (5.3) that prevents to have compile-time checks everywhere. In order for Kotlin DSL to work it has to add extensions on top of the Gradle API and it can't do it magically. First of all you need to go through Kotlin DSL Primer page to understand it.

    How can I extract plugin applying to the include scripts (to avoid Gradle module scripts overload)?

    The one way to do it is to use precompiled build scripts with Kotlin DSL Plugin. In order to do it you need to move your script into $rootDir/buildSrc project. Here how it might look like:

    // $rootDir/buildSrc/build.gradle.kts
    plugins {
      `kotlin-dsl`
    }
    repositories {
       maven {
         url = uri("http://host:8082/artifactory/...")
      }
    }
    

    Then put your common script like that:

    // $rootDir/buildSrc/src/main/kotlin/common.gradle.kts
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    
    plugins {
      kotlin("jvm") version "1.3.21"
    }
    tasks.compileKotlin {
      kotlinOptions.jvmTarget = "1.8"
    }
    tasks.compileTestKotlin {
      kotlinOptions.jvmTarget = "1.8"
    }
    

    Then you can apply this script as to a plugin like that:

    // $rootDir/build.gradle.kts
    subprojects {
      apply(id = "common")
    }
    

    How can I use precompiled script plugins without public global repositories access?

    Configuring custom repositories for pre-compiled scripts plugin is no different that your usual build script. Do it like that:

    // $rootDir/buildSrc/settings.gradle.kts
    pluginManagement {
      repositories.maven {
        url = uri("http://host:8082/artifactory/...")
      }
    }
    

    The other way around that if you don't want to use precompiled plugins is to configure extensions explicitly. You can do it like that:

    // $rootDir/gradle/common.gradle.kts
    import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
    tasks.withType<KotlinCompile> {
      kotlinOptions.jvmTarget = "1.8"
    }
    

    And in main script:

    // $rootDir/build.gradle.kts
    subprojects {
      apply {
        plugin(KotlinPlatformJvmPlugin::class)
        from("common.gradle.kts")
      }
    }
    

    How can I put all common constants (such as dependency versions) to the separate file to include them just by using something like springBootVersion or Constants.springBootVersion with compile-time checks?

    There is no good way to do it currently. You can use extra properties, but it won't guarantee compile time checks. Something like that:

    // $rootDir/dependencies.gradle.kts
    
    // this will try to take configuration from existing ones
    val compile by configurations
    val api by configurations
    dependencies {
      compile("commons-io:commons-io:1.2.3")
      api("some.dep")
    }
    
    // This will put your version into extra extension
    extra["springBootVersion"] = "1.2.3"
    

    And you can use it like this:

    // $rootDir/build.gradle.kts
    subprojects {
      apply {
        plugin<JavaLibraryPlugin>()
        from("$rootDir/dependencies.gradle.kts")
      }
    

    And in your module:

    // $rootDir/module/build.gradle.kts
    // This will take existing dependency from extra
    val springBootVersion: String by extra
    dependencies {
      compile("org.spring:boot:$springBootVersion")
    }