jjohannes/gradle-jpms-experiments

Publish variants in module-java8

Closed this issue · 13 comments

Hi Jendrik,

could you modify the sample to show how to publish the jdk8 and jdk9 variants?

Thanks in advance

Hi @elect86

The sample already sets up a Multi-Release (MR) Jar. So Gradle's default publishing setup will publish that Jar. You only need to add something like this:

plugins {
    ...
    id("maven-publish")
}

publishing {
    publications.create<MavenPublication>("library") {
        from(components["java"])
    }
    repositories.maven {
        url = uri(rootProject.layout.buildDirectory.dir("local-repo"))
    }
}

https://github.com/jjohannes/gradle-jpms-experiments/blob/master/buildSrc/src/main/kotlin/de.jjohannes.javamodules.java-library.gradle.kts

Is that what you are looking for?

There is also the option to publish to separate Jars with Gradle module metadata. In that case I would do the whole setup a bit differently.

Yeah, you are right, I see in the produced jar module-info.class under META-INF/versions/9 indeed, thanks

How would you do the setup for having different Jars?

Also MR jars doesnt seem a great idea

In that case, I would suggest to register a separate source set. Usually, each source set becomes a separate jar and is published as a separate variant. Now we have a structure like this:

└── src
    └── main
        ├── java
        └── java9

Wit the separate source set the structure is like this

└── src
    └── main
    │   └── java
    └── java9
        └── java

On top of the source set you can then register a feature variant. With that the jar is automatically published as a separate variant (and with a classifier in the jar name).

The setup would look like this:

val java9 = sourceSets.create("java9")
java {
    registerFeature(java9.name) {
        usingSourceSet(java9)
        capability(project.group.toString(), project.name, project.version.toString()) // maybe you want this if the variants are mutually exclusive
    }
}

But then it depends on a lot of things what else you need to configure:

  • Do you have completely separate jars?
  • Or is the Java 9 Jar an add-on of the Java 8 Jar (would not work well with Java Modules)?
  • If the Jars are independent, do they still share code? (In that case, maybe you need to adjust the Jar tasks to include classes from both source sets.)
  • ...

No, Java Modules is a requisite, so I'd say completely separate jars

I'm interested in having the artifacts named by default compiled with the latest jdk, so in this case 15, and then on top one version for the last couple of LTS, that is 11 and 8 (-jdk11 and -jdk8?)

Would have you done also something like that or would you have opted for a different naming strategy for completely separate jars?

Do you need to add additional code to the -jdk11 etc. Jars or do you just want to compile the same code with different JDKs?

That's interesting indeed, I'd like to see both options, if possible

I added an example for this setup. The main source set is shared by all variants but each also has a separate source set for additional variant specific sources (Like the module file, which can't be shared as the Java 8 variant won't recognise it).

https://github.com/jjohannes/gradle-jpms-experiments/blob/master/module-multi-jdk-jars/build.gradle.kts

That's exactly what I was eager to see.. thanks

It's actually quite astonishing to see how compact is the build script after all.. gradle godness :)

One thing though: I see you declare three sourceSets in the script, java8, java11 and java15, but in the code we actually have main, java11 and java15, isn't there an error?

May I bother you one last time? :p

How this would change with a kotlin-only project?

src

  • main // jdk8
  • jdk9 // extending main sourceset, almost exclusively for the module-info.class

Three variants again: 8, 11 and 15 (or 16 now..)

One thing though: I see you declare three sourceSets in the script, java8, java11 and java15, but in the code we actually have main, java11 and java15, isn't there an error?

All source sets are used. The folder for the "java8" source set is just empty, because we do not add anything on top of the "main" sources. Creating the source set also creates the "compile task" etc. which we use.

How this would change with a kotlin-only project?

I would do the same setup for a "kotlin" project. And configure KotlinJvmCompile similar to what I did for JavaCompile.

Here is how you can use toolchains there currently: https://docs.gradle.org/current/userguide/toolchains.html#integration_with_tasks_relying_on_a_java_executable_or_java_home

To access the "kotlin" part of the source set you can define an extension like this (unfortunately not yet generated by Gradle):

val SourceSet.kotlin: SourceDirectorySet
    get() = withConvention(KotlinSourceSet::class) { kotlin }

Then you should be able to configure something like: source = sourceSets.main.get().kotlin + sourceSet.kotlin for KotlinJvmCompile.

Ok, I'll try that

Will that be maven compatible though? I guess no, right?

@elect86 in Maven you will have to explicitly select a variant via classifier in the dependency. The classifier corresponds to the name of the feature variant (e.g. java11).

KotlinJvmCompile has no source...

Edit: KotlinCompile looks right what you meant or?

Other problems with the kotlin attempt:

  • -jdk8 variant is not created unless I explicitly create a new sourceset for that (I'd like to re-use main)
  • both published jar are built with java 1.6, if I try to explicitly set the jvmTarget accordingly, only the one targeting 1.8 gets compiled so, the other one stays always on 1.6