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
Specific Difference Between Bufferedreader and Filereader
Closing Bufferedreader and System.In
JSON Consumer of Polymorphic Objects
What Is the Use of Filter and Chain in Servlet
Why Does Intellij Give Me "Package Doesn't Exist" Error
Jackson/Hibernate, Meta Get Methods and Serialization
How to "Add" to Classpath Dynamically in Java
How to Get an Enum Based on the Value of Its Field
Can a Class Have No Constructor
How to Debug a Multi-Threaded App in Intellij
Why Maven? What Are the Benefits
JSON Gson.Fromjson Java Objects
Using Eclipse Java Compiler (Ecj) in Maven Builds
Use Cases for Rxjava Schedulers
Exception Without Stack Trace in Java
Java Restfull Webservice: Jax-Rs Implementation with Jersey 2.3.1 Libraries
Found Shared References to a Collection Org.Hibernate.Hibernateexception