Vladimir Orany
05/30/2024, 8:50 AMorg.gradle.api.UnknownDomainObjectException: Extension with name 'libs' does not exist. Currently registered extension names: [ext, base, defaultArtifacts, sourceSets, reporting, javaToolchains, java, testing]
at org.gradle.internal.extensibility.ExtensionsStorage.unknownExtensionException(ExtensionsStorage.java:148)
at org.gradle.internal.extensibility.ExtensionsStorage.getByName(ExtensionsStorage.java:125)
at org.gradle.internal.extensibility.DefaultConvention.getByName(DefaultConvention.java:187)
at org.gradle.kotlin.dsl.accessors.runtime.RuntimeKt.extensionOf(Runtime.kt:35)
at Build_gradle$1$1$1$2.invoke(build.gradle.kts:15)
at Build_gradle$1$1$1$2.invoke(build.gradle.kts:14)
Groovy version:
plugins {
id 'com.agorapulse.gradle.root-project'
}
gradleProjects {
subprojects {
dir('libs') {
repositories {
mavenCentral()
}
dependencies {
implementation libs.commonsLang
}
}
}
}
Kotlin version:
import com.agorapulse.gradle.root.ProjectsExtension
plugins {
id("com.agorapulse.gradle.root-project")
}
extensions.configure<ProjectsExtension> {
subprojects {
dir("libs") {
repositories {
mavenCentral()
}
dependencies {
add("implementation", libs.commonsLang)
}
}
}
}
Reproducer with build scan links can be found here https://github.com/musketyr/gradle-kotlin-libs-issueVampire
05/30/2024, 10:18 AMextensions.configure<ProjectsExtension> { ... }
, as gradleProjects { ... }
should work just fine.
Besides that, I guess as soon as you stop to use the highly discouraged and problematic subprojects { ... }
, your issue would go away anyway.Andres Almiray
05/30/2024, 10:27 AMsubprojects
section is not Gradle’s but from the custom DSL exposed by Vladimir’s plugin based on the Kordamp pluginsVampire
05/30/2024, 10:28 AMsubprojects { ... }
and thus has the same problems. ;-)Andres Almiray
05/30/2024, 10:29 AMVampire
05/30/2024, 10:29 AMVampire
05/30/2024, 10:31 AMAndres Almiray
05/30/2024, 10:32 AMVampire
05/30/2024, 10:32 AMsubprojects { ... }
or allprojects { ... }
or project(...) { ... }
or any other means does not really matterVampire
05/30/2024, 10:33 AMbuildSrc
or an included build, for example implemented as precompiled script plugins.Andres Almiray
05/30/2024, 10:33 AMVampire
05/30/2024, 10:40 AMAndres Almiray
05/30/2024, 10:45 AMVampire
05/30/2024, 10:47 AMAndres Almiray
05/30/2024, 10:47 AMVampire
05/30/2024, 10:49 AMAndres Almiray
05/30/2024, 10:50 AMVampire
05/30/2024, 10:51 AMI don’t use Kotlin
You should though. :-) You immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio. :-)
Vampire
05/30/2024, 10:52 AMYeah, like using Kotlin where Groovy works
Sure, using Groovy is one option too. For various reasons a bad one in my personal opinion, but yes. But it was pointless to me to suggest that, as he is aware that that works. :-)
Andres Almiray
05/30/2024, 11:15 AMVladimir Orany
05/30/2024, 11:56 AMlibs
via the rootProject
. I’m not sure if this is bug or feature 🤷♂️
this is all about DX vs performance. having the predefined behavior for a projects in certain directories makes it much easier for developer to add new projects. this is mostly done by a bunch of plugins but I’m trying to avoid managing the dependencies within plugins. after all experiments with trying to use BOMs causing instability in the dependency graph, we’ve happily switched to version the catalogs.
this is the valid snippet
gradleProjects {
subprojects {
dir("libs") {
repositories {
mavenCentral()
}
dependencies {
add("implementation", rootProject.libs.commonsLang)
}
}
}
}
and I can use gradleProjects { }
as well, I’m not sure why it haven’t worked before when I was initially trying (but it doesn’t fix the libs
access)
What feels wrong is that in Kotlin DSL one trust what’s IntelliJ is showing because of the strong typing. and then it looks you can access the property and you actually can’t.Vampire
05/30/2024, 1:56 PMhaving the predefined behavior for a projects in certain directories makes it much easier for developer to add new projects.Really? There is one line difference. If you put the common configuration into some convention plugin, then the developer that adds a new project applies that one plugin and the conventions are in effect in his project. Imho, that is not so much boilerplate. But use whatever makes you happy and works for you. As I said, I just give recommendations.
after all experiments with trying to use BOMs causing instability in the dependency graph, we’ve happily switched to version the catalogs.BOMs / platforms and version catalogs are not mutually exclusive. They are complementing features with different use-cases. A version catalog is a list of coordinates and versions that per-se have not any influence on dependency resolution. But they are available to pick from using
libs...
with having the coordinates and versions declared centrally in one place.
And additionally when using a TOML as version catalog, just changing a version does not make all the tasks out-of-date
like the legacy alternatives like having a class with constants in buildSrc
would do.
A BOM or platform is for influencing version resolution.
It effectively is a bundle of version constraints on which you can depend on a whole.
You can use it to upgrade transitive dependencies without uncleanly depending on them.
You can use it to downgrade transitive dependencies without depending on them.
...
And you can use the version catalog entries in the platform project too as those are only a coordinate / version tuple.
Also, if what you are after is just adding multiple dependencies easily, you can also simple define a bundle in the version catalog.
Then the project could easily depend on the whole bundle at once.
And the repository declaration is imho anyway better kept in dependencyResolutionManagement [ repositories { ... } }
in the settings script,
where it then is used uniformly for all projects, especially if you forbid project-specific repositories being added. 🙂
What feels wrong is that in Kotlin DSL one trust what’s IntelliJ is showing because of the strong typing. and then it looks you can access the property and you actually can’t.That's one of the effects of the bad practice I mentioned. In Kotlin DSL build scripts you only get type-safe accessors for things that Gradle knows will be there at runtime. That means, the
plugins { ... }
block of that build script is extracted, applied to a dummy project, and then this project is investigated about what the plugins added.
For these things that were added (and are supported by the algorithm) the type-safe accessors are generated and can be used in the build script.
So by doing this cross-project configuration, the accessor is present when compiling that build script.
But then it fails at execution time as you do not evaluate the accessor against the project where Gradle knew it will work,
but against the subproject where it does work.
So actually the IDE is correct in not showing an error, as the accessor itself is present and can be compiled,
it is just that at runtime it fails as it does not find what it expects to find.Vladimir Orany
05/31/2024, 9:05 AMlibs
is not recognized at all (not even on the rootProject
)
https://github.com/musketyr/gradle-kotlin-libs-issue/blob/chore/precompiled-plugins/buildSrc/src/main/kotlin/common-dependencies.gradle.ktsVampire
05/31/2024, 9:08 AMlibs...
accessors you indeed cannot use unless https://github.com/gradle/gradle/issues/15383 is implemented or you use the hack-around that I documented in https://github.com/gradle/gradle/issues/15383#issuecomment-779893192.
Without the hack-around you would need to use the string-y API like versionCatalogs.named("libs").findLibrary("commonsLang")
and so onVladimir Orany
05/31/2024, 9:14 AMVladimir Orany
05/31/2024, 9:14 AMVampire
05/31/2024, 9:18 AMlibrary.gradle.kts
, and the file is not used as you configure build.gradle.kts
as build script in your settings script.
And you should add a namespace to your precompiled script plugin, either by adding a package
statement, or by renaming the file.
Plugin IDs without namespace (i.e. without dot) should be reserved to the built-in plugins.Vampire
05/31/2024, 9:18 AMdiff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 2a80c21..876c922 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -1,6 +1,5 @@
plugins {
`kotlin-dsl`
- `kotlin-dsl-precompiled-script-plugins`
}
repositories {
diff --git a/buildSrc/src/main/kotlin/common-dependencies.gradle.kts b/buildSrc/src/main/kotlin/common-dependencies.gradle.kts
index bfea837..33d2397 100644
--- a/buildSrc/src/main/kotlin/common-dependencies.gradle.kts
+++ b/buildSrc/src/main/kotlin/common-dependencies.gradle.kts
@@ -1,3 +1,5 @@
+package my
+
plugins {
`java-library`
}
@@ -7,5 +9,5 @@ repositories {
}
dependencies {
- implementation(libs.commonsLang)
+ implementation(versionCatalogs.named("libs").findLibrary("commonsLang").orElseThrow(::AssertionError))
}
diff --git a/libs/library/library.gradle.kts b/libs/library/library.gradle.kts
index 1017f50..2d88eec 100644
--- a/libs/library/library.gradle.kts
+++ b/libs/library/library.gradle.kts
@@ -1,3 +1,3 @@
plugins {
- `commond-dependencies`
+ my.`common-dependencies`
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 7874056..15d819a 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -9,5 +9,5 @@ rootProject.name = "gradle-kotlin-libs-issue"
include("library")
project(":library").projectDir = file("libs/library")
-project(":library").buildFileName = "build.gradle.kts"
+project(":library").buildFileName = "library.gradle.kts"
Vladimir Orany
05/31/2024, 10:04 AM