Toolchain configuation for subprojects block

I’m trying to use the toolchain functionality in gradle, and I’m following this article

My code looks like this and is gradle 7.4.2:

subprojects {
    tasks.withType<JavaCompile>().configureEach {
        options.compilerArgs.add("--enable-preview")
        javaCompiler.set(javaToolchains.compilerFor {
            languageVersion.set(JavaLanguageVersion.of(17))
        })
    }
}

Interestingly, the options bit and javaCompiler bit appear to work, it’s the javaToolchains that appear to be unknown.

> Configure project :
e: /Users/user/src/alert-logic-poc/build.gradle.kts:4:26: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val TaskContainer.javaToolchains: TaskProvider<ShowToolchainsTask> defined in org.gradle.kotlin.dsl
e: /Users/user/src/alert-logic-poc/build.gradle.kts:5:13: Unresolved reference: languageVersion

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/user/src/alert-logic-poc/build.gradle.kts' line: 4

* What went wrong:
Script compilation errors:

  Line 4:         javaCompiler.set(javaToolchains.compilerFor {
                                   ^ Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
                                       public val TaskContainer.javaToolchains: TaskProvider<ShowToolchainsTask> defined in org.gradle.kotlin.dsl

  Line 5:             languageVersion.set(JavaLanguageVersion.of(17))
                      ^ Unresolved reference: languageVersion

2 errors

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1s

Any advice would be greatly appreciated.

There are two issues.

The first is, is there a reason you are trying to set the javaCompiler directly instead of just configuring the JVM toolchain in the java extension? Because that would auomatically set it for all tasks supporting it, like KotlinCompile task, JavaCompile tasks, JavaDoc tasks, …
Setting javaCompiler directly only makes sense, if you want a different toolchain for a specific task for whatever reason, like using Java 18 to generate JavaDoc to have proper code snippets support while using an older version for everything else and similar things.

The second problem is, that you should not use subprojets { ... }, it is bad practice and legacy. It introduces coupling between projects which disturbs more sophisticated Gradle features like the configuration cache or parallel execution and also makes the build harder to maintain. Better use convention plugins that you apply directly where you want their effect to be present.

As you only showed a small snippet I can only guess, but in the build script where this snippet is contained, you most probably do not apply the java plugin or something that applies it. The javaToolchains extension you are trying to use is added to the project if the java plugin is applied. The type-safe accessor you are trying to use is for Kotlin DSL only generated if this happens in the plugins { ... } block - explicitly or implicitly - of the current build script. As the accessor is not found, my assumption that it is not applied like that. You can of course get the extension by type or by name there and keep the subprojects block, but I strongly recomment you do not.

This perspective really helps. I had no idea about the subprojects block being bad form. My group has been using gradle since the pre 1.0 milestones, and we have built a bunch of bad habits I’m trying to break. I’ve done some stuff with convention plugins in the past, I’ll explore that further, because I definitely see the points you are making.

Thanks!!

This reply was helpful for me. Thanks.

As an anecdote, I tried getting the javaToolchains extension in a convention plugin which has the java plugin applied in a plugins block and that does not seem to work. Should it?

I fell back to using: extensions.getByType<JavaToolchainService>().

The jvm-toolchains plugin is adding that extension to the project.
The java-base plugin is applying the jvm-toolchains plugin.
The java plugin is applying the java-base plugin.

So yes, applying the java plugin in the plugins block of your convention plugin will give you a type-safe accessor for that extension.
And actually, 3 days ago I did exactly that and it works fine:

1 Like

Seems to have been an Intellij sync issue. This is working perfectly.
Thanks and sorry the mistake.

1 Like