87

In these days, I am trying to write some codes to experience the Spring reactive features and kotlin extension in Spring 5, and I also prepared a gradle Kotlin DSL build.gradle.kt to configure the gradle build.

The build.gradle.kt is converted from Spring Boot template codes generated by http://start.spring.io.

But the ext in the buildscript can not be detected by Gradle.

buildscript {
  ext { }
}

The ext will cause Gradle build error.

To make the variables in classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") and compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlinVersion") work, I added the variables in the hard way.

val kotlinVersion = "1.1.4"
val springBootVersion = "2.0.0.M3"

But I have to declare them in global top location and duplicate them in the buildscript.

Code: https://github.com/hantsy/spring-reactive-sample/blob/master/kotlin-gradle/build.gradle.kts

Is there a graceful approach to make ext work?

Update: There are some ugly approaches:

  1. From Gradle Kotlin DSL example, https://github.com/gradle/kotlin-dsl/tree/master/samples/project-properties, declares the properties in gradel.properties.

     kotlinVersion = 1.1.4
     springBootVersion = 2.0.0.M3
    

    And use it in build.gradle.kts.

     buildScript{
        val kotlinVersion by project
    
     }
      val kotlinVersion by project //another declare out of buildscript block.
    
  2. Similar with above declare them in buildScript block:

     buildScript{
        extra["kotlinVersion"] = "1.1.4"
        extra["springBootVersion"] = "2.0.0.M3"
        val kotlinVersion: String by extra
    
     }
      val kotlinVersion: String by extra//another declare out of buildscript block.
    

How can I avoid the duplication of val kotlinVersion: String by extra?

Update(2023-7-5): The latest Gradle supports libs.versions.toml file to declare dependency versions in a central place, it is also can be recognized by GitHub dependabot. see: https://docs.gradle.org/current/userguide/platforms.html

Hantsy
  • 8,006
  • 7
  • 64
  • 109
  • 1
    I don't know how you can avoid the duplication, but you can join: `extra["kotlinVersion"] = "1.1.4" val kotlinVersion: String by extra` to `val kotlinVersion: String by extra("1.1.4")` – Claus Holst Dec 02 '17 at 21:22
  • @ClausHolst Excellent clarification! – neu242 Oct 29 '19 at 08:21

11 Answers11

53

With Kotlin DSL ext has been changed to extra and it can be used under buildscript.

Eg :-

buildscript {
    // Define versions in a single place
    extra.apply{
        set("minSdkVersion", 26)
        set("targetSdkVersion", 27)
    }
}
Shashi Bhushan
  • 1,292
  • 11
  • 15
  • 2
    How would this work if I wanted to add a function to the extras object? – Squeazer Apr 23 '19 at 13:53
  • 4
    Can you add an example on how to use the property? Simply `implementation("org.library:library:$myVersion")` doesn't work. – Jorn Feb 21 '20 at 09:26
  • 2
    Unfortunately, it doesn't work for me in buildscript. – Sir Codesalot Jul 07 '20 at 07:08
  • 2
    Reading extra properties: `val myVersion: String by rootProject.extra` `implementation("org.library:library:$myVersion")` – sylwano May 31 '21 at 16:40
  • 1
    This is no way easier than using groovy, even when it does work... – Chapz Jun 16 '21 at 21:02
  • But it's not just about constants of deps. What about shared configuration between modules? previously we could include `config_module.gradle` with `ext { android { ... } }` to each `build.gradle`. How can do it now? – user924 Jul 15 '22 at 10:54
  • Google uses this way for deps constants https://github.com/google/iosched/tree/main/buildSrc/src/main/java – user924 Jul 15 '22 at 10:54
  • Example for usage of 'extra' vars: `id("org.jetbrains.kotlin.jvm") version extra.get("minSdkVersion") as String` – Dimitar II Nov 19 '22 at 14:44
30

It is possible to use constants defined in .kt file in .gradle.kts files.

create buildSrc folder in root folder of your project

create buildSrc/build.gradle.kts file with the following content

plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}

create file buildSrc/src/main/kotlin/Constants.kt with the following content

object Constants {
    const val kotlinVersion = "1.3.70"
    const val targetSdkVersion = 28
}

Synchronize. Now you may reference created constants in various .gradle.kts files like this

...
classpath(kotlin("gradle-plugin", version = Constants.kotlinVersion))
...

...
targetSdkVersion(Constants.targetSdkVersion)
...
Eugene Popovich
  • 3,343
  • 2
  • 30
  • 33
  • This works flawlessly, even for multi-module projects, and doesn't require ugly code in the buildscripts. Awesome! – Thorbear Oct 01 '20 at 00:30
  • Love this solution, thanks! Do you have any pointers on how to share buildSrc as an artifact so it can be reused between independent projects? Just a link to some doc would be fine, I'm just not sure where to start looking – Emil Kantis Jan 12 '21 at 11:38
  • @EmilKantis, sorry, don't know such solution. Try to search/ask here at StackOverflow – Eugene Popovich Jan 12 '21 at 12:03
  • 1
    @EmilKantis buildSrc essentially works like a Gradle plugin that automatically gets applied to all the subprojects, so what you are looking to do is create a Gradle plugin. :) – vaughandroid May 14 '21 at 12:54
  • 1
    The biggest problem I have with this is that the versions defined by `ext` in older iteration gradle files would work with the lint checker for new version libraries in e.g. Android Studio, but it does not work with these constants files, so you don't know if something has a newer version available (if you wanted to know) – Meetarp Aug 20 '21 at 16:09
  • But it's not just about constants of deps. What about shared configuration between modules? previously we could include `config_module.gradle` with `ext { android { ... } }` to each `build.gradle`. How can do it now? – user924 Jul 15 '22 at 10:54
  • @user924 use precompilled builds scripts https://proandroiddev.com/sharing-build-logic-with-kotlin-dsl-203274f73013 – Eugene Popovich Jul 15 '22 at 11:18
  • @EugenePopovich they require to set `"com.android.application"` or `"com.android.library"` to it could see `android` scope, which is not good, because previously I had one `common_config.gradle` for all modules (doesn't matter if it is app or library) – user924 Jul 15 '22 at 11:47
  • @EugenePopovich even with adding app or library plugin it won't compile such script `Unresolved reference: android` – user924 Jul 15 '22 at 11:58
22

What is working for me is using ext in allprojects instead of buildscript, so in your top-level build.gradle.kts

allprojects {
  ext {
    set("supportLibraryVersion", "26.0.1")
  }
}

then you can use it in build.gradle.kts files in modules like this:

val supportLibraryVersion = ext.get("supportLibraryVersion") as String
kamil zych
  • 1,682
  • 13
  • 21
20

None of these answers felt clear to me. So here's my explanation:

/build.gradle.kts:

buildscript {
    extra.apply {
        set("compose_version", "1.0.3")
    }
    ...
}

/app/build.gradle.kts:

val composeVersion = rootProject.extra["compose_version"]
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
Blundell
  • 75,855
  • 30
  • 208
  • 233
13

There is a new possibility with Kotlin we can use:

object DependencyVersions {
    const val JETTY_VERSION = "9.4.12.v20180830"
}

dependencies{
    implementation("org.eclipse.jetty:jettyserver:${DependencyVersions.JETTY_VERSION}")
}

Here, DependencyVersions is a name I chose. You can choose another name, like "MyProjectVariables". This is a way to avoid using the extra or ext properties.

Per Christian Henden
  • 1,514
  • 1
  • 16
  • 26
6

Global properties in kotlin-gradle-dsl:
https://stackoverflow.com/a/53594357/3557894


Kotlin version is embedded into kotlin-gradle-dsl.
You can use dependecies with embedded version as follows:

implementation(embeddedKotlin("stdlib-jdk7"))

classpath(embeddedKotlin("gradle-plugin"))
nuamehas
  • 614
  • 9
  • 14
  • 1
    the newer way is to add to plugins: `kotlin("jvm")` and to deps: `implementation(kotlin("stdlib-jdk8")` – Matt Kerr Jan 09 '20 at 00:06
3
val junitVersion by extra("4.13.2")
testImplementation("junit:junit:$junitVersion")
顾小兔
  • 31
  • 1
2

In Kotlin, the way to do this is with by extra or an ext block.

With by extra:

val kotlinVersion = "95" by extra
val kotlinCompiler = true by extra

With ext:

ext {
    set("kotlinVersion", "95")
    set("kotlinCompiler", true)
}
solidsnack
  • 1,631
  • 16
  • 26
2

Between, from latest code style above will not work, only with some bits and pieces. However, added a plugins block if someone needs a way to handle the same in the .kts file system.

/build.gradle.kts:

buildscript {
    extra.apply {
        set("compose_ui_version", "1.2.0")
    }
}// Top-level build file where you can add configuration options common to all sub-projects/modules.

plugins {
    id("com.android.application") version "7.4.0" apply false
    id("com.android.library") version "7.4.0" apply false
    id("org.jetbrains.kotlin.android") version "1.7.0" apply false
}

/app/build.gradle.kts:

dependencies {
    val composeUiVersion = rootProject.extra["compose_ui_version"]
    implementation("androidx.compose.ui:ui:$composeUiVersion")
    implementation("androidx.compose.ui:ui-tooling-preview:$composeUiVersion")
    androidTestImplementation ("androidx.compose.ui:ui-test-junit4:$composeUiVersion")
    debugImplementation ("androidx.compose.ui:ui-tooling:$composeUiVersion")
    debugImplementation ("androidx.compose.ui:ui-test-manifest:$composeUiVersion")
}
Min2
  • 10,751
  • 2
  • 19
  • 22
1

Set it like this:

val kotlinVersion by extra("1.1.4")

Use it like this:

val kotlinVersion: String by rootProject.extra
Eva
  • 4,397
  • 5
  • 43
  • 65
0

It's a possibility to define global properties within gradle.properties:

xyzVersion=1.0.0

And then use them in your module's build.gradle.kts:

val xyzVersion: String by project
sergiosua
  • 364
  • 2
  • 4
  • You could also just skip the additional variable declaration and just access the variable directly in your dependency: `implementation("io.ktor:ktor-server-core:${project.ext["io_ktor_version"]}")` – eekboom Apr 15 '23 at 10:45