# Using variants and toolchains together

**URL:** <https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658>\
**Category:** Help/Discuss\
**Created:** [January 13, 2023, 3:22pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658 "2023-01-13T15:22:18Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![DanySK](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/danysk/32/7700_2.png) [@DanySK](https://discuss.gradle.org/u/DanySK)\
**Post date:** [January 13, 2023, 3:22pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/1 "2023-01-13T15:22:18Z")

</div>

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:

```gradle
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?

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [January 14, 2023, 3:14am UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/2 "2023-01-14T03:14:08Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![DanySK](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/danysk/32/7700_2.png) [@DanySK](https://discuss.gradle.org/u/DanySK)\
**Post date:** [January 14, 2023, 12:46pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/3 "2023-01-14T12:46:11Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [January 15, 2023, 12:19pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/4 "2023-01-15T12:19:48Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [January 17, 2023, 3:35pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/5 "2023-01-17T15:35:10Z")

</div>

You could define an additional [JavaCompile - Gradle DSL Version 7.6](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html) task using examples from [Using toolchains Sample](https://docs.gradle.org/current/samples/sample_jvm_multi_project_with_toolchains.html) :

```gradle
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:

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

```

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

```gradle
JavaLanguageVersion.of(key)

```

---

<div class="post-metadata">

**Author:** ![DanySK](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/danysk/32/7700_2.png) [@DanySK](https://discuss.gradle.org/u/DanySK)\
**Post date:** [January 17, 2023, 3:56pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/6 "2023-01-17T15:56:12Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [January 17, 2023, 4:01pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/7 "2023-01-17T16:01:00Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![gavenkoa](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/gavenkoa/32/6263_2.png) [@gavenkoa](https://discuss.gradle.org/u/gavenkoa)\
**Post date:** [January 21, 2023, 3:32pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/8 "2023-01-21T15:32:57Z")

</div>

Is there an example of variant redefinition for JavaCompiler version?

[Modeling feature variants and optional dependencies (gradle.org)](https://docs.gradle.org/current/userguide/feature_variants.html)

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

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [January 21, 2023, 6:38pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/9 "2023-01-21T18:38:14Z")

</div>

> 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](https://github.com/gradle/gradle/issues/16908)  
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.

---

<div class="post-metadata">

**Author:** ![serpro69](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/serpro69/32/9792_2.png) [@serpro69](https://discuss.gradle.org/u/serpro69)\
**Post date:** [December 20, 2023, 2:03pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/10 "2023-12-20T14:03:30Z")

</div>

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”

---

<div class="post-metadata">

**Author:** ![serpro69](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/serpro69/32/9792_2.png) [@serpro69](https://discuss.gradle.org/u/serpro69)\
**Post date:** [December 21, 2023, 11:18am UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/11 "2023-12-21T11:18:54Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 21, 2023, 1:11pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/12 "2023-12-21T13:11:28Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![DanySK](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/danysk/32/7700_2.png) [@DanySK](https://discuss.gradle.org/u/DanySK)\
**Post date:** [December 21, 2023, 1:49pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/13 "2023-12-21T13:49:55Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 21, 2023, 2:28pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/14 "2023-12-21T14:28:22Z")

</div>

Here a simple example:

```kts
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:

```kts
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.

---

<div class="post-metadata">

**Author:** ![DanySK](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/danysk/32/7700_2.png) [@DanySK](https://discuss.gradle.org/u/DanySK)\
**Post date:** [December 21, 2023, 2:57pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/15 "2023-12-21T14:57:41Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [December 21, 2023, 3:00pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/16 "2023-12-21T15:00:52Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![serpro69](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/serpro69/32/9792_2.png) [@serpro69](https://discuss.gradle.org/u/serpro69)\
**Post date:** [December 21, 2023, 6:17pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/17 "2023-12-21T18:17:41Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![TWiStErRob](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/twisterrob/32/7261_2.png) [@TWiStErRob](https://discuss.gradle.org/u/TWiStErRob)\
**Post date:** [April 15, 2025, 10:35pm UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/18 "2025-04-15T22:35:06Z")

</div>

First off thanks @Vampire for providing some code guidance, very appreciated.

I just wanted to post a quick update: the first code block shown gives a deprecation warning since Gradle 8.5, so we can only use the second block.

> The ‘java9’ feature was created using the main source set. This behavior has been deprecated. This will fail with an error in Gradle 9.0. The main source set is reserved for  
> production code and should not be used for features. Use another source set instead. Consult the upgrading guide for further information: [Upgrading your build from Gradle 8.x to the latest](https://docs.gradle.org/8.14-rc-1/userguide/upgrading_version_8.html#deprecate_register_feature_main_source_set)

* * *

I also got

> \> Task :sub:proj:generatePomFileForMyNamePublication  
> Maven publication ‘myName’ pom metadata warnings (silence with ‘suppressPomMetadataWarningsFor(variant)’):
> 
> - Variant java9ApiElements:
> - Declares capability foo:bar:1.0 which cannot be mapped to Maven
> 
> - Variant java9RuntimeElements:
> - Declares capability foo:bar:1.0 which cannot be mapped to Maven
> 
> These issues indicate information that is lost in the published ‘pom’ metadata file, which may be an issue \> if the published library is consumed by an old Gradle version or Apache Maven.  
> The ‘module’ metadata file, which is used by Gradle 6+ is not affected.

Which is suggesting this code to be present in some form:

```gradle
publishing {
	publications {
		named<MavenPublication>("myName") {
			suppressPomMetadataWarningsFor("java9ApiElements")
			suppressPomMetadataWarningsFor("java9RuntimeElements")
		}
	}
}

```

is the capability required for the feature? according to [this line](https://docs.gradle.org/current/userguide/feature_variants.html#:~:text=By%20default%2C%20a%20variant%20provides%20a%20capability%20corresponding%20to%20the%20GAV%20coordinates%20of%20its%20component) it might be what you set by default:

> By default, a variant provides a capability corresponding to the GAV coordinates of its component

---

<div class="post-metadata">

**Author:** ![Vampire](https://sea1.discourse-cdn.com/gradle/user_avatar/discuss.gradle.org/vampire/32/9082_2.png) [@Vampire](https://discuss.gradle.org/u/Vampire)\
**Post date:** [April 16, 2025, 8:34am UTC](https://discuss.gradle.org/t/using-variants-and-toolchains-together/44658/19 "2025-04-16T08:34:16Z")

</div>

> I just wanted to post a quick update: the first code block shown gives a deprecation warning since Gradle 8.5, so we can only use the second block.

The replacement for the first block can be found at [Please revert the deprecation to declare additional feature variants on the `main` source set · Issue #29455 · gradle/gradle · GitHub](https://github.com/gradle/gradle/issues/29455) 🙂

> I also got

Yeah, that’s normal when using feature variants unless you suppress the warning.  
Maven POMs just cannot hold information about variants, that’s why the Gradle Module Metadata was introduced.

> is the capability required for the feature?

Any feature needs a capability.  
And if two features have the same capability and that is the only thing in which they differ, Gradle cannot choose which to use.  
In the examples above both feature variants have the same capability, because the Java version attribute will differ and thus be automatically selected when the default capability is requested by a simple dependency declaration.  
With other feature variants like “mysql implementation”, “postgres implementation”, and so on, all attributes would be the same and you would use the capability to choose which variant to select.  
Similar to requesting a jar with classifier from Maven, but with dependency information.

So yes, the capability is always necessary, and it is here not really the capability that cannot be mapped, but the POM can simply not hold the information about the feature variants properly.

> according to this line it might be what you set by default:

I think the sentence is either misleading or plainly wrong.  
The default capability of the default feature is the GAV of the component.  
The default capability of an additional feature has a dash and the feature identifier suffixed to the A.
