plantuml / plantuml

Generate diagrams from textual description
https://plantuml.com
Other
10.52k stars 958 forks source link

Provide `module-info.java` for PlantUML jar - make it modular #1446

Open abulka opened 1 year ago

abulka commented 1 year ago

I can't ship a Javafx app which uses Plantuml jar because the official jlink tool rejects plantuml-v1-2023-8.jar as not being modular.

There are other situations where I cannot deploy plantuml because plantuml is not a modern 'modular' jar, and thus jlink refuses to deal with it. Yes you can use plantuml jars in modular projects and plantuml becomes a 'automatic module' in those projects just fine. But jlink rejects automatic modules too.

Describe the solution you'd like Provide the necessary module-info.javafor PlantUML jar - make it modular.

Describe alternatives you've considered I've tried 'injecting' a module-info.java into the jar. Its not clear if the dependencies generated by my hack are good enough, and I worry that jlink will generate a custom JRE uneccessarily big (jlink seemed to generate a 400Mb JRE just because my hacked PlantUML.jar was included, up from 40Mb), unless someone who knows plantuml generates the proper module-info.java.

I've also tried injecting module-info.java into the generated grammar jars - however this caused Java The hierarchy of the type is inconsistentJava(16777543) problems - which have stopped all progress. See https://github.com/antlr/grammars-v4/issues/3505

Additional context Now that the JRE is no longer included with the JDK and the only way to generate a JRE runtime is with jlink - which rejects automatic modules and needs real modules. I believe PlantUML jars need to be modernised to keep pace with Java.

I believe adding module-info.java won't affect older normal, non-modular use cases. It just provides the necessary info for cases where you are using a modular Java project.

See also Forum Post re this https://forum.plantuml.net/13598/using-plantuml-jar-with-javafx?show=17924#a17924

pvlasov commented 1 month ago

1.2024.7 has a strange maven-module.class (javap'd below). Eclipse can't open it. It is not found in the source tree. It broke my build. 1.2024.6 also broke my build due to some conflict with flexmark. So, I have to stay on 1.2024.5. Maybe you are building a fat jar and this module info got into your fat jar from one of dependencies?

javap module-info.class
Compiled from "module-info.java"
module org.checkerframework.checker.qual {
  requires java.base;
  requires static java.compiler;
  requires static java.desktop;
  requires static jdk.compiler;
  exports org.checkerframework.checker.builder.qual;
  exports org.checkerframework.checker.calledmethods.qual;
  exports org.checkerframework.checker.compilermsgs.qual;
  exports org.checkerframework.checker.fenum.qual;
  exports org.checkerframework.checker.formatter.qual;
  exports org.checkerframework.checker.guieffect.qual;
  exports org.checkerframework.checker.i18n.qual;
  exports org.checkerframework.checker.i18nformatter.qual;
  exports org.checkerframework.checker.index.qual;
  exports org.checkerframework.checker.initialization.qual;
  exports org.checkerframework.checker.interning.qual;
  exports org.checkerframework.checker.lock.qual;
  exports org.checkerframework.checker.mustcall.qual;
  exports org.checkerframework.checker.nullness.qual;
  exports org.checkerframework.checker.optional.qual;
  exports org.checkerframework.checker.propkey.qual;
  exports org.checkerframework.checker.regex.qual;
  exports org.checkerframework.checker.signature.qual;
  exports org.checkerframework.checker.signedness.qual;
  exports org.checkerframework.checker.tainting.qual;
  exports org.checkerframework.checker.units.qual;
  exports org.checkerframework.common.aliasing.qual;
  exports org.checkerframework.common.initializedfields.qual;
  exports org.checkerframework.common.reflection.qual;
  exports org.checkerframework.common.returnsreceiver.qual;
  exports org.checkerframework.common.subtyping.qual;
  exports org.checkerframework.common.util.count.report.qual;
  exports org.checkerframework.common.value.qual;
  exports org.checkerframework.dataflow.qual;
  exports org.checkerframework.framework.qual;
}
arnaudroques commented 1 month ago

It is probably related to ELK (see https://plantuml.com/en/elk )

Now that the JRE is no longer included with the JDK and the only way to generate a JRE runtime is with jlink - which rejects automatic modules and needs real modules. I believe PlantUML jars need to be modernised to keep pace with Java.

I believe adding module-info.java won't affect older normal, non-modular use cases. It just provides the necessary info for cases where you are using a modular Java project.

I am really not familiar with module-info.java so your help is highly requested here :-) Is it something you could contribute on? That would be very nice.

The only constraint we have is that we still target Java 8 in our build, because some people are still there.

pvlasov commented 1 month ago

Compilation problem solved - remove all module-info.class files from the jar and add a proper module-info.class to the root of the jar! I did not try to run it. Also I did not try with Java 8. I hope it would just ignore module-info.class in the jar root.

Initially I thought that what was needed was a multi-release jar with module-info.class under META-INF/versions/9 folder.

In the plantuml-epl-1.2024.7 jar there is module-info.class in this folder:

open module com.google.errorprone.annotations@2.28.0 {
  requires java.base;
  requires java.compiler;
  exports com.google.errorprone.annotations;
  exports com.google.errorprone.annotations.concurrent;
}

And there is also module-info.class in the root of the jar - see my first comment.

This is what I did:

I used https://github.com/Nasdanika/plantuml-diagram-generator/releases/tag/maven-2023.12.1 to test compilation.

My module-info.java

module net.sourceforge.plantuml {

    exports net.sourceforge.plantuml;

}

Package conflict outputs module-info.java (automatic module)

[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.common.command from both net.sourceforge.plantuml and org.eclipse.emf.common
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.common.archive from both net.sourceforge.plantuml and org.eclipse.emf.common
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.plugin from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.resource from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.resource.impl from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.impl from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.namespace from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.namespace.util from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.namespace.impl from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.type from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.type.util from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.type.internal from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.xml.type.impl from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.eclipse.emf.common reads package org.eclipse.emf.ecore.util from both net.sourceforge.plantuml and org.eclipse.emf.ecore
[ERROR] module org.json reads package org.eclipse.emf.common from both net.sourceforge.plantuml and org.eclipse.emf.common
[ERROR] module org.json reads package org.eclipse.emf.common.util from both net.sourceforge.plantuml and org.eclipse.emf.common

Recommendation

So, you need to modify your build process to do something like this. I added just one export to the module info file because this is the only package I used. You would likely have to add more and maybe also add opens for reflection.

IMO in the modular world you should stay away from fat jars. If any of your API's use classes from co-packaged dependencies you'd have to add exports for those dependencies as well or remove dependencies from the jar. The second is preferred because no two modules can export the same package and as such if you export, say, com.google.errorprone.annotations projects that use that dependency directly or transitively would not compile for the best of my knowledge.

You already have multiple artifacts in Maven Central, maybe create another one - plantuml-epl-modular (I use your EPL artifact, so this is why EPL), don't package dependencies like eclipse.emf - add requires to the module definition instead.