84

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?

aemaem
  • 935
  • 10
  • 20
crobicha
  • 1,674
  • 1
  • 16
  • 21

9 Answers9

57

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:

  1. https://docs.gradle.org/5.6/release-notes.html#central-management-of-plugin-versions-with-settings-script
  2. https://docs.gradle.org/5.6/userguide/plugins.html#sec:plugin_management
zhouji
  • 5,056
  • 1
  • 26
  • 26
  • 16
    Note: This works fine for groovy build.gradle but does not work with kotlin DSL, build.gradle.kts, support is yet to come for kotlin DSL, https://github.com/gradle/gradle/issues/9830 – dkb Nov 22 '19 at 11:10
  • Thanks! Curly braces are not really needed, so you can directly put `id "org.springframework.boot" version "$springBootVersion"` – Joaquin Iurchuk Jan 28 '21 at 01:27
  • 1
    "Plugin [id: ...] was not found in any of the following sources:" (did not find gradle.properties in same directory or ignored it) – spyro Nov 08 '21 at 17:56
47

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.

BoygeniusDexter
  • 2,154
  • 1
  • 16
  • 14
  • 5
    This is an open bug: [Allow the plugin DSL to expand properties as the version #1697](https://github.com/gradle/gradle/issues/1697) – Laurence Gonsalves Jan 19 '18 at 00:18
  • 2
    There is an acceptable workaround posted in the above issue: https://github.com/gradle/gradle/issues/1697#issuecomment-386824663 – Nick Nov 02 '18 at 19:25
  • Also, it is possible to write arbitrary code in Kotlin DSL there, although there could be issues accessing specifically *project* properties. You can use whatever is provided by the [buildSrc](https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources) project, though. – Vladimir Matveev Sep 11 '19 at 23:13
  • Looks like it should be possible as of Gradle 5.6 for the plugin version under the plugins block to refer to a property, but apparently only if that property is declared in a `gradle.properties` file. Official comment: https://github.com/gradle/gradle/issues/1697#issuecomment-506910915 – Anders Rabo Thorbeck Apr 21 '21 at 06:52
13

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 {
        mavenCentral()
    }
    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
    // ...
}
barfuin
  • 16,865
  • 10
  • 85
  • 132
  • 1
    This does not work for me for a multi module app: it appears plugins loaded that way in the "allprojects" section are not propagated to the modules. – arkascha Jul 12 '22 at 08:36
10

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}"
}
Dmitry Spikhalskiy
  • 5,379
  • 1
  • 26
  • 40
7

Gradle 7

Gradle 7 introduced version catalogs for declaring dependencies and sharing them between projects/modules.

Plugins can be declared in the catalog:

// settings.gradle

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('android-plugins', '7.3.0')
            plugin('android-application', 'com.android.application').versionRef('android-plugins')

            // A library declaration using the same version.
            library('some-library', 'com.group', 'artifact').versionRef('android-plugins')
        }
    }
}

The declaration can be referred to in the plugins block:

// build.gradle

plugins {
    alias(libs.plugins.android.application) apply false
}

The version can also be referred to in the plugins block:

// build.gradle

plugins {
    id 'com.android.application' version libs.versions.android.plugins apply false
}

See the version catalogs documentation for more information.

Note: In the declaration, - is used as separator in the dependency name. On the consuming side . is used instead, as - is the sign for the minus operator of the groovy language and can't be used in property names. So the separators are different on the declaring and consuming side - which threw me off at first.

aruh
  • 396
  • 6
  • 10
  • Android Studio Giraffe (2022.3.1) - _currently in Preview_ - is adding [IDE support](https://developer.android.com/studio/preview/features/#gradle-version-catalogs) for Gradle version catalogues. – aruh Jan 18 '23 at 13:41
4

Summary

  1. Omit the version from the plugin block
  2. Move/specify the version in the buildscript.dependencies block, instead
  3. 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!

gMale
  • 17,147
  • 17
  • 91
  • 116
1
// 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")
}
seunggabi
  • 1,699
  • 12
  • 12
0

I hit this issue.

when I ran

gradle clean test build

instead of

./gradlew clean test build

and my "gradle" was at version 4.x

but my ./gradlew resolved to 6.9 or higher.

aka, "oops, I forgot my build.gradle files were coded to use later versions of gradle(w), but I snapfu-ed and executed the older 4.x version of regular "gradle"

granadaCoder
  • 26,328
  • 10
  • 113
  • 146
0

To add yet another approach, you can use plugin resolution rules. This allows you to skip declaring the version in the build.gradle.kts files and instead declare them in the settings.gradle.kts file.

This approach allows you to use the "real" name of the plugin and makes sure all of them are the same version. If you need different versions in different projects this may not be a good approach for you.

// build.gradle.kts
plugins {
    id("com.autonomousapps.dependency-analysis")
    id("com.dorongold.task-tree")
    id("com.github.ben-manes.versions")
}
// settings.gradle.kts
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "com.autonomousapps.dependency-analysis") { useVersion("1.19.0") }
            if (requested.id.id == "com.dorongold.task-tree") { useVersion("2.1.1") }
            if (requested.id.id == "com.github.ben-manes.versions") { useVersion("0.46.0") }
        }
    }
}

requested.id.id is not a typo.

Captain Man
  • 6,997
  • 6
  • 48
  • 74