Where to configure precompiled script plugin toolchain repositories and toolchains?

Greetings, I’m a little confused on where toolchains and toolchain repositories need to be configured with precompiled script plugins.

The version 8 release notes say, “Precompiled script plugins now use the configured Java Toolchain”. They also say, “In Gradle 8.0, there is no longer a default toolchain provisioner. You have to declare at least one Java Toolchain repository explicitly.” The manual shows how to do this via a settings plugin.

For a project with a root build, and a “buildSrc” (or plain old “build-logic” included build) that contains precompiled script plugins, do I add the settings plugin to the root settings.gradle.kts, buildSrc/settings.gradle.kts, or both?

Also, the docs show configuring a toolchain in the precompiled script plugin itself (in the “myproject.java-conventions.gradle.kts” example). That makes sense to me for configuring the toolchain of the projects the precompiled script plugin is applied to. Does it also configure the toolchain for building the precompiled script itself? I would have guessed that’d need to be declared in buildSrc/build.gradle.kts, thereby configuring the toolchain used for building all the precompiled scripts in buildSrc.

1 Like

But as far as I understood, you don’t actually need to have any toolchain provisioner configured if you don’t want auto-provisioning.
Then the toolchain that the build requires needs to be already installed on the machine trying to build.
If you want toolchains auto-provisioned (downloaded and unpacked to <GRADLE_USER_HOME>/jvms, then you now need to configure a provisioner when before there was one built-in for AdoptOpenJdk and Temurin.

Whether you need or want to define toolchains and provisioners on the project building the precompiled script plugins or / and on the main build, is independent of each other, as those are separate builds. The same way you need to declare repositories in both, you also need to configure the toolchain provisioner for both if you want for both builds toolchains auto-provisioned.

Even if you configure the same toolchain for the build logic build and for the main build, you should not rely on only having the toolchain provisioner on the build logic build. It will work as the build logic build will already auto-provision the toolchain, even if all tasks are up to date. But you will get a deprecation warning when the main build is evaluated saying:

Using a toolchain installed via auto-provisioning, but having no toolchain repositories configured. This behavior is deprecated. Consider defining toolchain download repositories, otherwise the build might fail in clean environments; see Toolchains for JVM projects

It will of course not fail in clean environments, as the included build provisioned and will provision the toolchain, at least currently. But maybe in the future it will only be provisioned when it is actually needed? Or maybe it is a false positive that should be fixed? I don’t know, but to be safe, just apply the foojay plugin to all builds where you want auto-provisioning to happen.

1 Like

Thanks for the explanation. I do want auto-provisioning, so I added the settings plugin to my root ‘settings.gradle.kts’, ‘build-logic/settings.gradle.kts’, and ‘platforms/settings.gradle.kts’.

It seems I can’t use version catalog alias() declarations for settings plugins, so I’m duplicating the foojay-resolver-convention version number in all three places. That’s unfortunate, but a topic for another thread perhaps.

One thing I’m still not clear on is where to configure the toolchain itself for precompiled script plugins vs. the projects they’re applied to. I added the configuration to my ‘build-logic/build.gradle.kts’ to cover my included build-logic project and the precompiled scripts themselves, my ‘build-logic/src/main/kotlin/org.sdkotlin.buildlogic.kotlin-project.gradle.kts’ project convention script to cover all the subprojects of my root project it’s applied to, and added a ‘platforms/build.gradle.kts’ to cover my platforms included build and its java-platform subprojects. Is that all correct, necessary, and idiomatic?

Thanks for the explanation. I do want auto-provisioning, so I added the settings plugin to my root ‘settings.gradle.kts’, ‘build-logic/settings.gradle.kts’, and ‘platforms/settings.gradle.kts’.

:ok_hand:

It seems I can’t use version catalog alias() declarations for settings plugins, so I’m duplicating the foojay-resolver-convention version number in all three places. That’s unfortunate, but a topic for another thread perhaps.

That’s correct, that is a hen-and-egg problem.
The version catalogs feature was designed in a way so that a settings plugin can contribute version catalogs.
But that of course means that by the time the settings plugins are applied the version catalogs are not yet available for usage.
The only thing you could do is to use some TOML parser or pure String reading to extract the version number from the version catalog file.

One thing I’m still not clear on is where to configure the toolchain itself for precompiled script plugins vs. the projects they’re applied to. I added the configuration to my ‘build-logic/build.gradle.kts’ to cover my included build-logic project and the precompiled scripts themselves, my ‘build-logic/src/main/kotlin/org.sdkotlin.buildlogic.kotlin-project.gradle.kts’ project convention script to cover all the subprojects of my root project it’s applied to, and added a ‘platforms/build.gradle.kts’ to cover my platforms included build and its java-platform subprojects. Is that all correct, necessary, and idiomatic?

Sounds like it, yeah.
Unless you extract that to yet another convention plugin that you then can apply in your builds.
But usually you would not want the same value.
By setting a Java 17 toolchain for the build-logic build, you are requiring to run Gradle with Java 17 itself.
Because the build-logic build could well run on Java 8, using the toolchain to build the convention plugins, but if you then try to apply those plugins to your main build, it will fail with incompatible class file version as Gradle is still running on Java 8 then.

1 Like

Ah, I think I may have been overestimating the scope of provisioned toolchains. I was presuming the wrapper would somehow auto-provision and fork the build VM based on your toolchain configuration. I see now that’s not a thing: [PROPOSAL] Allow Gradle to run without a JDK installed · Issue #2508 · gradle/gradle · GitHub.

So it’s on the user to launch Gradle with a Java version equal to or greater than the one required for the Gradle build itself, and then Gradle can auto-provision newer Java versions as necessary for the build output target version if configured to do so via the toolchain support.

I believe I’m following now that there’s value in toolchain configuration for the build-logic and platform builds just to set an explicit target version for their output. I tried setting them to “8”, but ran into an issue where the Dependency Analysis Plugin seems to be compiled for Java 11 now. I’m fine with that as a minimum requirement for the build itself in my case.

It’ll be really handy if issue #2508 ever does get implemented for bootstrapping the build toolchain.

1 Like

Indeed, but probably not the biggest priority unless you use some of the custom hackery mentioned in that issue afair.
I doubt it will have much traction within Gradle itself unless someone comes up with a clever PR.

1 Like

Thanks for the useful discussion.

The proposed solution of adding the foojay plugin to both settings.gradle.kts files doesn’t seem to work in my case when using a buildSrc project (with Gradle 7.6.1). Gradle either complains that the same plugin is already on the classpath (if a version was provided) or that the plugin could not be found (if no version was provided).

It seems like a convention plugin in the buildSrc project which uses java.toolchain causes Gradle to emit the “Java toolchain auto-provisioning enabled, but no java toolchain repositories declared by the build” warning even if buildSrc itself is not using toolchains.

Can you share a short MCVE showing your setup?

Sure:

buildSrc fails to build due to:

Error resolving plugin [id: 'org.gradle.toolchains.foojay-resolver-convention', version: '0.5.0']
> The request for this plugin could not be satisfied because the plugin is already on the classpath with an unknown version, so compatibility cannot be checked.

But the “Java toolchain auto-provisioning enabled” warning I was on about before is due to me using java.toolchain in the buildSrc’s build.gradle.kts file and the plugin not applied. Sorry if that’s a false alarm.

I think I’m fine with just dropping toolchain usages from buildSrc.

Besides that you don’t need to apply the kotlin-dsl-precompiled-script-plugins as the kotlin-dsl already applies it, and that the org.gradle.toolchains.foojay-resolver-convention plugin is a settings plugin, so needs to be applied in the settings script, this seems to be another limitation of the special handling of buildSrc. Use an included build like build-logic or gradle/build-logic or whatever instead and it works just fine.

Nevertheless this feels like awkward behavior that you should report as bug and post the link here for others to follow.

Thanks for the tips @Vampire, I’ve applied them.

1 Like