Build variant, command line

plantuml has 2 main variants, a slim standard version, and one which allows to generate pdf. the pdf variant includes additional libraries, which can be used when on the classpath. i included this - but that seems not enough to get it built:

java {
	...
	registerFeature("pdf") {
		usingSourceSet(sourceSets["main"])
	}
}

dependencies {
	... 
	"pdfRuntimeOnly"("org.apache.xmlgraphics:fop:2.6")
	"pdfRuntimeOnly"("org.apache.xmlgraphics:batik-all:1.14")
}

it shows up:

> gradle outgoingVariants | grep -i pdf
Variant pdfApiElements
Description = API elements for feature pdf
    - net.sourceforge.plantuml:plantuml-pdf:1.2022.1-SNAPSHOT
Variant pdfRuntimeElements
Description = Runtime elements for feature pdf
    - net.sourceforge.plantuml:plantuml-pdf:1.2022.1-SNAPSHOT

there is no task though, and doing a normal build or assemble creates the fat jar unconditionally, including the feature dependencies:

> gradle tasks | grep -i pdf
> gradle assemble
> ls -l build/libs/
total 73324
-rw-r--r-- 1 rt rt 24987267 12. Feb 11:32 plantuml-1.2022.1-SNAPSHOT.jar
-rw-r--r-- 1 rt rt 26353195 12. Feb 11:32 plantuml-1.2022.1-SNAPSHOT-javadoc.jar
-rw-r--r-- 1 rt rt 23737102 12. Feb 11:32 plantuml-1.2022.1-SNAPSHOT-sources.jar

how is this supposed to work? the build.gradle.kts

there is a slightly related question here:

Some comments from a quick look:

  • you set source encoding for JavaCompile tasks to UTF-8, but you don’t do so for the JavaDoc tasks, they also need the correct source encoding or you might get encoding problems in the generated documentation
  • you don’t allow publishing to maven local without signing configured, that’s imho bad practice, if someone wants to publish to maven local, he should be able to do without needing to set up signing
  • your sources jar cannot be built because there are duplicate entries
  • the pdf feature you define is also for source set main, so the respective dependencies are also on the runtimeClasspath for source set main that you pack into your jar
    In the POM the dependencies will be marked as optional, in the Gradle module metadata file they will only be present on the pdfRuntimeElements variant.
  • imho you shouldn’t build a fat jar, especially if you publish to a Maven repository and especially as the default jar and even worse with still having the dependencies declared in the metadata (well, you don’t have dependencies except for the pdf variant, but as soon as you add one you will have it), if someone depends on your artifact, he will get your artifact and additionally the declared dependencies and thus has the classes twice in the class path. And even if you leave out the dependencies from the metadata, if someone depends on your artifact and also adds one of the packaged dependencies separately or as transitive dependencies of another dependency he will also have the classes twice on the classpath and maybe in different versions which can generate big headaches. If you really want to publish fat jars to a Maven repository, then at least do it as a separate variant with proper attributes set that define that the dependencies are packaged within the jar. From Maven view this is then an artifact with a classifier for Gradle it is a separate variant one can explicitly request by requesting via attribute that he wants the version with packaged dependencies.
1 Like

And one more, don’t use tasks.withType<Javadoc> { ... } it is eager and defeats task configuration avoidance efforts, better use tasks.withType<Javadoc>().configureEach { ... }

1 Like

impressive list, thank you! i saw now optional dependencies, and i like how this can be stated in gradle.

the goal was not to publish a fat jar into the maven repo, but generate both, the jar for maven, and the fat jar. maybe publish the fat jar on github to make peoples live easier so they do not need to download the jars when they run it on command line to generate pdf’s. in maven, with the current pom, one can generate a jar with dependencies.

i understood your last remark, to create a variant, with proper attributes set to denote dependencies are in the jar. i guess this is what i tried to reach and hoped the 4 lines “registerFeature” would do it.

what would you recommend in gradle?

all other hints i fixed javadoc, duplicates, permit signing only when properties are there, and added optional dependencies with this PR. fascinating … as suggested in the video i put a sleep(3000) in and typed gradle help to understand task configuration avoidance:

what produces a jar and a jar including the optional dependencies is:

plugins {
	id("com.github.johnrengelman.shadow") version "7.1.2"
}

tasks.shadowJar {
	archiveAppendix.set("pdf")
	archiveClassifier.set("")
}
> gradle -q clean build \
    shadowJar \
    generateMetadataFileForMavenPublication \
    generatePomFileForMavenPublication

and it gives an error with local publish …

> gradle publishMavenPublicationToMavenLocal
Maven publication 'maven' pom metadata warnings (silence with 'suppressPomMetadataWarningsFor(variant)'):
  - Variant pdfApiElements:
      - Declares capability net.sourceforge.plantuml:plantuml-pdf:1.2022.1-SNAPSHOT which cannot be mapped to Maven
  - Variant pdfRuntimeElements:
      - Declares capability net.sourceforge.plantuml:plantuml-pdf:1.2022.1-SNAPSHOT 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.
> Task :publishMavenPublicationToMavenLocal FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':publishMavenPublicationToMavenLocal'.
> Failed to publish publication 'maven' to repository 'mavenLocal'
   > Invalid publication 'maven': multiple artifacts with the identical extension and classifier ('jar', 'null').

there is:

am not sure - currently i downgraded to shadowJar 5.2.0 to not publish the fat jar. but it does not sign the artifact for a github upload as well. hmm.

decided to not use a plugin, just a dedicated task to produce the fat jar, and upload it into github, no publish. but i’d love to sign it.

signing {
	sign(task("pdfJar"))
}

tasks.create("pdfJar", Jar::class) { .. }

when trying to sign it says “no archive task”. does the Jar:class not make it an archive task @Vampire ?

task("pdfJar") creates an eager (not using task configuration avoidance) instance of an ad-hoc task named pdfJar. You then give this task of type DefaultTask to the sign method and so it of course is not an archive task.

If you swap it around to

tasks.create("pdfJar", Jar::class) { ... }

signing {
    sign(task("pdfJar"))
}

you will thus get an error, that you try to add another task with the name pdfJar.

What you want instead is to give your already created task to the sign method, so this:

val pdfJar = tasks.create("pdfJar", Jar::class) { ... }

signing {
    sign(pdfJar)
}

Now if you consider task-configuration avoidance, you want to use register instead of create and then need to use the closure variant of sign to not trigger task realization early as it does not accept a TaskProvider:

val pdfJar = tasks.register("pdfJar", Jar::class) { ... }

signing {
    sign(closureOf<SignOperation> { sign(pdfJar.get()) })
}

And finally we can use property delegation to make it a bit nicer:

val pdfJar by tasks.registering(Jar::class) { ... }

signing {
    sign(closureOf<SignOperation> { sign(pdfJar.get()) })
}
1 Like

thank you, changed it like you suggested, and replaced the gpg part with the gradle in memory signing, with if for local. the “sing” task now got a new names, signMavenPublication and signPdfJar and gradle help does not list them. is this a bug in the sign plugin, as it does not list “sign” in gradle tasks as well?

The Sign tasks are usually not called by an end-user manually so they don’t get a category assigned and tasks without category are not shown by gradlew tasks. They are shown by gradlew tasks --all though under the “Other” category.

1 Like