bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.53k stars 1.58k forks source link

java.lang.UnsatisfiedLinkError: no jniopenblas_nolapack in java 11 #1435

Open jkolobok opened 4 years ago

jkolobok commented 4 years ago

java 11 macos 10.14.6

Warning: Could not load Loader: java.lang.UnsatisfiedLinkError: no jnijavacpp in java.library.path: [/Users/jkolobok/Library/Java/Extensions, /Library/Java/Extensions, /Network/Library/Java/Extensions, /System/Library/Java/Extensions, /usr/lib/java, .]
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jniopenblas_nolapack in java.library.path: [/Users/jkolobok/Library/Java/Extensions, /Library/Java/Extensions, /Network/Library/Java/Extensions, /System/Library/Java/Extensions, /usr/lib/java, .]
    at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2660)
    at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:827)
    at java.base/java.lang.System.loadLibrary(System.java:1871)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:1631)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.load(Loader.java:1265)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.load(Loader.java:1109)
    at org.bytedeco.openblas/org.bytedeco.openblas.global.openblas_nolapack.<clinit>(openblas_nolapack.java:12)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:398)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.load(Loader.java:1176)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.load(Loader.java:1125)
    at org.bytedeco.javacv/org.bytedeco.javacv.OpenCVFrameConverter.<clinit>(OpenCVFrameConverter.java:43)
    at org.bytedeco.javacv/org.bytedeco.javacv.OpenCVFrameGrabber.<init>(OpenCVFrameGrabber.java:95)
    at recorder/recorder.DesktopRecorder.main(DesktopRecorder.java:11)
Caused by: java.lang.UnsatisfiedLinkError: no openblas_nolapack in java.library.path: [/Users/jkolobok/Library/Java/Extensions, /Library/Java/Extensions, /Network/Library/Java/Extensions, /System/Library/Java/Extensions, /usr/lib/java, .]
    at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2660)
    at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:827)
    at java.base/java.lang.System.loadLibrary(System.java:1871)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.loadLibrary(Loader.java:1631)
    at org.bytedeco.javacpp/org.bytedeco.javacpp.Loader.load(Loader.java:1213)
    ... 9 more

Sample project: javafxsample.zip Workaround - remove module-info.java.. and it starts working...

saudet commented 4 years ago

Have you tried to add a line for requires org.bytedeco.javacv.platform?

jkolobok commented 4 years ago

Just tried

Error occurred during initialization of boot layer
java.lang.module.FindException: Module org.bytedeco.flycapture.linux.armhf not found, required by org.bytedeco.flycapture.platform

I'm on mac. not linux

saudet commented 4 years ago

Yeah, that's a limitation of JPMS. It's not possible to have optional modules.

@HGuillemet What would be your recommendation?

HGuillemet commented 4 years ago

I'm not sure to understand the problem. Thanks @jkolobok for supplying a standalone sample project, but when I run it either on linux or macos, With requires org.bytedeco.javacv or requires org.bytedeco.javacv.platform, it does work. What did I miss ?

There are some kind of optional modules in JPMS : requires static, but I'd like to understand the problem before recommending anything :)

jkolobok commented 4 years ago

ok. one more thing: I had -Djavacpp.platform=macosx-x86_64 in maven importer in intellij settings

jkolobok commented 4 years ago

Screenshot 2020-05-31 at 01 09 24

HGuillemet commented 4 years ago

When declaring "dependencies", we must distinguish: 1) The tree of maven dependencies (from the <dependency> in the poms) 2) The module graph (determined by the requires in the module-infos) The javacpp.platform property affects maven dependencies only: it limits the dependencies of *-platform artifacts to those of one or several platforms. When it's not set, these artifacts depend on all native artifacts of all possible plaforms.

The native libraries are resources that must be accessible from your app, which means they are either in a "required" module or in the unnamed module. Your first exception is thrown because the native libraries needed by OpenCFFrameGrabber (opencv, openblas...) are not accessible : they are not in the module path (not in a "required" module) neither in the unnamed module (not in a jar of the class path). You can add them in the classpath. That's what mvn javafx:run does: all modules from the module graph are added to the module path and all remaining dependency artifacts are added to the class path. So mvn javafx:run works in your sample app, but mvn javafx:jlink does not produce a working image because the image will only contain the named modules.

When you replace org.bytedeco.javacv by org.bytedeco.javacv.platform, all native jars are brought into the module graph, for all plaforms. So now the native libraries are found, both mvn javafx:run and mvn javafx:jlink work, but not if you specify the javacpp.platform property because the module graph will then contain modules (the native jars for other platforms) that are not in maven dependencies, so not in the module path. This explains your second exception.

I need to think a bit more about best fixes. Another post coming later or tomorrow.

saudet commented 4 years ago

@HGuillemet Would it work if we used requires static instead of just requires for all the entries in platform artifacts like the one at https://github.com/bytedeco/javacpp-presets/blob/master/opencv/platform/pom.xml#L173?

HGuillemet commented 4 years ago

Unfortunately that won't be enough. A "static required" module, even if present in the module path, will not be loaded into the module graph if the module is not "required" by some other module, or added explicitly on the command line with --add-module. So in our case, replacing the requires by requires static in the *-platform presets will make require org.javacpp.javacv.platform act just like require org.javacpp.javacv: native libs won't be found.

The only options I can think about are: 1) Add javacv-system-arch classifiers, like for presets, and have the users that do not want to load the universe of all possible native libraries to "require" these specific classifiers instead of javacv-platform, or maybe let them keep javacv-platform but have them add --add-module org.bytedeco.javacv.system.arch on the command line. 2) Try to rethink the loader in terms of service binding, as already discussed some months ago, which is the standard JPMS-compatible way to query the environment for available implementations of something.

Do you see another option ?

saudet commented 4 years ago

Unfortunately that won't be enough. A "static required" module, even if present in the module path, will not be loaded into the module graph if the module is not "required" by some other module, or added explicitly on the command line with --add-module. So in our case, replacing the requires by requires static in the *-platform presets will make require org.javacpp.javacv.platform act just like require org.javacpp.javacv: native libs won't be found.

Ah, yes, I remember about that. It's actually a compile-only thing, not about optional modules.

The only options I can think about are:

  1. Add javacv-system-arch classifiers, like for presets, and have the users that do not want to load the universe of all possible native libraries to "require" these specific classifiers instead of javacv-platform, or maybe let them keep javacv-platform but have them add --add-module org.bytedeco.javacv.system.arch on the command line.

I don't think we need any additional modules for that to work. We can already do something like this and it works, right?

module recorder {
    requires org.bytedeco.javacv;
    requires org.bytedeco.opencv.macosx.x86_64;
}

It's just annoying that we have to do that... I think we could wrap all that pretty easily with a Gradle plugin in Gradle JavaCPP, if only Gradle supported JPMS: https://github.com/gradle/gradle/issues/890

  1. Try to rethink the loader in terms of service binding, as already discussed some months ago, which is the standard JPMS-compatible way to query the environment for available implementations of something.

Do you see another option ?

I don't see how that would help. Do you have an example of that?

HGuillemet commented 4 years ago
  1. Add javacv-system-arch classifiers, like for presets, and have the users that do not want to load the universe of all possible native libraries to "require" these specific classifiers instead of javacv-platform, or maybe let them keep javacv-platform but have them add --add-module org.bytedeco.javacv.system.arch on the command line.

I don't think we need any additional modules for that to work. We can already do something like this and it works, right?

module recorder {
    requires org.bytedeco.javacv;
    requires org.bytedeco.opencv.macosx.x86_64;
}

Yes, for each native module used by javacv. That's what I do in my modular apps, but I use the presets directly, not javacv.

It's just annoying that we have to do that... I think we could wrap all that pretty easily with a Gradle plugin in Gradle JavaCPP, if only Gradle supported JPMS: gradle/gradle#890

  1. Try to rethink the loader in terms of service binding, as already discussed some months ago, which is the standard JPMS-compatible way to query the environment for available implementations of something.

Do you see another option ?

I don't see how that would help. Do you have an example of that?

The specificity of service loaders is that it can, in a JPMS compatible way, load different modules depending of what it finds in the module path. So the javacpp.platform property will be enough and no need to specify the platform elsewhere (no more specific platforms in the module-info).

That could roughly work like this: In the module-info of org.bytedeco.opencv.linux.x86_64:

  provides org.bytedeco.opencv.opencv_core.Implementation with org.bytedeco.opencv.linux.x86_64.opencv_core.Implementation

When loading opencv_core, instead of the getResource we do:

ServiceLoader<opencv_core.Implementation> loader = ServiceLoader.load(opencv_core.Implementation.class);

The resulting loader can then be used to iterate over all implementations found in the module path and select one having the right system and arch, by calling methods like Implementation.getArch(), or by looking for some annotations. Once an implementation is found, extract and cache the library as usual.

Alternatively, instead of using the service facility to locate specific libraries like opencv_core, it could be used to simply locate the whole native module opencv and then we findResource the library in the module.

HGuillemet commented 4 years ago

There is a 3rd option:

  1. Give up the modularization of native jars. In modular apps, add them to the class path and decide that the correct way to build a jlink image is to directly include the native libraries already extracted (@saudet, see our Gitter discussion of 2019-05-20).
saudet commented 4 years ago

That sounds like a lot trouble just to load resources... I think loading the JVM with options like --add-modules org.bytedeco.opencv.linux.x86_64,org.bytedeco.opencv.macosx.x86_64,etc sounds like a good compromise. It's easy to do since we're doing it at the same place where the module path gets set, and it's something that already works. It's also something we can set at runtime depending on the platform we're running, so we don't need to bake it in the application.

@jkolobok What do you think? Can you give it a try and see if that satisfies your needs?

There is a 3rd option:

  1. Give up the modularization of native jars. In modular apps, add them to the class path and decide that the correct way to build a jlink image is to directly include the native libraries already extracted (@saudet, see our Gitter discussion of 2019-05-20).

Yeah, it looks like JavaFX also gave up on that and they've decided to just fall back on the class path: https://github.com/openjfx/javafx-maven-plugin/issues/58. IMO, if we're going to need the class path to do that, I wouldn't bother with the module path in the first place.

saudet commented 4 years ago

@HGuillemet BTW, could you try to update the ModiTect plugin to 1.0.0.RC1 and make sure there's no issues with the new version?

HGuillemet commented 4 years ago

That sounds like a lot trouble just to load resources... I think loading the JVM with options like --add-modules org.bytedeco.opencv.linux.x86_64,org.bytedeco.opencv.macosx.x86_64,etc sounds like a good compromise. It's easy to do since we're doing it at the same place where the module path gets set, and it's something that already works. It's also something we can set at runtime depending on the platform we're running, so we don't need to bake it in the application.

module path is usually built automatically by maven, but why not. We would need to replace the requires by requires static in the *-platform modules so that the module paths to the native modules don't need to be added manually as well. I still think that adding classifiers for javacv is the simplest for users and will make javacv behave like all presets. We could do both.

@jkolobok What do you think? Can you give it a try and see if that satisfies your needs?

There is a 3rd option:

  1. Give up the modularization of native jars. In modular apps, add them to the class path and decide that the correct way to build a jlink image is to directly include the native libraries already extracted (@saudet, see our Gitter discussion of 2019-05-20).

Yeah, it looks like JavaFX also gave up on that and they've decided to just fall back on the class path: openjfx/javafx-maven-plugin#58. IMO, if we're going to need the class path to do that, I wouldn't bother with the module path in the first place.

Remember that modular app cannot read classes from the classpath, so moving classes to classpath just exclude the option to run modular apps. The Issue and PR you are linking doesn't involve anyone "official".

javafx uses a slighlty different approach : they have also, for instance, both artifacts javafx-graphics-14 and javafx-graphics-14-linux. But javafx-graphics-14 is an empty artifact, an automatic module, only used for its pom which dispatch the maven dependency to, eg, javafx-graphics-14-linux, based on the javafx.platform property just like javacpp.platform. javafx-graphics-14-linux contains both java and native libs and is the true module, named javafx.graphics. So applications always have the same module-info with requires javafx.graphics (no requires javafx.graphics.linux) and the right platform jar javafx-graphics-14-linux is listed in the module path (usually automatically by their maven plugin). The problem, raised by the issue you mentioned, is that there is no way to have a modular app with more than one platform (and even for non modular app it's a bit tricky like discussed in the issue). And also that using their maven plugin is more or less mandatory. Shall we investigate to see if we'd better use the javafx approach for javacpp ? Maybe with moditect in lieu of javafx-maven-plugin ?

HGuillemet commented 4 years ago

@HGuillemet BTW, could you try to update the ModiTect plugin to 1.0.0.RC1 and make sure there's no issues with the new version?

Done. No issue found.

saudet commented 4 years ago

module path is usually built automatically by maven, but why not. We would need to replace the requires by requires static in the *-platform modules so that the module paths to the native modules don't need to be added manually as well.

Right, by default, it's either all or nothing. If we can't remove them on demand, then I suppose the best we can do is force people to add them on demand.

I still think that adding classifiers for javacv is the simplest for users and will make javacv behave like all presets. We could do both.

How would that help? The problem is with artifacts for OpenCV, FFmpeg, etc, not JavaCV.

The problem, raised by the issue you mentioned, is that there is no way to have a modular app with more than one platform (and even for non modular app it's a bit tricky like discussed in the issue). And also that using their maven plugin is more or less mandatory. Shall we investigate to see if we'd better use the javafx approach for javacpp ? Maybe with moditect in lieu of javafx-maven-plugin ?

Why? That limitation is quite severe. Like I said, if we have to fall back on the class path to get it working, I don't see the point of using the module path at all, so let's try to figure out something else.

jkolobok commented 4 years ago

That sounds like a lot trouble just to load resources... I think loading the JVM with options like --add-modules org.bytedeco.opencv.linux.x86_64,org.bytedeco.opencv.macosx.x86_64,etc sounds like a good compromise. It's easy to do since we're doing it at the same place where the module path gets set, and it's something that already works. It's also something we can set at runtime depending on the platform we're running, so we don't need to bake it in the application.

@jkolobok What do you think? Can you give it a try and see if that satisfies your needs?

Hm. I'm not sure what the list of modules should look like depending of what parst of java vc I'm using.. Do I need to add all platforms? The app is a desktop application so it must be as small as possibe. Modules are note required they are just a nice to have if it's not too much pain

saudet commented 4 years ago

Hm. I'm not sure what the list of modules should look like depending of what parst of java vc I'm using.. Do I need to add all platforms? The app is a desktop application so it must be as small as possibe. Modules are note required they are just a nice to have if it's not too much pain

Ok, so you should probably not use the "-platform" artifacts anyway. As long as you don't get any errors, you don't need those modules that you're not adding.

HGuillemet commented 4 years ago

I still think that adding classifiers for javacv is the simplest for users and will make javacv behave like all presets. We could do both.

How would that help? The problem is with artifacts for OpenCV, FFmpeg, etc, not JavaCV.

The problem is with the *-platform modules that require all platforms artifacts, independently of the javacpp.platform property and of what is available on the module path. For instance if @jkolobok delares a maven dependency towards javacv, classifier macosx-x86_64 (or towards javacv-platform and using the javacpp-platform property) AND changes his module-info.java to requires org.bytedeco.javacv.macosx.x86_64 then his problems will be solved. Only the macosx artifact will be downloaded and includes in the jlink image. If needed, the platform in module-info can be automatically changed using either a maven plugin like templating-maven-plugin or using moditect.

The problem, raised by the issue you mentioned, is that there is no way to have a modular app with more than one platform (and even for non modular app it's a bit tricky like discussed in the issue). And also that using their maven plugin is more or less mandatory. Shall we investigate to see if we'd better use the javafx approach for javacpp ? Maybe with moditect in lieu of javafx-maven-plugin ?

Why? That limitation is quite severe.

I agree.

saudet commented 4 years ago

The problem is with the *-platform modules that require all platforms artifacts, independently of the javacpp.platform property and of what is available on the module path. For instance if @jkolobok delares a maven dependency towards javacv, classifier macosx-x86_64 (or towards javacv-platform and using the javacpp-platform property) AND changes his module-info.java to requires org.bytedeco.javacv.macosx.x86_64 then his problems will be solved. Only the macosx artifact will be downloaded and includes in the jlink image. If needed, the platform in module-info can be automatically changed using either a maven plugin like templating-maven-plugin or using moditect.

Right, but that has nothing to do with JavaCV. We can do all that at the level of OpenCV, FFmpeg, etc.

If you know of a way to generate those module-info.java files on demand using the existing Maven profiles, that would be great: https://github.com/bytedeco/javacpp-presets/wiki/Reducing-the-Number-of-Dependencies

saudet commented 4 years ago

Or alternatively, a Maven extension, which would also allow us to define the target platforms in the pom.xml file instead: https://github.com/bytedeco/javacpp-presets/issues/846

/cc @maxsenft

saudet commented 4 years ago

Looks like ModiTect is also available for Gradle: https://github.com/moditect/moditect-gradle-plugin Might be easier to implement something with that and Gradle JavaCPP: https://github.com/bytedeco/gradle-javacpp

HGuillemet commented 4 years ago

After some more thought, here is what I'm suggesting:

To illustrate this, I have updated the sample project stitching-jlink (PR) Here are the list of modules included in the jlink image in the 3 cases mentioned above:

java.base@14.0.1
jdk.unsupported@14.0.1
org.bytedeco.javacpp
org.bytedeco.openblas
org.bytedeco.opencv
org.bytedeco.samples.stitching
java.base@14.0.1
jdk.unsupported@14.0.1
org.bytedeco.javacpp
org.bytedeco.openblas
org.bytedeco.openblas.linux.x86_64 open
org.bytedeco.opencv
org.bytedeco.opencv.linux.x86_64 open
org.bytedeco.samples.stitching
java.base@14.0.1
jdk.unsupported@14.0.1
org.bytedeco.javacpp
org.bytedeco.openblas
org.bytedeco.openblas.ios.arm64 open
org.bytedeco.openblas.ios.x86_64 open
org.bytedeco.openblas.linux.arm64 open
org.bytedeco.openblas.linux.armhf open
org.bytedeco.openblas.linux.ppc64le open
org.bytedeco.openblas.linux.x86 open
org.bytedeco.openblas.linux.x86_64 open
org.bytedeco.openblas.macosx.x86_64 open
org.bytedeco.openblas.windows.x86 open
org.bytedeco.openblas.windows.x86_64 open
org.bytedeco.opencv
org.bytedeco.opencv.ios.arm64 open
org.bytedeco.opencv.ios.x86_64 open
org.bytedeco.opencv.linux.arm64 open
org.bytedeco.opencv.linux.armhf open
org.bytedeco.opencv.linux.ppc64le open
org.bytedeco.opencv.linux.x86 open
org.bytedeco.opencv.linux.x86_64 open
org.bytedeco.opencv.macosx.x86_64 open
org.bytedeco.opencv.platform
org.bytedeco.opencv.windows.x86 open
org.bytedeco.opencv.windows.x86_64 open
org.bytedeco.samples.stitching

Nothing needs to be changed to presets or javacpp for this to work.

I know 3 maven plugins that can run jlink: moditect, jlink and javafx. The jlink goal of moditect doesn't use maven dependencies to build the module path, it must be populated manually, which is cumbersome and error-prone. We could suggest the author to fix this. The jlink plugin constructs the module path from all maven dependencies. That's not what we want because of the *-platform artifacts. The javafx plugin somehow reconstructs the module graph from the module-infos and builds the module path. That's what works here.

In order to generate the module-info according to javacpp.platform, I had to use the build-helper plugin to replace the - by . (that's the annoying part), and the templating plugin. moditect could be used instead of the templating plugin but it's not compatible with the javafx plugin and the way it reads module-info.java files.

That said, the sample program does not work for some independent reason i didn't investigate:

reading panorama_image1.jpg
reading panorama_image2.jpg
OpenCL program build log: features2d/orb
Status -11: CL_BUILD_PROGRAM_FAILURE
-D ORB_RESPONSES -D blockSize=7 -D scale_sq_sq=3,847753306719e-16f -D HARRIS_K=0,039999999106f
<kernel>:35:49: error: invalid digit '9' in octal constant
responses[idx] = ((float)a * b - (float)c * c - HARRIS_K * (float)(a + b) * (a + b))*scale_sq_sq;
                                                ^
<built-in>:19:22: note: expanded from here
#define HARRIS_K 0,039999999106f

There are also spurious warnings about not finding jnijavacpp, I haven't investigate either.

What do you think ? If you agree with this approach, let's see javacv case.

saudet commented 4 years ago

After some more thought, here is what I'm suggesting:

  • for artifacts dependencies, if we want to depend on native jars, we should always use the *-platform artifacts, using the javacpp.platform property if we want to limit the dependencies to some system/arch. Depending directly on the *-system-arch artifacts won't draw the native jars of depencies so it's not satisfactory.

  • for module graph, let's offer the choice to the user between:

    • not adding the native modules (useful when the user choose to add the native libs directly in the image): eg requires org.javacpp.opencv
    • adding modules of selective platforms, eg requires org.javacpp.opencv.linux.x86_64
    • adding modules for all the platforms, eg requires org.javacpp.opencv.platform

Ok, but ideally one would assume that the module path and graph could just be automatically adjusted to whatever Maven modules the user gave to the build...

Nothing needs to be changed to presets or javacpp for this to work.

I know 3 maven plugins that can run jlink: moditect, jlink and javafx. The jlink goal of moditect doesn't use maven dependencies to build the module path, it must be populated manually, which is cumbersome and error-prone. We could suggest the author to fix this. The jlink plugin constructs the module path from all maven dependencies. That's not what we want because of the *-platform artifacts. The javafx plugin somehow reconstructs the module graph from the module-infos and builds the module path. That's what works here.

maven-jlink-plugin seems abandoned and if javafx-maven-plugin works better for the purpose of the sample project, meh, sure, why not. Ideally, a general plugin like ModiTect should implement all that and JavaFX should then depend on it, but JavaFX is OpenJDK, which can't depend on anything external anyway so it's probably a lost cause, but maybe I'm mistaken, @johanvos? Do you think the guys at JavaFX could contribute their code to ModiTect?

In order to generate the module-info according to javacpp.platform, I had to use the build-helper plugin to replace the - by . (that's the annoying part), and the templating plugin. moditect could be used instead of the templating plugin but it's not compatible with the javafx plugin and the way it reads module-info.java files.

We can use JavaCPP for this. It returns that value as ${javacpp.platform.module} to the project.

That said, the sample program does not work for some independent reason i didn't investigate:

reading panorama_image1.jpg
reading panorama_image2.jpg
OpenCL program build log: features2d/orb
Status -11: CL_BUILD_PROGRAM_FAILURE
-D ORB_RESPONSES -D blockSize=7 -D scale_sq_sq=3,847753306719e-16f -D HARRIS_K=0,039999999106f
<kernel>:35:49: error: invalid digit '9' in octal constant
responses[idx] = ((float)a * b - (float)c * c - HARRIS_K * (float)(a + b) * (a + b))*scale_sq_sq;
                                                ^
<built-in>:19:22: note: expanded from here
#define HARRIS_K 0,039999999106f

That looks like an issue with your OpenCL driver... ?

There are also spurious warnings about not finding jnijavacpp, I haven't investigate either.

That just means javacpp-platform is missing somewhere, see issues #1305 and https://github.com/bytedeco/javacpp/issues/393 for details.

What do you think ? If you agree with this approach, let's see javacv case.

I'm not convinced that we're getting anywhere because we still have to list all the modules manually in the module-info.java file anyway, no?

HGuillemet commented 4 years ago

Ok, but ideally one would assume that the module path and graph could just be automatically adjusted to whatever Maven modules the user gave to the build...

For the module path, yes, that's what maven-jlink plugin and javafx plugin do (the javafx plugin in a smarter way). But we cannot automatically generate the module-info of the user, there are information there we cannot guess: the name of the module, what it exports, opens, which dependencies are transitive... People building JPMS app should be used to write their module-info in addition to their artifact dependencies anyway.

maven-jlink-plugin seems abandoned and if javafx-maven-plugin works better for the purpose of the sample project, meh, sure, why not. Ideally, a general plugin like ModiTect should implement all that and JavaFX should then depend on it, but JavaFX is OpenJDK, which can't depend on anything external anyway so it's probably a lost cause, but maybe I'm mistaken, @johanvos? Do you think the guys at JavaFX could contribute their code to ModiTect?

Yes there is a lot in the javafx maven plugin that has nothing to do with javafx that could be in moditect or directly in maven.

In order to generate the module-info according to javacpp.platform, I had to use the build-helper plugin to replace the - by . (that's the annoying part), and the templating plugin. moditect could be used instead of the templating plugin but it's not compatible with the javafx plugin and the way it reads module-info.java files.

We can use JavaCPP for this. It returns that value as ${javacpp.platform.module} to the project.

Right, but the normal user does't need the javacpp maven plugin. and using the build mojo for just 1 line of Java (properties.setProperty("platform.module", module.replace('-', '.'));) seems a bit overkill. If you think of other features targeted to users and not preset-developers that could be isolated in a small mojo it could be the best solution. Using the build-helper + the templating plugin in the sample project just to generate the module-info of the sample project is overkill too, but it allows to run the project from the command line without editing anything.

That looks like an issue with your OpenCL driver... ?

That was a strange locale problem. Running with LANG=C solved the issue in my case.

There are also spurious warnings about not finding jnijavacpp, I haven't investigate either.

That just means javacpp-platform is missing somewhere, see issues #1305 and bytedeco/javacpp#393 for details.

Shouldn't all the preset-platform have a maven and module dependency towards javacpp-platform ?

What do you think ? If you agree with this approach, let's see javacv case.

I'm not convinced that we're getting anywhere because we still have to list all the modules manually in the module-info.java file anyway, no?

Yes, see above.

saudet commented 4 years ago

Ok, but ideally one would assume that the module path and graph could just be automatically adjusted to whatever Maven modules the user gave to the build...

For the module path, yes, that's what maven-jlink plugin and javafx plugin do (the javafx plugin in a smarter way). But we cannot automatically generate the module-info of the user, there are information there we cannot guess: the name of the module, what it exports, opens, which dependencies are transitive... People building JPMS app should be used to write their module-info in addition to their artifact dependencies anyway.

I still think we can do better than that, but I'll leave it up to you :)

maven-jlink-plugin seems abandoned and if javafx-maven-plugin works better for the purpose of the sample project, meh, sure, why not. Ideally, a general plugin like ModiTect should implement all that and JavaFX should then depend on it, but JavaFX is OpenJDK, which can't depend on anything external anyway so it's probably a lost cause, but maybe I'm mistaken, @johanvos? Do you think the guys at JavaFX could contribute their code to ModiTect?

Yes there is a lot in the javafx maven plugin that has nothing to do with javafx that could be in moditect or directly in maven.

@johanvos Is this due to something legal restriction from Oracle?

In order to generate the module-info according to javacpp.platform, I had to use the build-helper plugin to replace the - by . (that's the annoying part), and the templating plugin. moditect could be used instead of the templating plugin but it's not compatible with the javafx plugin and the way it reads module-info.java files.

We can use JavaCPP for this. It returns that value as ${javacpp.platform.module} to the project.

Right, but the normal user does't need the javacpp maven plugin. and using the build mojo for just 1 line of Java (properties.setProperty("platform.module", module.replace('-', '.'));) seems a bit overkill. If you think of other features targeted to users and not preset-developers that could be isolated in a small mojo it could be the best solution. Using the build-helper + the templating plugin in the sample project just to generate the module-info of the sample project is overkill too, but it allows to run the project from the command line without editing anything.

I don't consider adding these 5 lines to the pom.xml file to be overkill:

      <plugin>
        <groupId>org.bytedeco</groupId>
        <artifactId>javacpp</artifactId>
        <version>1.5.3</version>
      </plugin>

But if you prefer build-helper, that's alright.

That looks like an issue with your OpenCL driver... ?

That was a strange locale problem. Running with LANG=C solved the issue in my case.

There are also spurious warnings about not finding jnijavacpp, I haven't investigate either.

That just means javacpp-platform is missing somewhere, see issues #1305 and bytedeco/javacpp#393 for details.

Shouldn't all the preset-platform have a maven and module dependency towards javacpp-platform ?

They do, that's why it works, but again if it doesn't get added to the module graph, it won't work with JPMS, right? We have to add it manually, I guess?

What do you think ? If you agree with this approach, let's see javacv case.

I'm not convinced that we're getting anywhere because we still have to list all the modules manually in the module-info.java file anyway, no?

Yes, see above.

Are you sure? Really really sure? :)

HGuillemet commented 4 years ago

I don't consider adding these 5 lines to the pom.xml file to be overkill:

      <plugin>
        <groupId>org.bytedeco</groupId>
        <artifactId>javacpp</artifactId>
        <version>1.5.3</version>
      </plugin>

But if you prefer build-helper, that's alright.

We also need to execute the plugin somehow, don't we ?

Shouldn't all the preset-platform have a maven and module dependency towards javacpp-platform ?

They do, that's why it works, but again if it doesn't get added to the module graph, it won't work with JPMS, right? We have to add it manually, I guess?

This PR will do it automatically. If it's ok to assume that all native presets need the libraries in the native javacpp artifact.

saudet commented 4 years ago

We also need to execute the plugin somehow, don't we ?

Right, it doesn't look like we can set default executions. Something like this then:

        <plugin>
          <groupId>org.bytedeco</groupId>
          <artifactId>javacpp</artifactId>
          <version>1.5.3</version>
        </plugin>
        <executions>
          <execution>
            <id>javacpp-validate</id>
            <phase>validate</phase>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        <executions>

They do, that's why it works, but again if it doesn't get added to the module graph, it won't work with JPMS, right? We have to add it manually, I guess?

This PR will do it automatically. If it's ok to assume that all native presets need the libraries in the native javacpp artifact.

Ok, I see, thanks! Also let's try to make this work: https://github.com/bytedeco/sample-projects/pull/49#discussion_r437876843

HGuillemet commented 4 years ago
        <plugin>
          <groupId>org.bytedeco</groupId>
          <artifactId>javacpp</artifactId>
          <version>1.5.3</version>
        </plugin>
        <executions>
          <execution>
            <id>javacpp-validate</id>
            <phase>validate</phase>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        <executions>

That does the trick. If it's likely that we can find other use for this plugin for the user (as opposed to the preset builder), it might be worth creating a separate goal.

This PR will do it automatically. If it's ok to assume that all native presets need the libraries in the native javacpp artifact.

Ok, I see, thanks! Also let's try to make this work: bytedeco/sample-projects#49 (comment)

javacpp.platform.host works. PR updated.

To go back to the OP issue: with JavaCV, we can already have option 1 of options mentioned here (no native jars in module),and option 3 (all native jars) available. If we want option 2 (native jars for selective platform in the module graph), then either:

Another related issue: I'm not familiar with JavaCV, does the user has to access Java types defined in the JavaCV components, like opencv Mat or whatever ? If yes, the requires in the javacv module-info should be transitive.

saudet commented 4 years ago

That does the trick. If it's likely that we can find other use for this plugin for the user (as opposed to the preset builder), it might be worth creating a separate goal.

Actually, for users, we need to create a Maven extension for this, see issue https://github.com/bytedeco/javacpp-presets/issues/846. We can't modify the dependencies of a project with just Maven plugins (although we can with a Gradle plugin, see https://github.com/bytedeco/gradle-javacpp#the-platform-plugin), so in this case as well, just using a Maven plugin creates inconsistencies in the build when all the magical properties are not perfectly aligned with the profiles.

javacpp.platform.host works. PR updated.

:+1:

To go back to the OP issue: with JavaCV, we can already have option 1 of options mentioned here (no native jars in module),and option 3 (all native jars) available. If we want option 2 (native jars for selective platform in the module graph), then either:

  • we add javacpp-system-arch modules, so that the user module-info will just contain:
module recorder {
  requires org.bytedeco.javacv.linux-x86_64;
}

How would this work if/when we split JavaCV into multiple modules like https://github.com/bytedeco/javacv/issues/1071#issuecomment-518504533?

* or the user has to determine which component of JavaCV it needs and list native modules:
module recorder {
  requires org.bytedeco.javacv;
  requires org.bytedeco.opencv.linux-x86_64;
}

Well, I still think the build system should be smart enough to add all this stuff on its own. Like: "Oh, we have opencv-linux-x86_64 on the module path, let's add that module too!" You know, it's really basic stuff, but if we have to do it manually for now, that's alright.

Another related issue: I'm not familiar with JavaCV, does the user has to access Java types defined in the JavaCV components, like opencv Mat or whatever ? If yes, the requires in the javacv module-info should be transitive.

Yes, it's part of the "API", and users are expected to use those classes.

saudet commented 4 years ago

BTW, if one of your goals is to produce executables that are as small as possible, give it a try with GraalVM Native Image as well. It produces much smaller images. Here's what I get on linux-x86_64 with GraalVM 20.1.0 for OpenJDK 11:

$ git clone https://github.com/bytedeco/sample-projects
$ cd sample-projects/opencv-stitching-jlink
$ mvn clean package -Djavacpp.platform.custom -Djavacpp.platform.host
$ tar -cJvf image.tar.xz target/image
$ ln -lh image.tar.xz
-rw-rw-r--. 1 saudet saudet  54M Jun 13 11:58 image.tar.xz
$ cd ../opencv-stitching-native
$ mvn clean package -Djavacpp.platform.custom -Djavacpp.platform.host
$ tar -cJvf stitching.tar.xz target/stitching
$ ls -lh stitching.tar.xz
-rw-rw-r--. 1 saudet saudet  23M Jun 13 11:57 stitching.tar.xz
HGuillemet commented 4 years ago

I will.

Well, I still think the build system should be smart enough to add all this stuff on its own. Like: "Oh, we have opencv-linux-x86_64 on the module path, let's add that module too!" You know, it's really basic stuff, but if we have to do it manually for now, that's alright.

Here is something that should make you happy:

--add-modules ALL-MODULE-PATH

That works for javac, java AND jlink (but unfortunately not jdeps).

saudet commented 4 years ago

Here is something that should make you happy:

--add-modules ALL-MODULE-PATH

That works for javac, java AND jlink (but unfortunately not jdeps).

Nice, does it actually work? If you figure out how to use it with javafx-maven-plugin, please update the sample project! Thanks

HGuillemet commented 4 years ago

After some experiments:

The apache jlink-maven-plugin solution seems then more user-friendly : no need to duplicate the platform selection in the module-info in addition to the javacpp.platform property, no need for the javacpp maven plugin nor for the templating plugin.

Too bad this plugin is still alpha and more or less abandoned. I saw this fork that may be promising.

johanvos commented 4 years ago

I'll have to read the whole thread do understand what is missing, but if you want to make changes in the javafx-maven-plugin, you are very welcome to file an issue at https://github.com/openjfx/javafx-maven-plugin/issues (and a PR?).

For usage with GraalVM, we actually use a different plugin: https://github.com/gluonhq/client-maven-plugin (which is targeting desktop/mobile/embedded)

saudet commented 4 years ago

@johanvos tl;dr We're basically missing something that can add all modules found on the module path, well actually just Maven dependencies are fine, to the module graph, especially in the case of jlink but in general as well. Apparently javafx-maven-plugin doesn't do that.

@jkolobok or @HGuillemet Please open an issue over there about this issue! Thanks

HGuillemet commented 4 years ago

Done

HGuillemet commented 4 years ago

The javafx-maven-plugin is being updated. To sum up, users of javacpp presets in JPMS applications will have to:

This solution should also avoid the need to add native modules for JavaCV.

They will still have the option to explicitly add the native module (eg with requires org.bytedeco.opencv.linux.x86_64 in module-info or --add-modules org.bytedeco.opencv.linux.x86_64 on the command line) if the ALL-MODULE-PATH mechanism does not work for them. Or to add nothing if they extract "manually" the native libraries in the image.

However we need to change the module-info of all -platform modules and either replace the requires towards the native modules with requires static, or to remove the requires entirely. I don't think it makes any difference.

Am I overlooking some details ?

saudet commented 4 years ago

Sounds good! Let's use "requires static" just for good measure. :) Thanks!!

saudet commented 4 years ago

@HGuillemet When javafx-maven-plugin gets updated, please also update opencv-stitching-jlink! Thank you very much

saudet commented 4 years ago

@HGuillemet Any news??

HGuillemet commented 4 years ago

https://github.com/openjfx/javafx-maven-plugin/pull/92#issuecomment-675960117

saudet commented 3 years ago

Ok, that PR has finally been merged. @HGuillemet Could you update the sample project and let's see what that looks like?

HGuillemet commented 3 years ago

Unfortunately this commit only adds the option to populate the module path with maven dependencies. The required option to add the maven dependencies to the jlink image has been forked in another issue. So currently the javafx:run goal works as we need it to, but not javafx:jlink yet.

saudet commented 3 years ago

@HGuillemet It should now work for @jkolobok's use case though right?

@jkolobok Can you give it a try with the new version of javafx-maven-plugin?

HGuillemet commented 3 years ago

We first need to replace the requires in *-platform modules by static requires. I can file a PR only for this if it helps.

saudet commented 3 years ago

Didn't you already do that in pull https://github.com/bytedeco/javacpp-presets/pull/900?

HGuillemet commented 3 years ago

Yes sorry, it's in javacpp.platform then that the static is missing.

saudet commented 3 years ago

Sure, please fix whatever needs fixing. Thanks!