Closed krezovic closed 3 weeks ago
More details ... The default classes directory is set to "target/classes" by native-maven-plugin
If I run it like this
./mvnw native:compile-no-fork -DclassesDirectory=target/demo-0.0.1-SNAPSHOT.jar.original
Then the app behaves as expected
$ target/demo
14:06:19.307 [main] INFO com.example.demo.DemoApplication -- Running version: 0.0.1-SNAPSHOT
I wonder if this is something worthy of a documentation, or a pre-configuration to native-maven-plugin - since it comes from spring boot parent in this case.
native-image invocation before
Executing: /home/armin/.sdkman/candidates/java/current/bin/native-image -cp /home/armin/projects/native-image-demo/target/classes:/home/armin/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.4.0-M3/spring-boot-starter-web-3.4.0-M3.jar...
native-image invocation after
[INFO] Executing: /home/armin/.sdkman/candidates/java/current/bin/native-image -cp /home/armin/projects/native-image-demo/target/demo-0.0.1-SNAPSHOT.jar.original:/home/armin/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.4.0-M3/spring-boot-starter-web-3.4.0-M3.jar...
Using JAR without .original suffix (spring boot fatjar) obviously fails
[INFO] Executing: /home/armin/.sdkman/candidates/java/current/bin/native-image -cp /home/armin/projects/native-image-demo/target/demo-0.0.1-SNAPSHOT.jar:/home/armin/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.4.0-M3/spring-boot-starter-web-3.4.0-M3.jar:...
...
Error: Main entry point class 'com.example.demo.DemoApplication' neither found on
classpath: '/home/armin/projects/native-image-demo/target/demo-0.0.1-SNAPSHOT.jar:/home/armin/.m2/repository/org/springframework/boot/spring-boot-starter-web/3.4.0-M3/spring-boot-starter-web-3.4.0-M3.jar:... nor modulepath: '/home/armin/.sdkman/candidates/java/23.1.4.r21-mandrel/lib/svm/library-support.jar'.
@krezovic thanks for the report.
Using JAR without .original suffix (spring boot fatjar) obviously fails
Yes. And that is why our parent configures the native image maven plugin to use the classesDirectory
option. It would be better if we didn't have to do this but we can't know upfront if the build is configured to create a repackaged archive or not, and if a classifier is set. We can't configure it to hardcode .original
as it may or may not be accurate based on how the app is configured.
Our best course of action is some documentation here.
Leaving as a reference
Another hint is required if you dynamically use manifest via ResourceLoader
static class ApplicationRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerPattern("META-INF/MANIFEST.MF");
}
}
Example code that works after adding the hint, but not before
var resource =
new ClassPathResource(
"META-INF/MANIFEST.MF", this.getClass().getClassLoader());
log.info("Manifest resource: " + resource);
log.info("Manifest class loader: " + this.getClass().getClassLoader());
log.info("Manifest exists: " + resource.exists());
Manifest manifest = null;
if (resource.exists()) {
try {
manifest = new Manifest(resource.getInputStream());
log.info(
"Manifest attributes: "
+ manifest.getMainAttributes().entrySet().stream()
.map(
kv ->
String.join(
": ",
kv.getKey().toString(),
kv.getValue().toString()))
.collect(Collectors.joining("\n")));
} catch (IOException e) {
log.warn("Could not read manifest", e);
}
}
When native image is built, there is no MANIFEST.MF for the application being built. Running "MyApp.class.getPackage().getImplementationVersion()" will return "null" in such scenario.
This is expected, as MANIFEST.MF is written by maven-jar-plugin, but native image build operates on target/classes dir itself.
How would one proceed with "get version of the currently running app" when running as native executable?
Consider the following example: https://github.com/krezovic/native-image-demo
will produce
Running
Running native executable, however
As expected, there's no MANIFEST.MF inside target/classes/META-INF
It is only present inside final JAR file