Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to define property to be used in both settings.gradle.kts and projects/subprojects build.gradle.kts with gradle 6.0?

We have multi-module android app with build logic written in gradle kotlin dsl. We use buildSrc to extract common logic like dependencies versions. We have something like:

buildSrc/src/main/kotlin/Dependencies.kt:

object Versions {
    const val fooVersion = "1.2.3"
    const val barVersion = "4.5.6"
}

object Libraries {
    val foo = "com.example.foo:foo:$fooVersion"
    val bar = "com.example.bar:bar:$barVersion"
}

object Modules {
    const val app = ":app"
    const val base = ":base"
    const val baz = ":baz"
}

Then we can use these in modules' dependencies block to avoid hardcoded/duplicated values:

app/build.gradle.kts:

dependencies {
    implementation(Libs.foo)
    implementation(Libs.bar)

    implementation(project(Modules.base))
    implementation(project(Modules.baz))
}

And we also use it in settings.gradle.kts:

settings.gradle.kts:

include(
    Modules.app,
    Modules.base,
    Modules.baz
)

This works ok with gradle 5.6. When I upgrade to 6.0, I get Unresolved reference: Modules in settings.gradle.kts file. I found it mentioned in migration guide:

Previously, the buildSrc project was built before applying the project’s settings script and its classes were visible within the script. Now, buildSrc is built after the settings script and its classes are not visible to it. The buildSrc classes remain visible to project build scripts and script plugins.

Custom logic can be used from a settings script by declaring external dependencies.

So I know what broke the build and I can fix the build by using hardcoded values in settings.gradle.kts:

include(
    ":app",
    ":base",
    ":baz"
)

Is it possible to avoid this duplication with gradle 6.0?

like image 999
ferini Avatar asked Nov 27 '19 09:11

ferini


People also ask

How do I define properties in Gradle?

In Gradle, properties can be defined in the build script, in a gradle.properties file or as parameters on the command line. It’s common to declare properties on the command line for ad-hoc scenarios. For example you may want to pass in a specific property value to control runtime behavior just for this one invocation of the build.

Is it possible to use Gradle kts with Gradle properties?

No, this is not possible since gradle.properties configures the JVM that runs the Gradle build and settings.gradle.kts configures the project once the JVM has started and the build starts up. See the documentation on the build environment Show activity on this post.

What is a Gradle script?

A Gradle script is a program. We use a Groovy DSL to express our build logic. Gradle has several useful built-in methods to handle files and directories as we often deal with files and directories in our build logic. In today’s post, we will take a look at how to set Gradle properties in a project build.

What is the default name of the settings file in Gradle?

The settings file is determined by Gradle via a naming convention. The default name for this file is settings.gradle . The settings file is executed during the initialization phase. There is a one-to-one correspondence between a Settings instance and a settings.gradle settings file.


1 Answers

See ticket #11090 "Definitions from buildSrc/ not found in settings.gradle.kts using gradle 6.0-rc-1".

As you already noticed this changed recently:

This has changed in 6.0, and was deprecated in 5.6. Please see: https://docs.gradle.org/current/userguide/upgrading_version_5.html#buildsrc_usage_in_gradle_settings

-- https://github.com/gradle/gradle/issues/11090#issuecomment-544473179

One of the maintainers describes the reasons behind the decision:

Unfortunately, there are pros and cons to both arrangements (settings-then-buildSrc and buildSrc-then-settings), and we opted for the former after considering.

(...)

The pros that compelled us to make the change:

  1. Settings plugins can influence buildSrc and main build (i.e. apply a build plugin to both)
  2. Build cache configuration is applied to buildSrc
  3. buildSrc behaves more like a regular included build

-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268

And finally some bad news:

We won't be changing the behaviour back to the pre Gradle 6 arrangement. Please let us know if you would like more detail on how to use one of the alternative mechanisms for using complex logic in a settings script.

-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268

Workarounds

In the aforementioned post the author proposes some workarounds:

The con of this is exactly what you have hit. It's now less convenient to use complex logic in your settings script. Now, you have to either:

  1. Inline the logic into the settings file
  2. Move the logic to a shared script that can be used where it needs to
  3. Move the logic to a pre-built binary that you load in the settings file (i.e. a settings plugin)

-- https://github.com/gradle/gradle/issues/11090#issuecomment-545697268

#1 is pretty straightforward, but I can only assume what #2 and #3 mean. I come from the Groovy world and only recently started making friends with Kotlin DSL. Having said that let's give it a try.

In #3 the author might be talking about developing an external plugin and applying it in both scripts. I'm not really sure if this is something that would make sense to implement (it gives you strong typing though).

"#2 Move the logic to a shared script that can be used where it needs to"

I think it's about having a common script plugin and including it in both settings.gradle and build.gradle files. The plugin would put the static information in the ExtraPropertiesExtension of the ExtensionAware in scope (Settings in case of a settings.gradle script plugin and Project in case of build.gradle). This is described in this answer to "Include scripts with Gradle Kotlin DSL":

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

-- Include scripts with Gradle Kotlin DSL

like image 195
jannis Avatar answered Oct 11 '22 10:10

jannis