Modules with platform specific parts

Hervé Guillemet hg at apteryx.fr
Wed Sep 15 16:09:42 UTC 2021


Hi,

Thank you Johan for starting this discussion. The current situation
is indeed problematic and a (de facto or not) standard should emerge.

IMO, beyond being easy to use, the selected mechanism should allow to
build multi-platform images (for instance for MacOSX on both aarch64
and x86_64).

 > 1. There is a single module (i.e. only one Module). All code, no
 > matter on what platform or in which layer, will report the same value
 > for class.getModule().getName(). This would lead to a single
 > "javafx.graphics" conceptual module, but there will be a number of
 > physical module files that are different (with different class files
 > and native code). Those different modules should obvisouly be mutually
 > exclusive in a runtime.

This option doesn’t allow multi-platform images. Also the duplication of
all common Java classes in each platform jars is not ideal.

 > 2. There are 2 modules: the platform-independent Java code goes into
 > one module (let's call that javafx.graphics.api) and a second module
 > is named javafx.graphics.platform and contains the platform-dependent
 > Java code and the native code.
 > In this approach, developers use the javafx.graphics.api module to
 > compile against, and at runtime the javafx.graphics.platform module is
 > required. Again, that second module will have a number of different
 > physical implementations. As an extension to this, we might add the
 > Service Provider Interface approach for loading platform-specific
 > modules/bits at runtime.

This option doesn’t allow multi-platform images.

 > 3. We create one module for each platform. There is no
 > "javafx.graphics" module in this case, but there is a
 > "javafx.graphics.linux.aarch64" module for example.
 > Doing so, there is a tight coupling between one conceptual module and
 >  one physical module. A clear drawback of this is that this is a real
 > challenge at compiletime. Developers (who are only using generic
 > API's) need to compile against a platform-specific module.

Indeed. The building of the module path can be automatized with
Maven on another build system, but the module descriptor should also
be changed for each platform you are building for.
Multi-platform images won’t work either since packages would be split,
and common classes are also duplicated. I don’t see any advantage compared
to option 1.

 > 4. We use 2 artifacts: an "empty" one and then a number of
 > implementation specific ones. The difference with option 2 is that the
 > empty "module" exists solely for the purpose of tools, which can
 > detect what implementation specific module(s) need to be loaded at
 > compile/runtime.

Like mentioned in your referenced links, this causes problems.
I haven’t worked with OpenJFX 17 but with OpenJFX 11-16, and to be
able to build JLink images I had either to use the JavaFX maven
plugin (which takes care of empty modules but one may wish to use
alternate plugins because of some limitations [1-4]) or explicitly
depend directly on the implementation-specific artifact, and remove
indirect dependency towards the empty modules with maven configurations
like :

         <dependency>
             <groupId>org.openjfx</groupId>
             <artifactId>javafx-controls</artifactId>
             <classifier>${javafx.platform}</classifier>
             <version>16</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.openjfx</groupId>
                     <artifactId>*</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>


I’m adding the option currently used by JavaCPP:

5. There is one main/API module (javafx.graphics or
javafx.graphics.api), any number of platform-specific modules
(javafx.graphics.linux.aarch64, etc…), and an empty module
(javafx.graphics.platform) similar to the current one in OpenJFX.
There is a 1-1 coupling between modules and artifacts.
Users declares maven dependencies towards the empty module but the
module descriptor "requires" the API module. The build system takes
care or populating the module path according to the value of a
property (javafx.platform) that could be multi-valued.

Compared to option 2, it allows for multi-platform builds, but
users must take care of the javafx.platform property and the platform
modules must be brought into the module graph by an --add-modules
command line argument.
The special value ALL-MODULE-PATH can be used to benefit from the path
calculation of the build system.

Slighlty OOT, I believe that the selected mechanisms should provide a
way to ship the native libraries pre-extracted in a JLink image. There
is no technical reason from the user point of view to slow down the
first run with library extraction and to silently pollute the home
directory with a never cleaned .javacpp or .openjfx directory, when the
library could simply come pre-extracted in the image. That means that
the artifact with the native libraries have Maven scope “provided”, or
that exists some stripped version of the artifact without the libraries
if the artifact must also contain Java classes or other resources.

I finally link another OOT but close issue that was faced by JavaCPP
users trying to run their App within an OSGi framework and that shows
how tricky it can be to enumerate the bundled native libraries and
extract them to the file system in some not-mainstream cases, and that
we do need some common standard solution : [5].

--
Hervé Guillemet


[1] https://github.com/javafxports/openjdk-jfx/issues/387
[2] https://github.com/openjfx/javafx-maven-plugin/issues/91
[3] https://github.com/openjfx/javafx-maven-plugin/pull/92
[4] https://github.com/openjfx/javafx-maven-plugin/pull/109
[5] https://github.com/bytedeco/javacpp/pull/511



More information about the jigsaw-dev mailing list