ext in buildscript can not be recognised by Gradle Kotlin DSL - gradle

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:
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.
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?

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)
}
}

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)
...

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

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")

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.

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"))

val junitVersion by extra("4.13.2")
testImplementation("junit:junit:$junitVersion")

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)
}

Set it like this:
val kotlinVersion by extra("1.1.4")
Use it like this:
val kotlinVersion: String by rootProject.extra

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

Related

How to use plugin version from gradle.properties in Gradle Kotlin DSL?

Now I use this way:
plugins {
val kotlinVersion: String by project
val springBootPluginVersion: String by project
val springDependencyManagementPluginVersion: String by project
id("org.jetbrains.kotlin.plugin.allopen") version kotlinVersion
id("org.jetbrains.kotlin.jvm") version kotlinVersion
id("org.springframework.boot") version springBootPluginVersion
id("io.spring.dependency-management") version springDependencyManagementPluginVersion
}
This variant compiles and works, but I don't know is this way right and why IntelliJ IDEA shows error on lines where placed versions definitions:
'val Build_gradle.project: Project' can't be called in this context by implicit receiver. Use the explicit one if necessary
(Cross-post: source)
Apparently this has become possible recently, if it wasn't possible in the past. (Almost) from the docs:
gradle.properties:
helloPluginVersion=1.0.0
settings.gradle.kts:
pluginManagement {
val helloPluginVersion: String by settings
plugins {
id("com.example.hello") version helloPluginVersion
}
}
And now the docs say that build.gradle.kts should be empty but my testing shows that you still need this in build.gradle.kts:
plugins {
id("com.example.hello")
}
The version is now determined by settings.gradle.kts and hence by gradle.properties which is what we want...
There are a couple issues that have some details around this:
gradle/kotlin-dsl#480
gradle/gradle#1697
The way to do this in the most recent verions of Gradle is to use settings.gradle or settings.gradle.kts and the pluginManagement {} block.
In your case, it could look like:
pluginManagement {
resolutionStrategy {
eachPlugin {
when (requested.id.id) {
"org.jetbrains.kotlin.plugin.allopen" -> {
val kotlinVersion: String by settings
useVersion(kotlinVersion)
}
"org.jetbrains.kotlin.jvm" -> {
val kotlinVersion: String by settings
useVersion(kotlinVersion)
}
"org.springframework.boot" -> {
val springBootPluginVersion: String by settings
useVersion(springBootPluginVersion)
}
"io.spring.dependency-management" -> {
val springDependencyManagementPluginVersion: String by settings
useVersion(springDependencyManagementPluginVersion)
}
}
}
}
}

Kotlin DSL: Import a versions.gradle.kts into another build.gradle.kts

I have created a versions.gradle.kts just like that:
object Defines {
const val kotlinVersion = "1.2.61"
const val junitVersion = "5.3.0"
}
Now I want to import and use that files like that:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
group = "io.github.deglans"
version = "0.0.1-SNAPSHOT"
plugins {
application
kotlin("jvm") version Defines.kotlinVersion
}
application {
mainClassName = "io.github.deglans.polishnotation.MainKt"
}
dependencies {
compile(kotlin("stdlib-jdk8"))
testCompile("org.junit.jupiter", "junit-jupiter-api", Defines.junitVersion)
testRuntime("org.junit.jupiter", "junit-jupiter-engine", Defines.junitVersion)
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
How can I do that?
NOTE:
I have already seen this post but it is not exactly that I search...
While I think it should be possible to import another gradle.kts file, I couldn't get it to work properly.
However, I did manage to define my dependencies in a separate Kotlin file in the buildSrc directory.
Create a buildSrc folder in the root of your project (same level as build.gradle.kts)
Add a build.gradle.kts in that buildSrc folder. Here, you need to define the kotlin-dsl plugin. You also need to define the repository where to get the plugin.
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
Create a Kotlin file where you define your dependencies in src/main/kotlin inside the buildSrcfolder. You need to create a normal Kotlin .kt file, not a gradle.kts.
Reimport your Gradle config and you can now use the variables you defined in your Kotlin file created in step #3 in your build.gradle.kts.

Gradle Kotlin DSL: Define Kotlin version in unique place

For describing Gradle build scripts, we can use Kotlin via build.gradle.kts files. It's a common problem to globally define the Kotlin version to be used, both in the dependencies and also in the build plugin section (It's rather uncommon to have different versions in use for the given case).
Consider the following code (Gradle 4.3.1):
plugins {
var pluginVersion = "1.2.30"
kotlin("jvm").version(kotlinVersion)
// more
}
var dependencyVersion = "1.2.30"
dependencies {
compile(kotlin("stdlib", kotlinVersion))
compile(kotlin("reflect", kotlinVersion))
testCompile(kotlin("test", kotlinVersion))
// more
}
As you can see, the kotlin version (1.2.30 in this case) is defined twice: dependencyVersion and pluginVersion, which very often does not differ. Due to DSL restrictions, it is impossible to access the pluginVersion from outside the plugins block or access the dependencyVersion from within the plugins block.
How can the version string, "1.2.30" be extracted to a single place?
In later versions of Gradle you no longer need to specify the version of your kotlin(stdlib|reflect|test) dependencies, the Kotlin plugin will automatically configure them for you.
As for extracting the dependency to a single place, there are two main patterns:
define the constants you want to share in an object within buildSrc/src/main/kotlin/ and use that object in your build script, code from buildSrc is available to the whole script including the plugins block
use a system property, you can define a system property in gradle.properties by prefixing its name with systemProp. and you can access system properties via System.getProperties(), for example:
// build.gradle.kts
plugins {
val kotlinVersion by System.getProperties()
println("Kotlin version is $kotlinVersion")
}
// gradle.properties
systemProp.kotlinVersion=1.2.20
What I just stumbled upon was using Kotlin classes ins my build.gradle.kts.
I had to:
create a module called buildSrc with src/main/kotlin and a build.gradle.kts in its root.
(obsolete) include("buildSrc") in settings.gradle.kts
The buildSrc/build.gradle.kts is very minimal:
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
In buildSrc/src/main/kotlin I've added a Config.kt
const val GROUP_ID = "my-company"
const val VERSION = "0.1.0-SNAPSHOT"
const val POM_NAME = "my-library-name"
const val POM_DESCRIPTION = "A library doing stuff."
const val POM_URL = "https://github.com/${GROUP_ID}/${POM_NAME}/"
const val POM_SCM_URL = POM_URL
const val POM_SCM_CONNECTION = "scm:git:git://github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_SCM_DEV_CONNECTION = "scm:git:ssh://git#github.com/${GROUP_ID}/${POM_NAME}.git"
const val POM_LICENCE_NAME = "The Apache Software License, Version 2.0"
const val POM_LICENCE_URL = "http://www.apache.org/licenses/LICENSE-2.0.txt"
const val POM_LICENCE_DIST = "repo"
const val POM_DEVELOPER_ID = "me"
const val POM_DEVELOPER_NAME = "meeee"
const val POM_DEVELOPER_EMAIL = "me#foo.com"
And a Dependencies.kt
#file:Suppress("MemberVisibilityCanBePrivate")
object Jvm {
const val version = "1.8"
}
object Kotlin {
const val version = "1.3.50"
const val stdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
const val jvmId = "jvm"
const val kaptId = "kapt"
}
object MavenPublish {
const val id = "maven-publish"
}
object Arrow {
const val version = "0.10.1"
const val core = "io.arrow-kt:arrow-core:$version"
const val syntax = "io.arrow-kt:arrow-syntax:$version"
const val optics = "io.arrow-kt:arrow-optics:$version"
const val fx = "io.arrow-kt:arrow-fx:$version"
const val meta = "io.arrow-kt:arrow-meta:$version"
}
object Versions {
const val version = "0.27.0"
const val versions = "com.github.ben-manes:gradle-versions-plugin:$version"
const val id = "com.github.ben-manes.versions"
}
So I could use it in my root build.gradle.kts like
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin(Kotlin.jvmId) version Kotlin.version
kotlin(Kotlin.kaptId) version Kotlin.version
id(Versions.id) version Versions.version
id(MavenPublish.id)
}
group = GROUP_ID
version = VERSION
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation(Kotlin.stdlibJdk8)
implementation(Arrow.core)
implementation(Arrow.syntax)
kapt(Arrow.meta)
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = Jvm.version
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
#Suppress("UnstableApiUsage")
pom {
name.set(POM_NAME)
description.set(POM_DESCRIPTION)
url.set(POM_URL)
licenses {
license {
name.set(POM_LICENCE_NAME)
url.set(POM_LICENCE_URL)
distribution.set(POM_LICENCE_DIST)
}
}
developers {
developer {
id.set(POM_DEVELOPER_ID)
name.set(POM_DEVELOPER_NAME)
email.set(POM_DEVELOPER_EMAIL)
}
}
scm {
connection.set(POM_SCM_CONNECTION)
developerConnection.set(POM_SCM_DEV_CONNECTION)
url.set(POM_SCM_URL)
}
}
}
}
}
I am quite happy with this, but when it comes down to automatically increment the version I may fall back to maintain it in the gradle.properties.
Edit:
It is no longer necessary (and allowed) to add buildSrc to the settings.gradle.kts, instead it will automatically get picked up if present.
You can extract the version from the plugin class:
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
plugins {
kotlin("jvm") version "1.2.0"
}
val kotlinVersion = plugins.getPlugin(KotlinPluginWrapper::class.java).kotlinPluginVersion
Once you've defined a version for the Kotlin plugin all other Kotlin libraries will use the same version, and do not need a specific version set.
So the only place you need to set the version is when defining the classpath for the plugin, e.g.:
buildscript {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31")
}
}
If you then need the version for some other purpose (such as in a resolutionStrategy or just for information) you can get it from org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
For example:
println("Kotlin version used is ${org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION}")
There's a workaround available, which searches the version defined for the kotlin plugin and assignes this one to the outer variable. The following demonstrates this:
val kotlinVersion: String? by extra {
buildscript.configurations["classpath"]
.resolvedConfiguration.firstLevelModuleDependencies
.find { it.moduleName == "kotlin-gradle-plugin" }?.moduleVersion
}
plugins {
kotlin("jvm").version("1.2.30")
//more
}
The variable kotlinVersion can then be used in the dependencies without further trouble.
Answer for #s1m0nw1 comment (too long for comment):
No you can't use buildSrc/src stuff in buildSrc/build.gradle. I had exactly this problem as I wrote android-based plugin and I need android gradle plugin dependency in buildsSrc but I also declare this dependency in project. So I had two different places and two versions to maintain.
I resolved this by creating gradle.properties file in buildSrc directory.
In it I've created prop androidGradlePluginVersion=3.6.0-rc02
buildSrc/build.gradle:
val androidGradlePluginVersion: String by project
dependencies {
implementation("com.android.tools.build:gradle:$androidGradlePluginVersion")
buildSrc/src/.../Versions.kt:
var ANDROID_PLUGIN = loadAndroidGradlePluginVersion()
Util for props:
val GRADLE_PROPERTIES = "buildSrc/gradle.properties"
val ANDROID_PLUGIN_VERSION_PROP = "androidGradlePluginVersion"
fun loadAndroidGradlePluginVersion(): String {
Properties().apply { load(FileInputStream(GRADLE_PROPERTIES)) }.let {
return it.getProperty(ANDROID_PLUGIN_VERSION_PROP)
}
error("Provide $ANDROID_PLUGIN_VERSION_PROP in $GRADLE_PROPERTIES")
}
Easy and fast workaroud:
In buildscript section set value as system property:
buildscript {
val version = "X.X.X"
System.setProperty("version", version)
}
In plugins section get this property:
plugins {
val version = System.getProperty("version")
}

LibGdx version variable in gradle classpath dependencies [duplicate]

I am new to gradle and have some question about project properties.
I need to declare spring boot dependences at multiple locations in my build.gradle and I'd like to use a variable to define the version. What is the best way in gradle? (in Maven, I use properties)
My attempt is use extra properties, but it cannot access the property in the buildscript closure. I googled around and read many articles accessing properties in custom tasks. What did I miss?
ext {
springBootVersion = '1.1.9.RELEASE'
}
buildscript {
print project.springBootVersion //fails here
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.springBootVersion}")
}
}
install {
repositories.mavenInstaller {
pom.project {
parent {
groupId 'org.springframework.boot'
artifactId 'spring-boot-starter-parent'
version "${project.springBootVersion}" //this one works
}
}
}
}
Moving the ext block inside the buildscript block solves the problem for me. Not sure if this is officially supported though, as it's effectively configuring project.ext from the (very special) buildscript block.
Because the buildscript block is evaluated first, before springBootVersion has been defined. Therefore, the variable definition must go in the buildscript block before any other definitions:
Source Here
buildscript {
ext {
springBootVersion = '1.1.9.RELEASE'
}
print project.springBootVersion //Will succeed
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${project.springBootVersion}")
}
}
This will not work.
First of all buildscript block is evaluated at the very beginning, before any other part of groovy script. Hence, properties defined in ext block just does not exist at that time.
Secondly, I'm unsure about if exchanging properties between buildscript and other part of script is possible.

Android gradle build: how to set global variables

How can I set a global variable that can be accessed from build.gradle and tasks?
To set a global variable
project.ext.set("variableName", value)
To access it from anywhere in the project:
project.variableName
For instance:
project.ext.set("newVersionName", versionString)
and then...
println project.newVersionName
For more information see: http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
EDIT:
As commented by Dmitry, In new versions you can use the following shorthand:
project.ext.variableName = value
The answer from Guy is excellent. I just want to add the practical code.
Example:
Put something like this in the Project build.gradle:
project.ext {
minSdkVersion = 21
targetSdkVersion = 23
}
And put something like this in the Module build.gradle to access it:
defaultConfig {
minSdkVersion.apiLevel project.minSdkVersion
targetSdkVersion.apiLevel project.targetSdkVersion
}
You can also do this. Let's say you want to add appcompat with the version 25.3.1, you can add a variable version_name in your project level build.gradle.
buildscript{
ext.version_name = '25.3.1'
}
Now you can add this to your application level build gradle and avoid any conflicts.
compile "com.android.support:appcompat-v7:$version_name"
compile "com.android.support:recyclerview-v7:$version_name"
compile "com.android.support:design:$version_name"
You cant create a Gradle file in the project's root directory and put all variables there, like this:
lib-versions.gradle
ext {
kt_core = '1.6.0'
app_compat = '1.3.1'
material = '1.4.0'
constraintlayout = '2.1.1'
nav_version = '2.3.5'
junit = '4.13.2'
junit_ext = '1.1.3'
escpresso = '3.4.0'
}
Then in the project build.gradle file on the bottom you should apply the Gradle file like this:
buildscript {
...
}
task clean(type: Delete) {
delete rootProject.buildDir
}
apply from: 'lib-versions.gradle' //apply it like this
Then you are able to use the variables in any module-level build Gradle file (app/build.gradle) like this:
implementation "androidx.core:core-ktx:$rootProject.ext.kt_core"
implementation "androidx.appcompat:appcompat:$rootProject.ext.app_compat"
implementation "com.google.android.material:material:$rootProject.ext.material"
implementation "androidx.constraintlayout:constraintlayout:$rootProject.ext.constraintlayout"
...
Additional, for dynamic global variables you can define global functions in the master build.gradle file:
First, define your function, for example for git branch:
def getGitBranch = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
In allProjects section set the variable:
allprojects {
repositories {
google()
jcenter()
}
project.ext {
gitBranch="\"${getGitBranch()}\""
}
}
In your build.gradle files of your sub projects or android modules, get this variable like this:
android {
compileSdkVersion project.mCompileSdkVersion.toInteger()
defaultConfig {
minSdkVersion project.mMinSdkVersion.toInteger()
...
buildConfigField "String", "GitBranch", project.gitBranch
}
...
}
Finally, you can use it in your code like this:
public static String getGitBranch() {
return BuildConfig.GitBranch;
}
This is for Kotlin DSL (build.gradle.kts).
Root (top-level) build file:
val myVariable by extra("watermelon")
// Alternative notations:
// extra.set("myVariable", "watermelon")
// extra["myVariable"] = "watermelon"
Extra properties on a project object are visible from its subprojects. Note that extra. is equivalent to project.extra.; in other words the project object is implicit.
A sub-project build file:
val myVariable: String by rootProject.extra
// Alternative notations:
// val myVariable: String by rootProject
// val myVariable: String = rootProject.extra["myVariable"] as String
// val myVariable: String = rootProject.extra.get("myVariable") as String
Note that when using Kotlin delegation (by keyword) the name of the variables should be the same in both build files.
See Gradle Docs: Extra properties for more information.

Resources