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:
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.
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?
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”
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.
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.
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.
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
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:
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.