If we do eventually have to go with multiple, differently-named modules, this would seem a sensible approach. I think it's roughly what Johan was alluding to when he spoke of extending option 2 to use an SPI. In this case, we would refactor the platform-specific code into different named modules (that would be a fair bit of work, but let's ignore that for now), and the existing javafx.graphics module would become the API module.

Somewhat related to this: Of the four options, I think option 3 is a non-starter. The application developer should never have to "requires", or even know about the existence of, platform-specific modules. This means that we either need to stick with different physical modules all of which are named the same, or else platform-specific modules that are loaded using some sort of SPI, so that they don't have to be known at compile / link time.

-- Kevin


On 9/15/2021 2:15 AM, Michał Kłeczek wrote:
Hi,

Hi,

There is another option:

API module and multiple platform specific modules (different names like 
javafx.graphics.linux.aarch64) that require API module and provide services.

API module does not “require” any platform specific module but uses 
ServiceLoader to find a proper implementation at runtime.

Thanks,
Michal


On 15 Sep 2021, at 10:45, Johan Vos <johan....@gluonhq.com> wrote:

Hi,

There have been discussions in the past about how to deal with
platform-specific parts (java code, native code, resources) in modules.
There is no standard for this, and afaik no recommendation. In the OpenJFX
project, we upload jars with module info to maven central, and we have
plugins for maven and gradle to deal with them at compiletime and at
runtime.

However, the lack of a standard recommendation forces us to change the
internal behavior every now and then. The latest change we made (removing
automatic module names from empty modules [1]) caused issues in the 17
release, when developers compile JavaFX modular apps [2]

Before we make many other changes, I would like to have a better view on
what would be the recommended approach, so here is my summary. Suggestions
are highly appreciated.

Let's assume we have a component that contains some
platform-independent Java code, some platform-dependent Java code, and
some platform-dependent native code. To make the example concrete,
let's use the javafx.graphics "module" from the OpenJFX project, which
contains exactly that.

There are a number of options, and before thinking about the best way
for tools do deal with this situation, it would be good to have a
recommended approach for those "hybrid" modules.

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.

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.

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.

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.

We currently use option 4, but in my opinion, option 2 would be the
better approach.

Thanks,

- Johan

[1] https://bugs.openjdk.java.net/browse/JDK-8264998
[2]
https://mail.openjdk.java.net/pipermail/openjfx-dev/2021-September/031934.html

Reply via email to