Create Java Runtime Image on One Platform for Another Using Jlink

Create Java runtime image on one platform for another using Jlink

The include directory is for header files, such as jni.h, that are needed when compiling C/C++ code that uses JNI and other native interfaces. It's nothing to do with jlink.

The jlink tool can create a run-time image for another platform (cross targeting). You need to download two JDKs to do this. One for the platform where you run jlink, the other for the target platform. Run jlink with --module-path $TARGET/jmods where $TARGET is the directory where you've unzipped the JDK for the target platform.

jlink packages current platform's binaries

Found the issue: the problem was with my reference to the jmods directory of both the Linux and the MacOS JDK distributions.

For the Linux one I mistakenly setup the build to download version 11.0.1 instead of 11.0.2, which ended up leading to the logic to flatten the hierarchy not flattening it. This means that the build/JREs/linux/jmods reference wasn't targeting any existing folder, meaning that jlink doesn't find the JDK modules there hence the host files being included.

The MacOS JDK has a completely different file structure so the flattening logic was just wrong. Ultimately this lead to the same missing jmods folder symptom.

With both issues fixed the jlink tool now correctly packages the target JDK's files when building cross-platform runtime images.

Create a personalised JRE with Java 15 on Windows for Linux and Mac

@deduper I tried with :

jlink --module-path C:\Users\hydrolien\Formiko\jmodsLinux --add-modules java.desktop --output OUTLinux\java

And that's working !

That was just a problem of absolute or relative path.

thanks a lot for your help !

How to run jlink-generated Java runtime image without CMD window?

Ok, I've figured out it's not posiible to eliminite shell window completely. In the best scenario it's just flickers for ~1sec. This is how it can be achieved:

@echo off
set JLINK_VM_OPTIONS=
set DIR=%~dp0
start "" "%DIR%\javaw" %JLINK_VM_OPTIONS% -m app/com.package.Launcher %* && exit 0

There is a feature request about native laucher implementation but it's not discussed actively.

Nonetheless I've solved the problem. There is "Batch to EXE Converter" tool. It can generate executable (basically the same batch file) which can run your app silently.

JLink does not produce redistributable image

Your issue is that the test container (bash:5) doesn't use the same version of the run-time linker as the java environment.

The binary produced by the jlink will only run if there is a compatible linux run-time linker on the system.

The purpose of the run-time linker is to configure the binary for execution on the system - at the time you are building an executable the default run-time linker is hard-coded into the binary. You can inspect the run-time linker using a tool such as readelf -l, or ldd (ldd only works if it can find the run-time linker)

The default run-time linker for amd64 linux (e.g. ubuntu) is: /lib64/ld-linux-x86-64.so.2

The default run-time linker for i386 linux is: /lib/ld-linux.so.2

On a bash:5 container, the default run-time linker is: /lib/ld-musl-x86_64.so.1

This is not compatible with the run-time linker for the jdk

The error: /app/bin/java: not found is caused because the run-time linker cannot be found for the binary. A dirty test of a jlinked VM in a bash:5 container gives the same error.

When I get the run-time linker for the java I've used:

$ docker run --rm -it -v (pwd)/edu-day-jlinked64:/app -w /here bash:5 bash

bash-5.0# /app/bin/java
bash: /app/bin/java: No such file or directory
bash-5.0# strings -a /app/bin/java | grep '^/lib'
/lib64/ld-linux-x86-64.so.2
bash-5.0# ls -l /lib64/ld-linux-x86-64.so.2
ls: /lib64/ld-linux-x86-64.so.2: No such file or directory

Testing with the run-time linker that's on-board:

bash-5.0# /lib/ld-musl-x86_64.so.1 --list /app/bin/java
/lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libjli.so => /app/bin/../lib/libjli.so (0x7fe28528c000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libz.so.1 => /lib/libz.so.1 (0x7fe285272000)
libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
Error relocating /app/bin/../lib/libjli.so: __snprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __vfprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __read_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __memmove_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __printf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __fprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __sprintf_chk: symbol not found

so it definitely won't work here.

Let's use something 'standard'. As I had built the jlinked app in an ubuntu:focal container, with an installed version of java let's use one that doesn't have java built-in:

$ docker run --rm -it -v $(pwd)/edu-day-jlinked64:/app -w /here ubuntu:focal bash
root@865c9c12c029:/here# /app/bin/java
Usage: java [options] <mainclass> [args...]
(to execute a class)
or java [options] -jar <jarfile> [args...]
(to execute a jar file)
or java [options] -m <module>[/<mainclass>] [args...]
java [options] --module <module>[/<mainclass>] [args...]
(to execute the main class in a module)
or java [options] <sourcefile> [args]
(to execute a single source-file program)

so it will work in this case.

Reproducibility:

Built using:

$ docker run --rm -it -v $(pwd):/here -w /here ubuntu:focal bash

# apt-get update
# DEBIAN_FRONTEND=noninteractive apt-get install -y git openjdk-14-jdk maven
# git clone https://gitlab.com/Dragas/edu-day-demo .
# git checkout modules-full
# ./mvnw package
# rm -rf edu-day-runtime/target/classes
# jlink --module-path edu-day-runtime/target/dependency/:edu-day-runtime/target/ --add-modules ALL-MODULE-PATH --output edu-day-jlinked --launcher edurun=edu.day.runtime
# ./edu-day-jlinked/bin/edurun 1 1
Result of sum is 2

In an adjacent directory:

$ docker run --rm -it -v $(pwd)/edu-day-jlinked:/app -w /here bash:5 bash
bash-5.0# /app/bin/edurun 1 1
/app/bin/edurun: line 4: /app/bin/java: not found

In another directory:

$ docker run --rm -it -v $(pwd)/edu-day-jlinked:/app -w /here ubuntu:focal bash
root@200b4a98f9ee:/here# /app/bin/edurun 1 1
Result of sum is 2

How do I run images generated by JDK 9 jlink?

To run, do this:

greetingsapp/bin/java -m com.greetings/com.greetings.Main 

Or, you can have jlink build a launcher script that does this

jlink --module-path $JAVA_HOME/jmods:mlib --add-modules com.greetings --output greetingsapp --launcher launch=com.greetings/com.greetings.Main

and then run with:

greetingsapp/bin/launcher

Form the same documentation :-

$ java -p mods -m com.greetings/com.greetings.Main

could be executed to run the Main class from the module structure without linking using jshell as well.


Also, jlink is the linker tool and can be used to link a set of modules, along with their transitive dependencies, to create a custom modular run-time image called as Modular Runtime Images which can be accomplished using the JMOD tool introduced with Java 9 modules.
As pointed out in comments and answered by @Jorn if you simply intend to execute the main class.

You can run your application by using the java binary in the bin
folder of the generated image, and using the command:

java com.greetings.Main

On the other hand, an example of creating a JMOD file to be used as a module further is as :

jmod create --class-path mods/com.greetings --cmds commands
--config configfiles --header-files src/h --libs lib
--main-class com.greetings.Main --man-pages man --module-version 1.0
--os-arch "x86_x64" --os-name "Mac OS X"
--os-version "10.10.5" greetingsmod

EDIT: Expanded + clarified to have the answer that I was looking for.



Related Topics



Leave a reply



Submit