-
Notifications
You must be signed in to change notification settings - Fork 4.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce a settings DSL for central dependency declaration #14896
Conversation
60b13a1
to
a4f434a
Compare
That is awesome.
Do you think it could support overriding the version with a command-line flag? What is the approach for file splitting? |
Technically speaking we can. Now I'd have to talk to the larger team to say if we should :) Note that if you override a dependency version, it would probably only override the default model, so if you have something like:
For now I'd say KISS. We could provide alternatives if there's a legitimate interest in making this more complicated. Alternatively settings plugins can provide this feature. Last but not least, because we do code generation we could hit the limit of the number of methods in a class...
This PR lets you declare this:
then you don't have to specify a version when applying in build scripts. |
Let me clarify the use-case. I want to verify if my project works with a newer version of a third-party dependency. Note: I can't use
Here's the case where I install Here's the case when Checkstyle CI job verifies the new Checkstyle works for verifying of pgjdbc sources: https://github.com/checkstyle/checkstyle/blob/3c2249b239cbfe002af3649f1f7c1f9ae61df3b1/.ci/wercker.sh#L60-L71 |
I wonder if TOML could be separate from the code generator, so out-of-core plugins could leverage the same generator. |
That's exactly the case: the TOML file is just, if you will, a standard plugin hooking into the code generator. In the end, there's a single code generator and settings plugins can contribute entries. But in the end we're going to be limited by the number of methods which can be defined in a class file. |
That is certainly a legitimate use case. |
That looks seriously awesome 🤩 . Thanks for addressing this !! Will the TOML file support dependencies "groups" that need to be all the same version so that it can be changed in a single place? For an example (that obviously won't work since I don't think TOML supports variables) but just to demonstrate the goal: [versions]
sqldelight = "1.7.0"
[dependencies]
sqlDelightPlugin = { group = "com.squareup.sqldelight", name = "gradle-plugin", version = "${versions.sqldelight}" }
sqlDelightDriverAndroid = { group = "com.squareup.sqldelight", name = "android-driver", version.strictly = "${versions.sqldelight}" }
sqlDelightDriverAndroid = { group = "com.squareup.sqldelight", name = "native-driver", version.strictly = "${versions.sqldelight}" }
Also would it be possible to copy/paste the maven coordinates in one go? That's a minor change but not having to separate group/artifactId/version would be a nice addition as most of the documentations use that representation: [dependencies]
guava = { gav = "com.google.guava:guava:27.0-jre" } Which leads me to a wider question: did you consider a Kotlin script for this that would expose a strongly typed way to register dependencies and could be self documented? Something like dependencies {
create("guava") {
group = "com.google.guava"
artifact = "guava"
version= "27.0-jre"
}
// or the shorthand gav version
create("guava") {
gav = "com.google.guava:guava:27.0-jre"
}
}
bundles {
create("groovy") {
from("groovy", "groovy-json")
}
} Could that work? |
Not out of the box. If we do this I'd rather add a
That's already supported by this PR :)
There's already the settings DSL for this. Note, however, that using Kotlin or the settings DSL would invalidate all build scripts classpath and trigger recompilation of all scripts if any version or coordinate changes, whereas using the TOML this would only happen if you add/remove an alias, not if you change GAV coordinates. |
Yup, makes sense 👍
Neat ! \o/
Cool! Is there an exemple somewhere?
That's a pretty big downside and certainly one of the main reason not a lot of people use this? (I think?) |
It's in the issue description ;) |
Got it, Thanks 👍 I was looking in the current production docs 🤦 . What about moving that DSL to a separate |
AFAIK I just checked a couple of modifications to I tried to edit a string literal inside |
no you're right, using the settings API this would be fine, I was mistaken with the current buildSrc approach. |
There are no docs because we didn't decide to go with this proposal yet. |
8855724
to
c152f81
Compare
This PR got me thinking... Would it make sense for the TOML file to be generic and not only about dependencies? A kind of supercharged type-safe # properties.toml
[libs]
guava = "com.google.guava:guava:27.0-jre" // build.gradle.kts
dependencies {
// can't think of a better name than `typesafeProperties` but something shorter would be nice
implementation(typesafeProperties.libs.guava)
} Of course this is less expressive than having the TOML file know about dependencies but I think specifying the versions constraints, etc can be left to a Having a generic # properties.toml
[publishing]
url = "https://github.com/me/my-library"
group = "com.example"
version = "1.0" // build.gradle.kts
group = typesafeProperties.publishing.group
version = typesafeProperties.publishing.version
publishing {
publications {
create<MavenPublication>("mavenJava") {
pom {
name.set("My Library")
description.set("A concise description of my library")
url.set(version = typesafeProperties.publishing.url)
}
}
}
} This could also be used to share properties between multiple projects, inject credentials, or whatever people use |
@martinbonnin I don't think we should do this. The big advantage of this DSL and TOML file is that we know what the properties are used for. It allows for some optimizations and it allows better modeling. We can discuss its extension to other use cases but we shouldn't mix all things together IMO. |
a4f434a
to
3b9b0f4
Compare
@bot-gradle test this |
OK, I've already triggered ReadyForMerge build for you. |
@bot-gradle test this |
OK, I've already triggered ReadyForMerge build for you. |
No, that's exactly about this. Currently the versions are not exposed to precompiled script plugins (or production code in
The So the So sharing with production code, because of class shadowing, is a bit tricky. We can certainly use a different namespace to make it explicit, maybe use a different prefix to access the extensions, ... But I think we should take time to think about the solution and not rush here. |
I really want some official solution for this dependency mess. |
It's expected because there is an overlap between the two, and you might want to use both at the same time. A platform is a component in a dependency graph, which can be upgraded on its own (for example if you have the Spring BOM in a dependency graph, it can be upgraded to a different version because of transitives), participates into dependency resolution (constraints declared in a platform will apply even if you don't use them), is inherited by consumers (a platform is a node in the graph so consumers will see it). Anything declared in a platform will influence any graph which uses this platform, including transitive dependencies. A catalog, like proposed here, is very lean: it’s only used for first level dependencies, in order to pick versions. It has no influence on the consumer and any “unused” dependency in a catalog will not influence the resolution. The consumer of a catalog is Gradle itself, while the consumer of a platform is any library, Gradle, Maven, etc. This proposal is about fixing the "redundant declaration" use case, which isn't really addressed today and that people implement in various ways.
This follow-up PR actually does something like that: it lets you declare a version catalog from a platform definition. The choice of how to consume the platform, either as a platform, or as a catalog, or both, is a consumer choice. Catalogs can also be composed from different sources easily, without "leaking" to consumers, which is one of the drawbacks of platforms today. |
I started to use it and it works great, in TOML file is possible to generate dependencies in three different ways
The disadvantages of TOML are missing code completion and error messages which not only tell the line with error but it also not tell that this error is related to TOML file parsing until you use --stacktrace DSL currently allows just this
There is no way how to use If you want to create a bundle from multiple library with the same version it is possible in this way via DSL
I would maybe expect some more structuralized way like
or just without bundle
or
It is visually much more clear that it is a group that belong together. I would prefer DSL way over TOML, but it is quite verbose and it must be inside of settings.gradle.kts file, separated will would be better. And I miss a possibility to create a more structuralized libs hierarchy by using "." inside of alias name, which will create a new packages inside of libs object like
|
And what about maven dependencies with classifier? It looks that it is completely unsupported :-( |
I'm playing with new versionCatalog DSL, works great, but I found big general issue for my common use case: convention plugins. So if I have dependencies.toml, I can use it any any module and in buildSrc (if include it), but I cannot use dependencies versions in my precompiled convention plugin: I understand why it happening, but I think it's a big limitation in general, because I apply many dependencies using different convention plugins, so even if I able to read toml file, I will not get any generated accessor. Should it be possible somehow to generate libs accessors also for precompiled script plugins (which already support Kotlin DSL accessors generation)? I may create a feature request, if it makes any sense |
We have ideas how to make this possible, but probably not for the first release. |
@melix Is there an issue to follow for this? |
The dependency catalog focuses on dependency coordinates, namely the Group, Artifact and Version. |
Do you know why this doesn't work for me in gradle-6.8-milestone-3?
It was removed meantime? |
Yes, this was removed from 6.8. You can use |
Thanks! So it will be in 6.9, right? I will keep it for now without it, I want to put into production with 6.8, 6.9 is too far. |
There are some cases when it fails like this one
ends with
|
Can you please file issues for those cases? |
It looks that in gradle-6.8-rc-1 it completely stops working :-( Or it is possible to enable it somehow in gradle.properties? |
No the whole central dependencies declaration has been removed. And I mean creating issues with the reproducers for what you've found. This ticket is closed, there have been many follow ups and if you don't want your bug reports to be lost it's better to track them with proper issues. |
@melix should be great there is a place where check all info about this feature. I always visit this issue. |
@melix Done. I hope that I did it in the right way. ;-) |
Yes. At least slack channel for this would be great. |
I just wrote a script that can generate a TOML file for a multimodule project, including versions ref extraction, and replace all old dependencies with libs.xyz in the next step. Except for the lack of IDE support, it works great. I also already implemented it on my project, because I was expected that it will be in 6.8 :-( At least as an experimental feature. |
Please refer to this issue for tracking progress: #15352 |
Weird that I can see, that this #15352 issue has "0 of 12 tasks complete", but I'm unable to find which tasks they are :-( |
@tprochazka Yeah I noticed that. We're using ZenHub to track epics and unfortunately you can't see anything. This is what you should see: |
This commit introduces a new DSL, available on
Settings
via thedependency resolution management block, which allows declaring aliases
for dependencies and bundles of dependencies.
Doing this, Gradle will automatically generate type-safe accessors
which are exposed as extensions to all projects. This effectively
allows sharing dependency declarations between projects.
Said differently, this is an officially supported pattern for the
various "dependency version sharing" patterns that we found in the
wild:
the version via
project.someLibraryVersion
buildSrc
which contains dependency coordinatesand then can be accessed in the different projects
In addition to this DSL, we introduce a TOML file, which, if found under
the
gradle
directory, will be used to source dependency aliases.For example, giving the following file:
a dependency can be added in a project using the type-safe
libs
extension:This file also supports bundles of dependencies, in case more than one
dependency needs to be added:
then dependencies can be added via:
The file is configuration-cache safe: any change to the file will invalidate the
cache. But more importantly, it's build cache safe: if the aliases don't change,
there's no need to rebuild the dependent scripts. This means that changing this
file by changing, for example, the dependency versions, will not trigger a
recompilation of build scripts like some of the approaches described above.
The TOML file also lets you declare plugin versions:
which allows application of the plugin without version in build scripts:
In addition, we also generate type-safe accessors for projects. For example:
can be replaced with:
Therefore any change to project coordinates, or removals of subprojects would
trigger a build script compilation error, avoiding tedious search and replace.
Fixes #?
Context
Contributor Checklist
<subproject>/src/integTest
) to verify changes from a user perspective<subproject>/src/test
) to verify logic./gradlew <changed-subproject>:check
Gradle Core Team Checklist