The plugin DSL that is used to load build script plugins does not currently support expanding properties, as defined e.g. as part of gradle.properties
. This is a follow-up to https://discuss.gradle.org/t/how-to-use-property-for-version-when-using-a-plugin-via-id/22122.
Code like
plugins {
id 'org.jetbrains.kotlin.jvm' version "${project.properties.kotlinVersion}"
}
println "${project.properties.kotlinVersion}"
should work, given that gradle.properties
has kotlinVersion=1.1.1
.
The build fails with
FAILURE: Build failed with an exception.
* Where:
Build file 'build.gradle' line: 2
* What went wrong:
Could not compile build file 'build.gradle'.
> startup failed:
build file 'build.gradle': 2: argument list must be exactly 1 literal non empty string
See https://docs.gradle.org/3.4.1/userguide/plugins.html#sec:plugins_block for information on the plugins {} block
@ line 2, column 9.
id 'org.jetbrains.kotlin.jvm' version "${project.properties.kotlinVersion}"
^
1 error
I'm trying to define the Kotlin version only once for both the plugin version and the stdlib compile time dependency. Also see this forum thread.
Use the code from above and run gradle build
.
Gradle 3.4.1 on Windows 10 64-bit, Kotlin Gradle plugin version 1.1.1.
I found a sort-of-workaround when using 3.5-rc-1
, haven't tried it in rc-2
yet but I don't expect it to change between those versions. I'm not sure if this is really defined behavior, but you can see other users with a similar request and my workaround on the forum at https://discuss.gradle.org/t/settings-gradle-not-finding-properties-from-gradle-properties/18175/14?u=mkobit
+1
Plugin resolution management (repositories, versions pinning etc..) should be done in settings.gradle
, see https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management
@bimargulies-google, settings.gradle
file is per build and is evaluated before any project, see https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:settings_file
I figured out that I mean to be commenting on https://github.com/gradle/gradle/issues/1323, so I want to delete these comments here.
Just to be clear, my original post is not about plugin resolution management, I just want to be able to use properties (for whatever purpose) as part of the plugins
closure.
+1
Is this that hard? I hate having duplicated variables.
+1
This is really annoying. It would be nice if the Gradle team stopped breaking Groovy/Kotlin semantics in their DSL (the biggest advantage of using powerful languages in the build files is exactly that you should be able to count on re-using variables and basic things like that).
Sorry for the rant :) To compensate for that, here's the workaround I ended up using, based on suggestions above:
gradle.properties
file. Example:kotlinVersion: 1.2.41
pluginManagement
block in settings.gradle
to set the version of the plugin:pluginManagement {
resolutionStrategy {
eachPlugin {
if ( requested.id.id == 'org.jetbrains.kotlin.jvm' ) {
useModule( "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" )
}
}
}
}
plugins
blocks, just don't give a version:plugins {
id "org.jetbrains.kotlin.jvm"
}
dependencies
blocks too:dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
}
Tested with Gradle 4.7.
@renatoathaydes This is a great solution. Will try to implement it in my work projects tomorrow.
How about using a lookup table instead of the if?
I saw that the Kotlin team now recommends not adding a version to the Kotlin dependencies in the dependencies
declarations as the plugin version will automatically be used for Jetbrains dependencies! So, that should actually solve this problem.
From this page:
Starting with Kotlin 1.1.2, the dependencies with group org.jetbrains.kotlin are by default resolved with the version taken from the applied plugin
It still seems to require repeating version in plugin DSL:
plugins {
id "org.jetbrains.kotlin.jvm" version "1.2.41"
id "org.jetbrains.kotlin.plugin.spring" version "1.2.41"
id "org.jetbrains.kotlin.plugin.jpa" version "1.2.41"
id "org.springframework.boot" version "2.0.1.RELEASE"
id "io.spring.dependency-management" version "1.0.5.RELEASE"
id "eclipse"
id "idea"
}
This is what we do:
pluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace != null && requested.id.namespace.startsWith("org.jetbrains.kotlin")) {
useVersion kotlinVersion
}
}
}
}
kotlinVersion is a property in gradle.properties which is resolved prior to settings being run
plugins {
id "org.jetbrains.kotlin.jvm"
id "org.jetbrains.kotlin.plugin.noarg"
id "org.jetbrains.kotlin.plugin.allopen"
}
If you're using the kotlin DSL, you can do
if (requested.id.namespace?.startsWith("org.jetbrains.kotlin") == true)
since it's marked as nullable
I saw that the Kotlin team now recommends not adding a version to the Kotlin dependencies
@renatoathaydes, that is pure awesome. To recap
plugins {
id "org.jetbrains.kotlin.jvm" version "1.2.41"
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib" // Version resolved by the Kotlin plugin
}
https://kotlinlang.org/docs/reference/using-gradle.html#configuring-dependencies
I just spent 20h wondering why I couldn't get my properties file recognized; it turns out it's an issue in the plugins block? This is pretty frustrating.
Came back from stackoverflow answer that it is an open question. Wanted to refer version of detekt
plugin for kotlin. I hope it gets resolved soon.
Here's a slightly more generic work-around. Steps:
version
from the plugins
id
line.gradle.properties
, e.g. "detektVersion".settings.gradle
:pluginManagement {
resolutionStrategy {
eachPlugin {
// Work around https://github.com/gradle/gradle/issues/1697.
if (requested.version == null) {
def pluginName = requested.id.name.split('-').collect { it.capitalize() }.join().uncapitalize()
def versionPropertyName = (requested.id.id == 'org.jetbrains.kotlin.jvm') ?
"kotlinPluginVersion" : "${pluginName}PluginVersion"
logger.info("Checking for plugin version property '$versionPropertyName'.")
if (gradle.rootProject.hasProperty(versionPropertyName)) {
def version = gradle.rootProject.properties[versionPropertyName]
logger.info("Setting '${requested.id.id}' plugin version to $version.")
useVersion version
} else {
logger.warn("No version specified for plugin '${requested.id.id}' and property " +
"'$versionPropertyName' does not exist.")
}
}
}
}
}
I'd like to add my voice to this issue. IMO, using resolution strategies is undesirable - it's far wordier and far more difficult for newcomers to discover or understand than simple variable expansion.
The lack of variables here is a real pain point for a few projects that I maintain, and has been the source of a number of frustrating build problems when the resulting profusion of version strings are updated incompletely.
Any update on this. It would be useful in the case where you have a multi-project gradle build and want to define the version of a plugin once throughout the build rather than having different versions everywhere.
@vab2048
Here is an example of how to get the same result now:
https://handstandsam.com/2018/02/11/kotlin-buildsrc-for-better-gradle-dependency-management/
@donbeave
Whilst that example is actually quite a method of managing dependency strings it still does not work when used _within a plugins block_.
@vab2048
Let me show an example of how I use this method:
/buildSrc/src/main/java/Versions.kt:
object Versions {
// Languages
const val kotlin = "1.3.21"
// Gradle
const val gradleWrapper = "5.2.1"
// Gradle plugins
// https://plugins.gradle.org/plugin/nu.studer.jooq
const val gradleJooqPlugin = "3.0.1"
// Libraries
const val springBoot = "2.1.3.RELEASE"
const val commonsLang = "3.8.1"
const val guava = "27.0.1-jre"
const val postgresql = "42.2.5"
// ... etc
}
/buildSrc/build.gradle.kts:
plugins {
`kotlin-dsl`
idea
}
repositories {
mavenLocal()
gradlePluginPortal()
jcenter()
}
idea {
module {
isDownloadJavadoc = false
isDownloadSources = false
}
}
/build.gradle.kts:
plugins {
java
idea
kotlin("jvm") version Versions.kotlin
id("org.springframework.boot") version Versions.springBoot
// ... etc
}
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath("org.postgresql:postgresql:${Versions.postgresql}")
// ... etc
}
}
// ... etc
dependencies {
compile("org.apache.commons:commons-lang3:${Versions.commonsLang}")
compile("com.google.guava:guava:${Versions.guava}")
// ... etc
}
As you can see this will help you to use one place to list all versions, which can use in plugins
, buildscript's dependencies
and project's dependencies
methods.
@donbeave your code only works because you're using Gradle Kotlin DSL, which is a good to know workaround!
If you convert back to Groovy build.gradle
:
/build.gradle.kts
to /build.gradle
plugins
and buildscript
(as necessary in Gradle Groovy)plugins
block:plugins {
id("java")
id("idea")
id("org.jetbrains.kotlin.jvm") version Versions.kotlin
id("org.springframework.boot") version Versions.springBoot
}
it fails with an error: "argument list must be exactly 1 literal non empty string"
Side-note: your example project actually doesn't use Versions.kotlin
version for production compilation, because of kotlin-dsl
in buildSrc
(which uses Gradle's built-in version), run gradle buildEnvironment
on your project and notice kotlin-stdlib:1.3.21 -> 1.2.61
Maybe it's a good motivation to switch from Gradle Groovy DSL to Kotlin DSL? )
Maybe it's a good motivation to switch from Gradle Groovy DSL to Kotlin DSL?
No, this is just lame. Why limit the Groovy DSL's plugins block so much that a constant can't be used to specify a plugin version, but that's fine in the Kotlin DSL?
EDIT
According to the docs:
Only the API of this type can be used, and values must be literal (e.g. constant strings, not variables)
A constant in another file is not a literal, so accepting that is actually a bug in the Kotlin DSL.
@renatoathaydes: this is categorized as a bug, not an intentional limitation. Fixing it all depends on how much effort it will take the Gradle devs, and where in their priority queue it falls.
Given that the Kotlin DSL seems to be better than the Groovy DSL in all aspects (so people should migrate from Groovy to Kotlin, where possible), if fixing this will take them a ton of time, it’s probably not worth fixing. If it’s a quick fix, then it might be worth it.
@donbeave was obviously making a joke, while also encouraging people to look into the Kotlin DSL. The Kotlin DSL has been much better so far, in my experience.
@renatoathaydes: RE: Your complaint about the difference between the JavaDoc & the Kotlin DSL: oh no, they forgot to update the JavaDoc, therefore they should remove useful functionality!
You could have nicely pointed out the discrepancy, and suggested that they update the JavaDoc to indicate that the Kotlin & Groovy DSLs are slightly different.
Here’s some advice: the more antagonistic you are, the less likely some other person will be to want to help you. (I’m not a Gradle dev, and I can’t speak specifically for them, but that maxim seems to hold true for people in general).
@rgoldberg no, the limitation exists for a reason.
The Kotlin implementation allows me to call a function to determine the plugin version.
That makes it very hard to achieve repeatable builds.
I don't buy that allowing variable expansion leads to non-reproducible builds; as long as you're not pulling your plugin versions from /dev/urandom
, it'll be deterministic. This just seems like a straight-up misfeature to me, and as long as the Groovy DSL is a first-class citizen here this ought to be fixed.
@renatoathaydes: being able to call functions doesn’t make it hard to have a repeatable build; just don’t call functions, or ensure that your functions return repeatable values. Almost everyone calling a function with non-repeatable return values from plugins
will know the consequences; almost everyone who wouldn’t understand the consequences will just use a literal anyway.
I would also like the plugins
block to not be so restrictive in Groovy, but let's not mix things up: the reason it is so limited is that it was designed that way - there is code there to forbid you from using anything other than a literal in the plugins block, and that is documented.
I will let the Gradle Team defend that design decision (I believe the main purpose is not just to make repeatable builds more likely, but to speed up Gradle execution as it can make more assumptions about what plugins the build uses on hot builds), but please don't call this a bug in the Groovy DSL: as the documentation and the design stands, it is the Kotlin DSL that is not behaving as designed, and that does not seem intentional to me at all, otherwise the Groovy DSL and the docs would have been modified accordingly (perhaps that's what they will do, I wouldn't mind that!).
A Gradle dev added the a:bug label to this issue soon after it was opened, so it has indeed been categorized as a bug (maybe that was a miscategorization).
I, too, would prefer the DSLs to be equivalent, but, for this issue, I would prefer them to be lenient like Kotlin rather than restricted like Groovy. The big questions are how much effort it will take, and whether that time could be better spent fixing other bugs and/or adding other features.
I don’t see any reason to prefer Groovy over Kotlin (except for possibly not having the time to learn Kotlin, or to redo an existing Groovy build). If the Gradle devs & community concur, then updating the Groovy DSL with new abilities to achieve parity with Kotlin (rather than just documenting differences) seems very low priority if it will take too much time away from higher priority tasks.
If anyone knows any reasons to prefer Groovy over Kotlin other than for legacy issues, it would be useful to hear them.
@rgoldberg this ticket has absolutely nothing to do with the Groovy VS Kotlin debacle, stop pushing that here. This issue is about the official Gradle build DSL, which is the Groovy one. Also, please don't pretend you have some insight into the Gradle's teams priorities or try to tell them what to spend time on.
Sorry to the others for polluting the thread even more.
@renatoathaydes:
Where is it documented that Groovy is the official DSL & Kotlin isn’t official?
I never said I have insight about the Gradle devs' priorities. You tried to dictate to them that they should work on making the 2 DSLs consistent; while I agreed with you that it is optimal for the 2 DSLs to be consistent, I acknowledged that this might be of low priority to them if it would take too much time / resources. Notice that I always said "if" it would take too much time; I don't know the amount of effort required or resources available.
I mentioned those things only because I was answering a question that you asked:
Why limit the Groovy DSL's plugins block so much that a constant can't be used to specify a plugin version, but that's fine in the Kotlin DSL?
My answer, to recap, is that allowing extra flexibility is desirable, so it shouldn't be removed from Kotlin, since it's already there; the flexibility isn't yet there in Groovy, however, but it would be fine for this to be available in Kotlin but not in Groovy if the Gradle devs don't have time to get around to it now.
A second possible answer to your question could be that the extra flexibility is too slow in Groovy, but isn't too slow in Kotlin. If that hypothetical is actually the case, why hinder Kotlin for a limitation that only applies to Groovy?
If you don't want people to answer your questions, don't ask them.
Where is it documented that Groovy is the official DSL & Kotlin isn’t official?
I was curious to know the answer, so here's a bit of research:
TL;DR: it depends, as usual 😉. When this issue was opened, production-ready Kotlin DSL was a dream, but then many months later it became supported.
2018-11-26 Gradle 5.0 https://docs.gradle.org/5.0/release-notes.html#kotlin-dsl-1.0
Official language: Groovy or Kotlin https://docs.gradle.org/5.0/userguide/writing_build_scripts.html
Gradle provides a domain specific language, or DSL, for describing builds. This build language is available in Groovy and Kotlin.
from release notes:
follow our migrating build logic from Groovy to Kotlin guide if you're interested. If you prefer the flexibility and dynamic features of Groovy, that's totally okay — the Groovy DSL will not be deprecated.
Note: I also found related issues in Kotlin DSL with similar issues as discussed here:
... but I digress, @rgoldberg have you seen this from OP? https://discuss.gradle.org/t/how-to-use-property-for-version-when-using-a-plugin-via-id/22122 Gradle devs stated this is a shortcoming and should be addressed. @oehme can you please update on your plugins
shortcomings addressing plans?
@TWiStErRob: Thanks for the link.
Thanks everyone for your comments on this issue, and for your patience while you wait for some action.
We've spent some time investigating this issue, and considered a number of potential fixes.
plugins
blockThis seems simple and would provide consistency with how many users manage the version of their regular code dependencies. However, the approach is more problematic than it first appears.
Plugin versions are different from regular dependencies, in that they influence the _classpath_ used for compiling and running Gradle build scripts. Gradle does extensive caching of both the script classpath and the compiled scripts, and this caching does not currently take into account properties and environment variables. So an initial attempt at allowing properties to be replaced in version strings was not successful: changes to the properties file were ignored due to caching of the compiled build scripts. While it would be possible to be smarter about cache invalidation, we want to be careful not to degrade performance by doing this too aggressively (particularly for Kotlin build scripts, where compilation time is significant).
In summary: allowing property replacement in plugin version strings seems straightforward, but caching of compiled build scripts makes it more problematic.
plugins
block to reference constants defined in buildSrc
The implementation of plugins
is quite different for the Groovy and Kotlin DSLs. For Groovy we implemented AST transforms to extract the information during a first pass script compilation (the code is never actually invoked). For Kotlin, we instead restrict the inputs and actually invoke the code in the plugins
block (I'll admit I don't fully understand how this works).
In order to make the Groovy DSL behave more like the Kotlin DSL, we'd likely need to reimplement the script compilation of the former to work more like the latter. This may be a worthwhile exercise, but defining constants in buildSrc
feels more like a workaround for the lack of property replacement, and has the downside of invalidating your entire build script whenever a version changes.
In summary: Allowing access to buildSrc
constants/code in the plugins
block will require significant refactoring of the mechanism used to load Groovy scripts.
buildSrc
dependenciesThis mechanism works in current versions of Gradle, and can be quite effective to centrally manage plugin versions. Basically, you add the plugin as an implementation
dependency to your buildSrc
root project, and the plugin can then be easily applied by ID from within your build scripts.
buildSrc/build.gradle
repositories {
gradlePluginPortal()
}
dependencies {
implementation "org.gradle.guides:gradle-guides-plugin:0.15.12"
}
build.gradle
plugins {
id "org.gradle.guides.tutorial"
}
One downside to this approach is that you must know both the GAV and the plugin ID for the plugin you're requesting. (Note the difference between org.gradle.guides:gradle-guides-plugin
and org.gradle.guides.tutorial
).
In summary: buildSrc
dependencies can be used to centrally manage the plugin versions, but it feels like a step back toward the old buildscript.classpath
dependency declarations.
pluginManagement
Ideally, the plugin versions required will be defined in a central location, allowing the plugins to be applied by id in any build script. Gradle 5.6 will provide this functionality, with plugin versions being configured in the pluginManagement
block of the Gradle Settings.
settings.gradle[.kts]
pluginManagement {
plugins {
id("org.gradle.guides.tutorial") version "${gradleGuidesVersion}"
}
}
build.gradle[.kts]
plugins {
id("org.gradle.guides.tutorial")
}
The new pluginManagement.plugins
block does not have the same constrained syntax as a buildscript plugins
block, allowing flexible version management via property replacement, version constants, etc.
In addition, this mechanism has benefits over buildSrc
dependencies:
a) it doesn't _require_ a buildSrc
project and
b) the GAV for the plugin artifact is not required.
In Summary: We think this new pluginManagement
mechanism will remove the need for the plugins
block to permit more flexible syntax or property replacement.
If you've voted for or commented on this issue, we'd like to hear from you. Does the new pluginManagement.plugins
block give you the functionality you need? Would you be OK with us closing this issue, providing the pluginManagement.plugins
block as the solution? Please let us know.
allowing property replacement in plugin version strings seems straightforward, but caching of compiled build scripts makes it more problematic.
The new pluginManagement.plugins block does not have the same constrained syntax as a buildscript plugins block...
Adding a new pluginManagement
block that has fewer restrictions seems to be equivalent to lifting the restrictions from the plugins
block?
Also, the benefit of having a central location for declaring dependencies would not be a real benefit if the plugins
block were not so limited in the first place (resolve properties first, then resolve the plugins
block, is all that would be needed).
The implementation of plugins is quite different for the Groovy and Kotlin DSLs.
Only the Groovy implementation seems to correctly limit the plugins
block's syntax as the design should do to allow proper caching (As we've shown above, the Kotlin DSL does not prevent arbitrary code running inside it, making it impossible to cache anything)?
In order to make the Groovy DSL behave more like the Kotlin DSL, we'd likely need to reimplement the script compilation of the former to work more like the latter.
Why are you considering changing the Groovy implementation rather than the Kotlin one if the result should be cache-able? Doesn't the current Kotlin implementation make it necessary to evaluate the plugin
block _even if nothing has changed in the build itself_?
Adding a new pluginManagement block that has fewer restrictions seems to be equivalent to lifting the restrictions from the plugins block?
It covers the same use cases, yes. The pluginManagement.plugins
block will be available in Gradle 5.6. I'm curious if there are use cases that are not supported by this mechanism, or where the new mechanism is clearly inferior.
Also, the benefit of having a central location for declaring dependencies would not be a real benefit if the plugins block were not so limited in the first place
I'm not sure I agree. Declaring all of the available plugins (and their versions) is a single location feels like a better solution. It will mean that your build scripts can apply any declared plugin without version, whether that's a built-in plugin (like java-library
) or a 3rd-party one (like org.springframework.boot
).
Only the Groovy implementation seems to correctly limit the plugins block's syntax as the design should do to allow proper caching (As we've shown above, the Kotlin DSL does not prevent arbitrary code running inside it, making it impossible to cache anything)?
No, this isn't correct. Due to the way the plugins
block is implemented for Groovy, dynamic behaviour like property replacement will be lost when we cache the compiled output (we've effectively implemented a custom compiler that resolves all of the plugin versions when compiling). For Kotlin, the implementation is a bit more conventional, and we actually invoke the compiled plugins block to determine the plugin versions. So even with caching, we are still able to resolve versions from dynamic sources.
Why are you considering changing the Groovy implementation rather than the Kotlin one if the result should be cache-able? Doesn't the current Kotlin implementation make it necessary to evaluate the plugin block even if nothing has changed in the build itself?
Yes, I think this is the case. But invoking the compiled code is cheap compared to the act of compiling the build scripts. @bamboo or @eskatos would have better insight into how the plugins
block is implemented for the Kotlin DSL, but my testing shows that it interacts well with caching. I _think_ that if the Groovy implementation was similar, then we'd be more easily able to allow property replacements.
Thanks @bigdaz for the proposals. In the end, pluginManagement.plugins
just seems to be syntactic sugar for using the pluginManagement.resolutionStrategy.eachPlugin
approach that already works with current Gradle versions. There are some small things that bother me about the pluginManagement.plugins
solution:
settings.gradle
would need to create one.build.gradle
and settings.gradle
.Does the new
pluginManagement.plugins
block give you the functionality you need?
Yes, it basically provides the needed functionality, but it's slightly less convenient to use than I had envisioned it to be.
Would you be OK with us closing this issue, providing the
pluginManagement.plugins
block as the solution?
As the reporter of the issue, I'd be fine with closing it. Like said, I could imagine the feature to be implemented in a more user-friendly way, but I understand that doing so might require significant engineering work to resolve the caching issues, which probably doesn't outweigh the gained small user-experience improvements.
Yes, I think this is the case. But invoking the compiled code is cheap compared to the act of compiling the build scripts.
Exactly right, @bigdaz.
The compiled form of the plugins
block is stored to the cache before the very first evaluation and while it might impose some overhead compared to, say, loading the set of applied plugins ({(id, version, apply)}
) directly from a file, we consider the additional flexibility important as it allows for a better user experience in the IDE, namely, content-assist for plugin ids found in the classpath and quick access to the documentation of core plugins.
It sounds like refactoring Groovy plugins
handling will take too much of an effort to fit into the foreseeable Gradle development schedule.
It also sounds like both the Gradle & Kotlin plugins
implementations currently have adequate performance, despite Kotlin allowing, and Gradle disallowing, ad hoc code in plugins
.
If any of the above is incorrect, please let me know.
If it is correct, will you continue to allow Kotlin to run ad hoc code in plugins
?
I don’t see any need to restrict Kotlin because of limitations in Groovy, as long as the difference between the two DSLs is adequately documented.
Thanks.
we've effectively implemented a custom compiler that resolves all of the plugin versions when compiling
For Kotlin, the implementation is a bit more conventional, and we actually invoke the compiled plugins block to determine the plugin versions.
I don't understand why you implemented the Kotlin parsing differently from the Groovy parsing. If you had a custom parser for the Groovy DSL, surely you could re-use it in the Kotlin implementation? And in case the Kotlin implementation (which is basically to evaluate the code like the normal Groovy compile would've done if you didn't write a custom compiler) is enough for the design objectives to be reached, why do you need to keep the custom compiler?
@rgoldberg :
I don’t see any need to restrict Kotlin because of limitations in Groovy,
This is not due to a limitation of Groovy, as explained above. As far as I know, the Groovy compiler allows far more compilation customization than the Kotlin compiler does.
@renatoathaydes: I didn’t mean a limitation in the Groovy language, I meant a limitation in the Groovy DSL.
It really doesn’t make sense to penalize Kotlin users because Groovy isn’t currently as flexible. I’d obviously like it if Groovy were just as flexible as Kotlin, but, it seems that the Gradle devs are saying that it will take too much effort to make it so right now (maybe I’m misunderstanding them, but no one has said so yet). I trust their ability to assess the required work, and their knowledge of the most useful tasks on which to allocate development resources.
I also understand your desire for consistency, but it shouldn’t be at the expense of existing functional flexibility for Kotlin.
If you so value consistency, and think that it should be easy to update Groovy to support the flexibility of Kotlin, Gradle is open source, so you can implement it yourself and submit a PR.
So for me, one of the key use-cases that property-expansion enables is sharing common versions across plugins and runtime-dependencies, Kotlin being the prime example. For the sake of the argument, let's pretend that kotlin isn't special-cased by Gradle here. What I really want out of this feature is to be able to say:
val kotlinVersion = "1.3.10"
plugins {
id("kotlin") version kotlinVersion
}
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
}
Of the five solutions presented, only the first one offers this possibility. I do appreciate that caching at the scale Gradle does is quite delicate, but every other solution feels overly verbose to me; they don't address the core issue, which is that the current state of affairs means I have either to repeat myself, or spread my build definition across kinds of files (build.gradle and either gradle.properties or settings.gradle).
Of the five solutions presented, only the first one offers this possibility.
Actually, none of the solutions as described offer this possibility. If implemented, the first one would permit values from gradle.properties
to be referenced in version string. Values defined in the build script would not be.
I'm not sure this is a particularly valid use-case. You're saying that the version of something used by your build logic needs to be the same as the version of something used by your production code. Besides convenience, is there a reason that these need to be the same? It feels strange to tie your build logic to your production code in this way.
@bigdaz: A company can mandate that only specific versions of software be used anywhere in the codebase, because those versions have been vetted, or just to reduce complexity of debugging. In such a situation, it probably makes sense to have a central version properties file instead of declaring a variable in a build script, but it’s an example of why you might tie together build & production library versions.
I don't understand why you implemented the Kotlin parsing differently from the Groovy parsing. If you had a custom parser for the Groovy DSL, surely you could re-use it in the Kotlin implementation?
The way the Groovy DSL support implemented (with AST transforms) made it difficult (if not impossible) to adapt to the Kotlin DSL.
And in case the Kotlin implementation (which is basically to evaluate the code like the normal Groovy compile would've done if you didn't write a custom compiler) is enough for the design objectives to be reached, why do you need to keep the custom compiler?
We don't need to keep it. Removing it would involve refactoring both DSL implementations (in particular the Groovy one) so that they use a common approach, and hopefully extracting some common code. The reason this wasn't done in the first place was due to pressure to deliver and prioritization. I'm sure anyone who's a software engineer is familiar with these tradeoffs.
@bigdaz: A company can mandate that only specific versions of software be used anywhere in the codebase, because those versions have been vetted, or just to reduce complexity of debugging. In such a situation, it probably makes sense to have a central version properties file instead of declaring a variable in a build script, but it’s an example of why you might tie together build & production library versions.
Fair enough. Assuming that the version of the plugin matches the version of a production library, you'll be able to achieve this in Gradle 5.6 by putting the version in a properties file, and configuring your plugin versions via settings.gradle
.
Fair enough. Assuming that the version of the plugin matches the version of a production library, you'll be able to achieve this in Gradle 5.6 by putting the version in a properties file, and configuring your plugin versions via settings.gradle.
To clarify, you're not limited to a properties file. You can implement whatever build logic you like to discover the supported versions.
Besides convenience, is there a reason that these need to be the same? It feels strange to tie your build logic to your production code in this way.
It's not just convenience, it's maintainability. _With_ variable substitution, I only have to update a coupled version in one place. _Without_, as proposed, I now have multiple places to make the same change. My team has had several issues over the past year or two specifically due to this restriction. It's not as if failing builds caused outages or anything, but time lost to build issues is a real cost.
I'm not sure this is a particularly valid use-case. You're saying that the version of something used by your build logic needs to be the same as the version of something used by your production code.
I'm not sure why it should be strange that plugin versions might need to be coordinated with runtime dependency versions; source-generating plugins might well need to be used with specific versions of an accompanying library, for example. Kotlin was perhaps a poor example here.
With variable substitution, I only have to update a coupled version in one place. Without, as proposed, I now have multiple places to make the same change.
I'm not sure which "as proposed" you're referring to. The primary benefit of what's being proposed is that it will be _possible_ to source all of your plugin versions from a properties file, or using other build logic.
In some cases it would be more convenient to allow property substitution directly into the build script (as opposed to declaring all of your plugins in settings.gradle
). I had hoped to get this working, but I tried to explain why this wasn't straightforward. So I'm interested in whether the new mechanism is an improvement (which I hope it is), and whether there are use cases that still make plugin versions difficult to manage.
Thank you for your thought and effort here. I recognize that you had hoped to get substitution working, and that it has turned out to be impractical, and understand that you are seeking feedback about the proposed mechanisms.
My feedback is that pluginManagement
doesn't represent an improvement over the status quo, in my opinion. Instead of helping to consolidate dependency definitions in one file (preferably _one place_ within one file), definitions will be spread across multiple files. In my ideal world, we would be able to define a single-module project in _one_ file, without being unduly repetitious within it.
If I had to choose one of the options above, having _all_ dependencies (both runtime and compile-time) be centralized is preferable. If that means relying on buildSrc
, that's better IMO than "plugin dependencies are defined _here_, and libraries go over _there_, and don't forget that versions are all in that one .properties file!".
I respect that there are tradeoffs inherent in any complex system, and trust that Gradle staff make good choices. Thanks for listening to us!
I, too, thank you for the options presented & work put into this. I think that pluginManagement.plugins
is useful. I also think that the current Kotlin DSL behavior (allowing ad hoc code in plugins
) is ideal. I honestly don’t care about the Groovy DSL as I'll never use it, but I understand that it will take tons of effort to update it, and that the effort isn’t warranted in the current development schedule. All I care about is that the Kotlin DSL won’t be limited in some quixotic effort to make Groovy & Kotlin exactly the same. No one has advocated that except @renatoathaydes, so I assume it won’t happen. But I haven’t seen anyone completely rule it out…
@rgoldberg this issue only exists because a lot of effort was spent by the Gradle team to make sure the plugins
block was limited to what they documented it should be limited to (basically, only literals, no property expansion).... for some reason, this was only done in the Groovy DSL, probably because doing this in the Kotlin DSL would be impossible (from what I know of the nearly non-documented Kotlin compiler API).
This is the only thing I am against: it's not like the Groovy compiler limits what the plugins block can do, it's the compiler hook (PluginUseScriptBlockMetadataExtractor
) they explicitly wrote to limit it. Now, if the Kotlin DSL is not properly limited and that's not considered to be a problem at all, then I can't see why PluginUseScriptBlockMetadataExtractor
should'nt be re-written to lift the limitations... looks like a simple matter of removing almost all of the restrict
calls in that class, not really a huge effort (though other parts of the system seem to have been written in away that assumes certain things, perhas, making that a bit more difficult?)
I have no intention of migrating to the Kotlin DSL, and none of my team does either. I am happy some people like the Kotlin DSL and believe they will benefit from type-checking their build scripts, but I've seen prettty much 0 benefit from using it in a couple of projects I tried, while I had a lot of problems getting "recipes" you find online to do common things working (nearly every example you find around uses Groovy DSL, and sometimes it's literally impossible to get things to type check in Kotlin). So making the Kotlin DSL superior by intentionally limiting the Groovy DSL in unnecessary ways is kind of a big let down for me.
Anything complex, we just put in buildSrc
(using Kotlin sometimes, or Java/Groovy when appropriate) so the SDL for us should simply be as clean as possible, and the Groovy DSL is superior in that regard as it is always as expressive as the Kotlin DSL, or more (having to do things like with(tasks.getByName("wow") as Wow)
rather than with wow
for example).
Sorry for the rant, and I also would like to thank the Gradle team for listening!!
I can't see why PluginUseScriptBlockMetadataExtractor should'nt be re-written to lift the limitations... looks like a simple matter of removing almost all of the restrict calls in that class
I _wish_ it was this simple. The problem is that we extract the plugin id/versions when _compiling_ the build script, and we never actually _invoke_ the compiled code. So when I tried to change PluginUseScriptBlockMetadataExtractor
, it would read an initial version from a properties file, but then ignore subsequent changes to that property. (Because our caching didn't force the script to recompile when properties are changed).
I'm currently working on changing the underlying mechanism, so that we actually execute the compiled plugins
block on each build invocation to determine the plugin requests. (This is the mechanism used in the Kotlin DSL). A big unknown is the impact this will have on performance, which may be different between Kotlin and Groovy.
I'm currently working on changing the underlying mechanism, so that we actually execute the compiled plugins block on each build invocation to determine the plugin requests.
Thanks, that would be great!
A big unknown is the impact this will have on performance, which may be different between Kotlin and Groovy.
In "normal" cases where you simply call id 'x' version 'y'
, and in cases where properties get expanded, all that Groovy will do is call a method implemented elsewhere (probably in Java) so the performance penalty should be negligible, no?
@renatoathaydes:
If there is no need to limit Kotlin for performance reasons, then Kotlin shouldn't be limited just to make it exactly the same as Groovy (Groovy users are not hurt by Kotlin having extra flexibility, but Kotlin users are hurt by losing the flexibility). You, however, have repeatedly made the completely asinine suggestion that Kotlin should be so limited, and have misrepresented things to bolster your completely spurious arguments.
All that needs to be done is to note the discrepancy in the documentation.
If the Gradle devs have the time to remove the restriction from Groovy, then great, I have no problem with that, as long as it doesn't take so much effort that it delays other more important new Gradle features, and as long as it doesn't cause many bugs.
Throughout this thread, you have continually misrepresented things.
this issue only exists because a lot of effort was spent by the Gradle team to make sure the plugins block was limited to what they documented it should be limited to (basically, only literals, no property expansion).... for some reason, this was only done in the Groovy DSL, probably because doing this in the Kotlin DSL would be impossible (from what I know of the nearly non-documented Kotlin compiler API).
It's been made abundantly clear that the reason for the limitation in Groovy is because of performance concerns, and the reason for the lack of the limitation in Kotlin is because of a lack of performance concerns.
This is the only thing I am against: it's not like the Groovy compiler limits what the plugins block can do, it's the compiler hook (PluginUseScriptBlockMetadataExtractor) they explicitly wrote to limit it. Now, if the Kotlin DSL is not properly limited and that's not considered to be a problem at all, then I can't see why PluginUseScriptBlockMetadataExtractor should'nt be re-written to lift the limitations... looks like a simple matter of removing almost all of the restrict calls in that class, not really a huge effort (though other parts of the system seem to have been written in away that assumes certain things, perhas, making that a bit more difficult?)
It's been stated numerous times that the time to implement workarounds for the Groovy performance issues will take too long relative to the benefits that can obtained by working on other Gradle features instead. Maybe there's a quicker way to implement this fix; again, I have nothing against the Gradle devs, you, or others working on this, as long as they think it's worth the effort.
So making the Kotlin DSL superior by intentionally limiting the Groovy DSL in unnecessary ways is kind of a big let down for me.
They didn't make Kotlin superior by intentionally limiting Groovy in unnecessary ways. They limited Groovy to avoid performance problems, not to make Kotlin seem better. Maybe the limitation was unnecessary for Groovy, or maybe it was necessary at the time it was implemented (possibly just to save development time for other more important issues).
Hi all,
Jumping in here because I'm going to be working in this area of the code for a Gradle Enterprise feature.
Here's another issue that we need to wrestle with. The goal of some of my work is to add support for the plugin
block to settings
and init
scripts. This means we also need to have support for the pluginManagement
block.
The issue here is this:
| Script Type | plugin
in | pluginManagement
in |
|-------------|-----------------------|-----------------------|
| Project | build.gradle(.kts) | settings.gradle(.kts) |
| Settings | settings.gradle(.kts) | init.gradle(.kts) |
| Init | init.gradle(.kts) | ????? |
A hacky quick fix is to put could put pluginManagement
for init scripts in an alphabetically higher script file because init scripts are evaluated alphabetically.
Doing this will make it so that init
scripts are the only script type where pluginManagement
is able to configure its own script type.
pluginManagement
into the file it's configuringThis would be a breaking change but the result would be that the Groovy and Kotlin handler logic would need to extract and compile first the pluginManagement
block and then the plugin
block. This may require a lot more refactoring and would be a paradigm shift.
The result would look like this:
| Script Type | plugin
in | pluginManagement
in |
|-------------|-----------------------|-----------------------|
| Project | build.gradle(.kts) | build.gradle(.kts) |
| Settings | settings.gradle(.kts) | settings.gradle(.kts) |
| Init | init.gradle(.kts) | init.gradle(.kts) |
plugins.init.gradle(.kts)
fileThis would be similar to the "abuse the alphabet" solution.
We need to continue to allow users to be able to execute arbitrary code inside of the pluginManagement
block so I don't believe that Solution 2 will work for us.
@rgoldberg said:
It's been made abundantly clear that the reason for the limitation in Groovy is because of performance concerns, and the reason for the lack of the limitation in Kotlin is because a lack of performance concerns.
This is something you just made up. No one has said that Groovy inspects the plugins
block AST at compile-time, and does not ever execute the block, due to performance concerns in Groovy which do not exist in Kotlin.
What was said by @bigdaz was:
I'm currently working on changing the underlying mechanism, so that we actually execute the compiled plugins block on each build invocation to determine the plugin requests. (This is the mechanism used in the Kotlin DSL). A big unknown is the impact this will have on performance, which may be different between Kotlin and Groovy.
This is quite different.
Also:
But invoking the compiled code is cheap compared to the act of compiling the build scripts... ... I think that if the Groovy implementation was similar (to the Kotlin one), then we'd be more easily able to allow property replacements.
I'd imagine that executing a similar, compiled plugins
block in Kotlin VS Groovy would result in a negligible difference in the order of a couple of milliseconds at most. I also have suggested, and no one has corrected me so far, that the fact that the Kotlin DSL executes the plugins
block, resulting in different behaviours in the two DSLs, was not a design choice and was simply a implementation artifact (given they didn't have the option to do the same thing exactly in the new Kotlin implementation).
Throughout this thread, you have continually misrepresented things.
@rgoldberg No, I haven't. Please refrain from making baseless accusations when that does not add anything to the conversation, it just shows your immaturity. Your only point here seem to be that the Kotlin DSL is superior in general, and bad luck to those using the Groovy DSL. I don't know how you can believe that this helps anyone or is somehow related to resolving this ticket.
In case youv'e forgotten, this ticket regards an issue in the Groovy DSL (not the Kotlin DSL) which is affecting lots of people, including me... and remember that the Groovy DSL is the one following the documented behaviour!
I am only here trying to help the Gradle team make the right decisions for Gradle users who, like me, would like Gradle scripts to be easy to use and predictable - hence executing the plugins
block (and keeping semantic compatibility between DSLs) is the best choice - specially given that the Kotlin DSL gets away fine (or does this make it slower than the Groovy one, potentially??) with doing that (and violating the documented behaviour), and again, doing so in Groovy would have nearly identical performance-wise, unless proven otherwise.
@renatoathaydes Wow, you never stop misrepresenting things:
Your only point here seem to be that the Kotlin DSL is superior in general, and bad luck to those using the Groovy DSL
One of my many points is that it does not make sense to limit the functionality of Kotlin just because Groovy currently has a specific limitation. How hard is it to understand that? Really, come on…
In case youv'e forgotten, this ticket regards an issue in the Groovy DSL (not the Kotlin DSL) which is affecting lots of people, including me... and remember that the Groovy DSL is the one following the documented behaviour!
You're the one who has repeatedly suggested limiting Kotlin in this very issue. You're the one who keeps bringing Kotlin into this, and I'm trying to ensure that your stupidity doesn't infect anyone else and result in Kotlin being limited. Also, don't you understand that documentation can be changed very easily?
This is something you just made up. No one has said that Groovy inspects the plugins block AST at compile-time, and does not ever execute the block, due to performance concerns in Groovy which do not exist in Kotlin.
In Groovy, non-literals don't currently work with the caching. If the caching were not used, the problem wouldn't exist. The caching is done for performance. See the connection between the limitation & performance? Or is it too hard for you to understand this?
No matter what, the issues that are resolved by only allowing literals exist solely in Groovy, not in Kotlin, so stop suggesting that Kotlin be limited.
that the fact that the Kotlin DSL executes the plugins block, resulting in different behaviours in the two DSLs, was not a design choice and was simply a implementation artifact
It doesn't matter if it was by design or by accident. Kotlin currently allows more flexibility. It shouldn't be limited just because Groovy is limited.
Again, despite you implying otherwise, I don't see anything wrong with Groovy being updated to have the same flexibility as Kotlin, as long as the Gradle devs think it's worth the time it will take to refactor it. There are other open issues in Gradle, and if they see them as more efficient uses of their time, then it makes sense to just document the difference between the two DSLs until such a time when Groovy can be refactored. If they do think it's worth the time, it wouldn't hurt if Groovy were more flexible and consistent with Kotlin, even if only to prevent you from posting more ridiculous comments in this thread.
No one has ever mentioned in this issue any reason for limiting Groovy to literals other than to allow it to work with the caching (except your ridiculous & baseless assertion that it was done to make Kotlin superior). Can you supply any real alternate/additional reason why Groovy was limited to literals? If not, stop screeching about limiting Kotlin.
If you'd stop suggesting that Kotlin be limited, and if you'd stop misrepresenting what others (including myself) have said, then I wouldn't have to deal with you anymore…
@rgoldberg and @renatoathaydes
Please respect our Contributor Code of Conduct in all of your communication here.
If possible, can any of the Gradle devs let everyone know if there’s currently any possibility that Kotlin will have literal-only (or similar) restrictions placed on its plugins
?
If such restrictions might be enacted, could you also provide rationales for doing so? That would be informative, and would also allow people on any side of the discussion to more accurately frame their arguments.
If possible, can any of the Gradle devs let everyone know if there’s currently any possibility that Kotlin will have literal-only (or similar) restrictions placed on its
plugins
?
No, we won't be adding any such restrictions. For one, then we'd have even more people voting to fix this issue, which is not what we want :). We are more likely to relax the restrictions on the Groovy DSL.
I think the original reason that this restriction was implemented for the plugins
block (Groovy DSL) was to make it as close to imperative as possible, so that external tooling could do static analysis of build scripts. As far as I know this never actually happened: tools like IDEA and Eclipse use the Gradle Tooling API to interrogate builds, and now that we have the Kotlin DSL as well it's becoming less likely.
so that external tooling could do static analysis of build scripts.
That's a really bad idea for build scripts that are written in an interpreted DSL anyway, IMO. All such external tooling simply must be using the tooling API for reliable results.
Thanks everyone for your feedback on this issue, and your passion to make Gradle better.
In the end, I bit the bullet and decided to push to make property replacement work for version strings in the plugins
block. If things go as expected, this will be sorted in Gradle 5.6: you'll be able to declare your plugin versions in a gradle.properties
file, and reference these properties in plugins
block.
Note that to me, declaring all of your plugin versions in settings.gradle
is preferable: this gives a nice separation of your build-logic dependency versions from your application dependency versions. But we understand that different projects have different requirements, and hopefully this change will make the plugins
block more useful.
One thing to note: the syntax of the plugins
block remains highly constrained:
version
accepts interpolated strings (id
must be a string literal)gradle.properties
file can be inserted into the version string, as can constant values that are defined in buildSrc
version
argument.Follow up for handling this in the Kotlin DSL: #9830
Is there any easy way for version
to support String
variables directly without having to reference the variable from inside a GString
? It would be nice to avoid boilerplate, if possible.
e.g.,
version s
Instead of
version “${s}”
Also, it looks like multiple concatenated variables are supported, e.g.:
version “${major}${minor}”
But anything containing literals is not supported, e.g.:
version “${major}.${minor}”
Is that correct?
I’m not suggesting that either of the above is good practice, but it would be weird to support the former but not the latter. Could you easily support the latter?
Before this gets released in 5.6, is there any word on the two minor modifications I requested in https://github.com/gradle/gradle/issues/1697#issuecomment-506926301 ?
I also asked 3 questions in the commit page, mainly focused on ensuring that the change is correctly described / documented. e.g.,
https://github.com/gradle/gradle/commit/41442d803ada9b119df8cbc77b41d9a49811ad79#r34126691
It is unfortunate that only project properties are supported. I have all my versions declared in the buildSrc project inside a class/object with static fields, and this is still not supported by this change.
IMO, it is more convenient and sensible to keep all versions, of both plugins and dependencies, in one place, and I don't really want to pollute gradle.properties
, because all of them are added to the project properties global namespace. Also, I have projects written in both groovy and kotlin dsls, and there is no way to unify them for now: using versions declared ion gradle.properties
in kotlin builds requires an extra step of declaring all of the val property: String by project.ext
lines to extract these properties, so it is highly unwieldy. Therefore, I cannot use the same approach across groovy/kotlin projects if I don't want to increase complexity.
I understand that this is a rather deep limitation of the groovy dsl, and it might take some time to fix, but this particular ticket is closed now. Is there a ticket about adding support for more complex things in the plugins
block?
@bigdaz Hi I'm not sure how to do it, but how to mention a dotted property in the plugin block
Following the documentation of 5.6 release
The current setup and third party use dot properties for a lot of things, e.g. sonarqube.version
, this property is already used elsewhere in the build by third party plugin. No usual notations works like project.'a.b'
, rootProject.'a.b'
, properties('a.b'), ext['a.b']
(which is somehow expected from what I read above), but is there a way to refer to this kind of notation, or am I bound to duplicate the property named as a single word.
What I'd like, or something close to it
pluginManagement {
plugins {
id 'org.sonarqube' version "${properties('sonarqube.version')}"
}
}
Why is it closed? I still cannot do the following in my build.gradle.kts
val kotlinVersion: String by project
plugins {
kotlin("jvm") version kotlinVersion apply false
kotlin("kapt") version kotlinVersion apply false
}
I am using gradle of version 6.0.1
Why is it closed? I still cannot do the following in my
build.gradle.kts
See above, you have to use literal strings for the version:
plugins {
kotlin("jvm") version "$kotlinVersion" apply false
kotlin("kapt") version "$kotlinVersion" apply false
}
Why is it closed? I still cannot do the following in my
build.gradle.kts
See above, you _have_ to use literal strings for the version:
plugins { kotlin("jvm") version "$kotlinVersion" apply false kotlin("kapt") version "$kotlinVersion" apply false }
Still doesn't work
I'm not using Kotlin DSL but this is what I do.
gradle.properties:
kotlinVersion=1.3.61
settings.gradle:
pluginManagement {
plugins {
id "org.jetbrains.kotlin.android" version "$kotlinVersion"
id "org.jetbrains.kotlin.android.extensions" version "$kotlinVersion"
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion"
id "org.jetbrains.kotlin.kapt" version "$kotlinVersion"
}
}
build.gradle:
plugins {
id "org.jetbrains.kotlin.jvm"
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
}
I'm not using Kotlin DSL but this is what I do.
gradle.properties:kotlinVersion=1.3.61
settings.gradle:
pluginManagement { plugins { id "org.jetbrains.kotlin.android" version "$kotlinVersion" id "org.jetbrains.kotlin.android.extensions" version "$kotlinVersion" id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" id "org.jetbrains.kotlin.kapt" version "$kotlinVersion" } }
build.gradle:
plugins { id "org.jetbrains.kotlin.jvm" } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib" }
Good.
My poing is that the feature is incomplete and this issue should not be marked as closed or completed. Otherwise there is nothing which puts this issue into developers agenda and we will continue to resort to code duplication.
In short, kotlin developers had bad experience before this issue was open and that bad experience remains after it is closed.
@sschuberth @technoir42 Kotlin DSL and Groovy DSL are different in this regard. In Groovy DSL, properties defined in the gradle.properties
file end up available directly as properties of the Project
instance, which in turn are available in the build script right away.
In Kotlin DSL, properties in gradle.properties
are not made available automatically, and one must explicitly reference them, either via delegates or by calling methods.
Also, this restriction about double-quoted strings applies only to Groovy DSL; in Kotlin DSL, "$whatever"
and whatever
are completely equivalent constructions inside the plugins
block.
@21region it seems that the recent versions of Gradle changed something, and now you can't really access the Project
instance inside the plugins
block. Or maybe it was never possible? Anyway, I think the only way now would be to define properties in the buildSrc
project.
@sschuberth @technoir42 Kotlin DSL and Groovy DSL are different in this regard. In Groovy DSL, properties defined in the
gradle.properties
file end up available directly as properties of theProject
instance, which in turn are available in the build script right away.In Kotlin DSL, properties in
gradle.properties
are not made available automatically, and one must explicitly reference them, either via delegates or by calling methods.Also, this restriction about double-quoted strings applies only to Groovy DSL; in Kotlin DSL,
"$whatever"
andwhatever
are completely equivalent constructions inside theplugins
block.@21region it seems that the recent versions of Gradle changed something, and now you can't really access the
Project
instance inside theplugins
block. Or maybe it was never possible? Anyway, I think the only way now would be to define properties in thebuildSrc
project.
Correct.
You couldn't reference kotlinVersion
in plugins
block in gradle 5.4.1 and you still cannot do that in gradle 6.0.1.
If I am not misguided by my reading comprehension abilties, the issue states exactly the same problem and somehow it is managed to get closed without the problem being fixed.
Now there is no forcing function
(how Elon Musk says =) ) on developers to implement this feature except for my comments in a closed issue almost no one is looking at...
@21region
If I am not misguided by my reading comprehension abilties, the issue states exactly the same problem and somehow it is managed to get closed without the problem being fixed.
The issue is probably about Groovy DSL, where it was not possible to do this at all :)
The issue is probably about Groovy DSL, where it was not possible to do this at all :)
So should a new issue be created for Kotlin? As @21region points out, this is a problem for anyone using the Kotlin DSL.
This issue and the lack of support for the Kotlin DSL in Dependabot are forcing me to switch to the Groovy DLSL.
@gnarea won't something like this work for you:
// settings.gradle.kts:
pluginManagement {
val kotlinVersion: String by settings
plugins {
id("org.jetbrains.kotlin.jvm").version(kotlinVersion)
id("org.jetbrains.kotlin.kapt").version(kotlinVersion)
}
}
// build.gradle.kts:
plugins {
// Apply plugins without versions:
id("org.jetbrains.kotlin.jvm")
id("org.jetbrains.kotlin.kapt")
}
Btw, you don't have to explicitly set the version of kotlin-stdlib
and other libraries in org.jetbrains.kotlin
group:
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib") // will match the version of Kotlin plugin
implementation("org.jetbrains.kotlin:kotlin-reflect")
}
Most helpful comment
I'd like to add my voice to this issue. IMO, using resolution strategies is undesirable - it's far wordier and far more difficult for newcomers to discover or understand than simple variable expansion.
The lack of variables here is a real pain point for a few projects that I maintain, and has been the source of a number of frustrating build problems when the resulting profusion of version strings are updated incompletely.