Using variants and toolchains together

Hi,
I would like to use variants to produce two versions of the same software, one targeting Java 8+ and one targeting Java 11+. I am already using the toolchains to test with multiple versions of the JDK, but I must specify:

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(8))
    }
}

how do I tell Gradle that the Java 11+ variant, besides its dependency set, also should use the Java 11 toolchain?

I’m not sure what you mean.
If you configure the toolchain like you showed, the 11+ variant should already also use the Java 8 toolchain.

Sorry, my bad. I want the Java 11 variant to use the JVM v11. I edited the original post.

Ah, makes more sense then. :slight_smile:
Iirc, you just set a different toolchain for the compile task of that variant.

You could define an additional JavaCompile - Gradle DSL Version 7.6 task using examples from Using toolchains Sample :

tasks.register("compile11", type: JavaCompile) {
    javaCompiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(8)
    }
}

so you will have tasks for different versions simultaneously. But you need to define other related tasks, like packaging or testing:

task('tests14', type: Test) {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = JavaLanguageVersion.of(14)
    }
}

Or you could utilize -P key=val and embed key into:

JavaLanguageVersion.of(key)

This would be fine if I weren’t using variants. My understanding is that variants pre-generate compilation tasks; according to @Vampire (if I got it correctly), I only need to configure the compiler for these tasks.

Yes, you got me right.
The manual shenenigans @gavenkoa suggests are not needed when using proper feature variants as the feature variants already register and wire all necessary tasks.

Is there an example of variant redefinition for JavaCompiler version?

Modeling feature variants and optional dependencies (gradle.org)

Could I use anything supported in java when I inside registerFeature closure?

Could I use anything supported in java when I inside registerFeature closure?

No.
Well, you can, but will probably configure the main things, not the feature variant as having things inside is just the same as having them outside.
I strongly recommend using the Kotlin DSL, because then you have amazingly better IDE support and can properly see what is available where.

Is there an example of variant redefinition for JavaCompiler version?

No official: Example for automatic variant aware resolution for target Java version in docs · Issue #16908 · gradle/gradle · GitHub
The additional features register additional tasks, configure these tasks as you need them.
Don’t forget to set the capability of the additional variant so that a consuming Gradle project can automatically select the correct variant just depending on the Java version.

Hi @Vampire ,
Do you by any chance have a working example that you could share? The issue you linked seems stale and hasn’t been touched for almost 2 years?
I’m struggling to find good examples of how to use variants and toolchains together to target different java versions, and the documentation hasn’t been updated properly - as you mention in your issue, in docs just says “this is possible”, but doesn’t really tell you “how”

@DanySK , maybe you also have some examples that you could share, if you’ve figured it out?
Thanks.

Basically you just need to have two feature variants with the same capability (set the one of the additional variant to the main capability) and the only difference in attributes would then be the Java version. You can list the outgoing variants with the outgoingVariants task.

No, sorry: I’ve tried a few times, and gave up in the end. It requires too much effort to get started without a template, and did not have enough time to investigate in depth.

Here a simple example:

plugins {
    `java-library`
}

java {
    registerFeature("java9") {
        usingSourceSet(sourceSets.main.get())
        capability("$group", project.name, "$version")
    }
}

val java9RuntimeElements by configurations.existing {
    attributes {
        attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 9)
    }
}

val java9ApiElements by configurations.existing {
    attributes {
        attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 9)
    }
}

val java9RuntimeOnly by configurations
dependencies {
    java9RuntimeOnly("a:b:1")
}

This declares a feature variant for Java 9 but with the same artifact as the main variant.
Gradle Java 9+ consumers will just get an additional dependency automatically.

Another example:

plugins {
    `java-library`
}

val java9 by sourceSets.creating

java {
    registerFeature("java9") {
        usingSourceSet(java9)
        capability("$group", project.name, "$version")
    }
}

val compileJava9Java by tasks.existing(JavaCompile::class) {
    javaCompiler = javaToolchains.compilerFor {
        languageVersion = JavaLanguageVersion.of(9)
    }
}

This declares also a feature variant for Java 9 but in this case with separate Java sources.
And as the variant automatically uses the Java version of the main compilation task for the attribute you do not need to set the attribute explicitly in this case, as you already set it on the compilation task.

3 Likes

I guess the first example might be the answer to the original question… I’ll try and let others know.

I would say the second example more answers the original question, but well, it depends on what you need. Whether you want different sources, different dependencies, or even both.

Thanks a lot for the examples @Vampire ! This really helps and it looks like I’ve just arrived at similar things myself. The confusing part for me was the usage of attributes and how they need to be declared and used. But overall it’s pretty much what I’ve arrived at myself after a day of searching and just trying things out with kotlin dsl (which helps with auto-completion and all).
I’m still getting some errors in my use-case, but they’re not related to this question per se, so I think I better make a different topic for that.

1 Like