In gradle, how to use a variable for a plugin version?
GradleNetflix Nebula-PluginsGradle Problem Overview
One of my build scripts imports that nebula plugin:
plugins {
id 'nebula.ospackage' version '3.5.0'
}
I've been moving all of my version info into a separate file that all projects have access to and am wondering what is the correct syntax to convert to something like:
plugins {
id 'nebula.ospackage' version "$versions.nebula_gradle_ospackage_plugin"
}
When I try running the above with "gradle clean build", I get the following error:
> build file 'build.gradle': 2: argument list must be exactly 1 literal > non empty string > > See > https://docs.gradle.org/2.7/userguide/plugins.html#sec:plugins_block > for information on the plugins {} block > > @ line 2, column 33. > id 'nebula.ospackage' version "$versions.nebula_gradle_ospackage_plugin"
The linked article shows how I could use the "buildscript" block, which works, but it seems like there must be a way to make this work in a single line?
Gradle Solutions
Solution 1 - Gradle
You cannot use variable here:
> Where «plugin version» and «plugin id» must be constant, literal, > strings. No other statements are allowed; their presence will cause a > compilation error.
Solution 2 - Gradle
As of Gradle 5.6, you can declare your plugin versions in the gradle.properties
file, and reference these properties in plugins
block.
For example, the gradle.properties
file:
springBootVersion=2.2.0.RELEASE
the plugins
block in build.gradle
:
plugins {
id "org.springframework.boot" version "${springBootVersion}"
}
See: https://github.com/gradle/gradle/issues/1697#issuecomment-506910915.
See also:
Solution 3 - Gradle
This is an old post, but the bug is still open, and I found this post looking for workarounds.
It seems you were aware of this already, and actually have BoygeniusDexter's answer, but I think this may help others finding this post like I did. The following workaround is based on the Gradle docs and solved the problem for me:
buildscript {
ext {
springBootVersion = '2.0.4.RELEASE'
}
repositories {
jcenter()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id 'java'
// and other plugins
id 'io.spring.dependency-management' version '1.0.6.RELEASE'
}
// but the one with the variable version is applied the old way:
apply plugin: 'org.springframework.boot'
// We can use the variable in dependencies, too:
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
// ...
}
Solution 4 - Gradle
Summary
- Omit the version from the
plugin
block - Move/specify the version in the
buildscript.dependencies
block, instead - Use a variable in that block that is a
const
variable
In short, plugin
blocks require string literals or properties whereas dependency
blocks permit varables. The original question asks for doing this in a "single line" and with this approach, your plugin block is shorter and all your dependencies live in one place for all modules. Depending on your goals, that might be better than doing everything in "one line."
Full Example
For Android, I was able to do this by omitting the version from the plugin
block and then specifying it as a const
in the buildscript dependencies, instead. That block allows variables, whereas the plugin block only allows string literals. From there, I use an object in buildSrc
because that provides the most flexibility, while keeping all dependency info in one file for all modules. So my setup looks like this:
├── project/
│ ├── build.gradle
| └── app/
| └── build.gradle
| └── buildSrc/
| └── build.gradle.kts
| └── src/main/java/com/example/package/Deps.kt
From there, to specify any plugin version as a variable in one place, the app/build.gradle
file omits the version (using kotlin as an example but the same approach works for any plugin):
app/build.gradle
plugins {
id 'kotlin-android' // omit version property here but provide it in buildscript dependencies
...
}
...
dependencies {
...
// Dagger
implementation Deps.Dagger.ANDROID_SUPPORT
kapt Deps.Dagger.ANDROID_PROCESSOR
kapt Deps.Dagger.COMPILER
}
Then the buildscript dependency section (usually in the parent build.gradle
file but can also be in the same file) provides the version USING A VARIABLE!
project/build.gradle
import com.example.package.Deps // object in the buildSrc folder with all version info
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
}
}
Lastly, all the version information is managed in buildSrc, with an object in the Deps.kt file (there are many blog posts out there on using buildSrc with Android projects):
Deps.kt
object Deps {
// For use in the top-level buildscript dependency section which only works with a constant rather than `Deps.Kotlin.version`
const val kotlinVersion = "1.3.72"
object Kotlin : Version(kotlinVersion) {
val STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
}
object Dagger : Version("2.25.2") {
val ANDROID_SUPPORT = "com.google.dagger:dagger-android-support:$version"
val ANDROID_PROCESSOR = "com.google.dagger:dagger-android-processor:$version"
val COMPILER = "com.google.dagger:dagger-compiler:$version"
}
}
open class Version(@JvmField val version: String)
Overall, I really like this setup. It's flexible, all dependency info is in one place, even across multiple modules and, best of all, it allows for code completion in the gradle files, when typing dependencies!
Solution 5 - Gradle
In addition to @zhouji comment, you can not only specify the versions in gradle.properties
, but you can also define it programmatically like:
buildscript {
ext {
kotlinVersion = "${JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16) ? '1.5.32' : '1.4.32'}"
}
}
plugins {
id 'org.jetbrains.kotlin.jvm' version "${kotlinVersion}"
}
Solution 6 - Gradle
// gradle.properties
springBootVersion=2.6.7
// settings.gradle.kts
pluginManagement {
val springBootVersion: String by settings
plugins {
id("org.springframework.boot") version springBootVersion
}
}
// build.gradle.kts
plugins {
id("org.springframework.boot")
}