diff --git a/docs/src/docs/asciidoc/gradle-plugin.adoc b/docs/src/docs/asciidoc/gradle-plugin.adoc index 01ba62cf5..c253beb64 100644 --- a/docs/src/docs/asciidoc/gradle-plugin.adoc +++ b/docs/src/docs/asciidoc/gradle-plugin.adoc @@ -159,6 +159,8 @@ nativeBuild { // Development options agent = true // Enables the reflection agent. Can be also set on command line using '-Pagent' + + useFatJar = true // Instead of passing each jar individually, builds a fat jar } ``` @@ -185,11 +187,55 @@ nativeBuild { // Development options agent.set(true) // Enables the reflection agent. Can be also set on command line using '-Pagent' + + + useFatJar.set(true) // Instead of passing each jar individually, builds a fat jar } ``` NOTE: For options that can be set using command-line, if both DSL and command-line options are present, command-line options take precedence. +==== Long classpath and fat jar support + +Under Windows, https://github.com/graalvm/native-build-tools/issues/85[it is possible that the length of the classpath exceeds what the operating system supports] when invoking the CLI to build a native image. +As a consequence, if you are running under Windows, the plugin will automatically shorten the classpath of your project by building a so called "fat jar", which includes all entries from the classpath automatically. + +In case this behavior is not required, you can disable the fat jar creation by calling: + +.Disabling the fat jar creation +[role="multi-language-sample"] +```groovy +nativeBuild { + useFatJar = false +} +``` + +[role="multi-language-sample"] +```kotlin +nativeBuild { + useFatJar.set(false) +} +``` + +Alternatively, it is possible to use your own fat jar (for example created using the https://imperceptiblethoughts.com/shadow/[Shadow plugin]) by setting the `classpathJar` property directly on the _task_: + +.Disabling the fat jar creation +[role="multi-language-sample"] +```groovy +tasks.named("nativeBuild") { + classpathJar = myFatJar +} +``` + +[role="multi-language-sample"] +```kotlin +tasks.named("nativeBuild") { + classpathJar.set(myFatJar) +} +``` + +When the `classpathJar` property is set, the `classpath` property is _ignored_. + [[testing]] === Testing support diff --git a/docs/src/docs/asciidoc/maven-plugin.adoc b/docs/src/docs/asciidoc/maven-plugin.adoc index f3f5180e5..90406896f 100644 --- a/docs/src/docs/asciidoc/maven-plugin.adoc +++ b/docs/src/docs/asciidoc/maven-plugin.adoc @@ -127,6 +127,7 @@ In this case, the arguments that will be passed to the `native-image` executable --no-fallback --verbose ``` +[[testing-support]] == JUnit Testing support In order to use the recommended test listener mode, you need to add following dependency: @@ -147,6 +148,47 @@ This step won't be needed as of JUnit 5.8 with a future release of native-maven- Running `mvn -Pnative -Dskip test` will then build and run native tests. +== Long classpath and shading support + +Under Windows, https://github.com/graalvm/native-build-tools/issues/85[it is possible that the length of the classpath exceeds what the operating system supports] when invoking the CLI to build a native image. + +If this happens, one option is to use a https://maven.apache.org/plugins/maven-shade-plugin[shaded jar] and use it instead of individual jars on classpath. + +First, you'll need to setup the https://maven.apache.org/plugins/maven-shade-plugin[Maven Shade plugin]: + +[source,xml,indent=0] +include::../../../../samples/java-application/pom.xml[tag=shade-plugin] + +Then the native plugin needs to be configured to use this jar instead of the full classpath: + +[source,xml,indent=0] +include::../../../../samples/java-application/pom.xml[tag=native-plugin] + +To be able to <>, you will need more setup: + +- Create a `src/assembly/test-jar-with-dependencies.xml` file with the following contents: + +[source,xml,indent=0] +include::../../../../samples/java-application-with-tests/src/assembly/test-jar-with-dependencies.xml[tag=assembly] + +- Add the assembly plugin to your `native` profile: + +[source,xml,indent=0] +include::../../../../samples/java-application-with-tests/pom.xml[tag=assembly-plugin] + +- Due to a limitation in Maven, you will need to move the tests execution to the "integration-test" phase: + +[source,xml,indent=0] +include::../../../../samples/java-application-with-tests/pom.xml[tag=native-plugin] + +Finally, you will need to execute tests using the `integration-test` phase instead of `test`: + +```bash +./mvn -Pnative integration-test +``` + +Please refer to the https://maven.apache.org/plugins/maven-shade-plugin[Maven Shade plugin documentation] for more details on how to configure shading and the https://maven.apache.org/plugins/maven-assembly-plugin[Maven Assembly plugin documentation] to tweak what to include in tests. + == Javadocs In addition, you can consult the link:javadocs/native-maven-plugin/index.html[Javadocs of the plugin]. diff --git a/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithResourcesFunctionalTest.groovy b/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithResourcesFunctionalTest.groovy index 83d1bd408..d6ba6a703 100644 --- a/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithResourcesFunctionalTest.groovy +++ b/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithResourcesFunctionalTest.groovy @@ -53,6 +53,11 @@ class JavaApplicationWithResourcesFunctionalTest extends AbstractFunctionalTest withSample("java-application-with-resources") buildFile << config + buildFile << """ + nativeBuild { + verbose = true + } + """ when: run 'nativeBuild' diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java index 61cc5390c..4fca359f4 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java @@ -47,17 +47,19 @@ import org.graalvm.buildtools.gradle.internal.GraalVMLogger; import org.graalvm.buildtools.gradle.internal.GradleUtils; import org.graalvm.buildtools.gradle.internal.ProcessGeneratedGraalResourceFiles; -import org.graalvm.buildtools.utils.SharedConstants; import org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask; import org.graalvm.buildtools.gradle.tasks.GenerateResourcesConfigFile; import org.graalvm.buildtools.gradle.tasks.NativeRunTask; +import org.graalvm.buildtools.utils.SharedConstants; import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; +import org.gradle.api.file.ArchiveOperations; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.DuplicatesStrategy; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.JavaApplication; @@ -69,15 +71,20 @@ import org.gradle.api.tasks.TaskCollection; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.testing.Test; import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.gradle.process.JavaForkOptions; import org.gradle.util.GFileUtils; +import javax.inject.Inject; import java.io.File; import java.util.Arrays; +import java.util.Locale; import java.util.concurrent.Callable; import java.util.function.Consumer; +import java.util.stream.Collectors; import static org.graalvm.buildtools.gradle.internal.GradleUtils.findConfiguration; import static org.graalvm.buildtools.gradle.internal.GradleUtils.findMainArtifacts; @@ -112,6 +119,11 @@ public class NativeImagePlugin implements Plugin { private GraalVMLogger logger; + @Inject + public ArchiveOperations getArchiveOperations() { + throw new UnsupportedOperationException(); + } + @Override public void apply(Project project) { Provider nativeImageServiceProvider = NativeImageService.registerOn(project); @@ -148,6 +160,7 @@ private void configureJavaProject(Project project, Provider task.getImage().convention(imageBuilder.map(t -> t.getOutputFile().get())); task.getRuntimeArgs().convention(buildExtension.getRuntimeArgs()); }); + configureClasspathJarFor(tasks, buildExtension, imageBuilder); TaskProvider processAgentFiles = registerProcessAgentFilesTask(project, PROCESS_AGENT_RESOURCES_TASK_NAME); @@ -217,6 +230,7 @@ private void configureJavaProject(Project project, Provider testExtension.getClasspath().from(testList); task.getAgentEnabled().set(testAgent); }); + configureClasspathJarFor(tasks, testExtension, testImageBuilder); tasks.register(NATIVE_TEST_TASK_NAME, NativeRunTask.class, task -> { task.setDescription("Runs native-image compiled tests."); @@ -226,6 +240,26 @@ private void configureJavaProject(Project project, Provider }); } + private void configureClasspathJarFor(TaskContainer tasks, NativeImageOptions options, TaskProvider imageBuilder) { + String baseName = imageBuilder.getName(); + TaskProvider classpathJar = tasks.register(baseName + "ClasspathJar", Jar.class, jar -> { + jar.from( + options.getClasspath() + .getElements() + .map(elems -> elems.stream() + .map(e -> getArchiveOperations().zipTree(e)) + .collect(Collectors.toList())) + ); + jar.setDuplicatesStrategy(DuplicatesStrategy.WARN); + jar.getArchiveBaseName().set(baseName.toLowerCase(Locale.ENGLISH) + "-classpath"); + }); + imageBuilder.configure(nit -> { + if (options.getUseFatJar().get()) { + nit.getClasspathJar().set(classpathJar.flatMap(AbstractArchiveTask::getArchiveFile)); + } + }); + } + private TaskProvider registerResourcesConfigTask(Provider generatedDir, NativeImageOptions options, TaskContainer tasks, diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java index 29f8e6e9b..839af3ff9 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java @@ -42,6 +42,7 @@ package org.graalvm.buildtools.gradle.dsl; import org.graalvm.buildtools.gradle.internal.GradleUtils; +import org.graalvm.buildtools.utils.SharedConstants; import org.gradle.api.Action; import org.gradle.api.JavaVersion; import org.gradle.api.Project; @@ -210,6 +211,7 @@ public NativeImageOptions(ObjectFactory objectFactory, getAgent().convention(false); getSharedLibrary().convention(false); getImageName().convention(defaultImageName); + getUseFatJar().convention(SharedConstants.IS_WINDOWS); getJavaLauncher().convention( toolchains.launcherFor(spec -> { spec.getLanguageVersion().set(JavaLanguageVersion.of(JavaVersion.current().getMajorVersion())); @@ -355,4 +357,14 @@ public NativeImageOptions runtimeArgs(Iterable arguments) { return this; } + /** + * If set to true, this will build a fat jar of the image classpath + * instead of passing each jar individually to the classpath. This + * option can be used in case the classpath is too long and that + * invoking native image fails, which can happen on Windows. + * + * @return true if a fatjar should be used. Defaults to true for Windows, and false otherwise. + */ + @Input + public abstract Property getUseFatJar(); } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java index 67e51f3fc..ef9691497 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java @@ -44,8 +44,10 @@ import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; import org.gradle.api.Transformer; import org.gradle.api.file.FileSystemLocation; +import org.gradle.api.file.RegularFile; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Nested; import org.gradle.process.CommandLineArgumentProvider; @@ -62,15 +64,18 @@ public class NativeImageCommandLineProvider implements CommandLineArgumentProvid private final Provider agentEnabled; private final Provider executableName; private final Provider outputDirectory; + private final Provider classpathJar; public NativeImageCommandLineProvider(Provider options, Provider agentEnabled, Provider executableName, - Provider outputDirectory) { + Provider outputDirectory, + Provider classpathJar) { this.options = options; this.agentEnabled = agentEnabled; this.executableName = executableName; this.outputDirectory = outputDirectory; + this.classpathJar = classpathJar; } @Nested @@ -93,13 +98,22 @@ public Provider getOutputDirectory() { return outputDirectory; } + @InputFile + public Provider getClasspathJar() { + return classpathJar; + } + @Override public List asArguments() { NativeImageOptions options = getOptions().get(); List cliArgs = new ArrayList<>(20); cliArgs.add("-cp"); - cliArgs.add(options.getClasspath().getAsPath()); + if (classpathJar.isPresent()) { + cliArgs.add(classpathJar.get().getAsFile().getAbsolutePath()); + } else { + cliArgs.add(options.getClasspath().getAsPath()); + } appendBooleanOption(cliArgs, options.getDebug(), "-H:GenerateDebugInfo=1"); appendBooleanOption(cliArgs, options.getFallback().map(NEGATE), "--no-fallback"); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java index 51ffd49d3..f7ecd1b2f 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java @@ -49,10 +49,12 @@ import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.RegularFile; +import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.Optional; @@ -112,6 +114,10 @@ public Provider getOutputFile() { @Inject protected abstract ProviderFactory getProviders(); + @InputFile + @Optional + public abstract RegularFileProperty getClasspathJar(); + public BuildNativeImageTask() { DirectoryProperty buildDir = getProject().getLayout().getBuildDirectory(); Provider outputDir = buildDir.dir("native/" + getName()); @@ -132,8 +138,8 @@ private List buildActualCommandLineArgs() { getExecutableName(), // Can't use getOutputDirectory().map(...) because Gradle would complain that we use // a mapped value before the task was called, when we are actually calling it... - getProviders().provider(() -> getOutputDirectory().getAsFile().get().getAbsolutePath()) - ).asArguments(); + getProviders().provider(() -> getOutputDirectory().getAsFile().get().getAbsolutePath()), + getClasspathJar()).asArguments(); } // This property provides access to the service instance diff --git a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationFunctionalTest.groovy b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationFunctionalTest.groovy index 88e60ff63..905c87b58 100644 --- a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationFunctionalTest.groovy +++ b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationFunctionalTest.groovy @@ -52,4 +52,15 @@ class JavaApplicationFunctionalTest extends AbstractGraalVMMavenFunctionalTest { buildSucceeded outputContains "Hello, native!" } + + def "can build and execute a native image with the Maven plugin and the shade plugin"() { + withSample("java-application") + + when: + mvn '-Pshaded', '-DskipTests', 'package', 'exec:exec@native' + + then: + buildSucceeded + outputContains "Hello, native!" + } } diff --git a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithTestsFunctionalTest.groovy b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithTestsFunctionalTest.groovy index fc0d0569a..883ef47f6 100644 --- a/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithTestsFunctionalTest.groovy +++ b/native-maven-plugin/src/functionalTest/groovy/org/graalvm/buildtools/maven/JavaApplicationWithTestsFunctionalTest.groovy @@ -64,6 +64,31 @@ class JavaApplicationWithTestsFunctionalTest extends AbstractGraalVMMavenFunctio [ 0 tests aborted ] [ 6 tests successful ] [ 0 tests failed ] +""".trim() + } + + def "can run tests in a native image with the Maven plugin using shading"() { + withSample("java-application-with-tests") + + when: + mvn '-Pshaded', 'integration-test' + + then: + buildSucceeded + outputContains "[junit-platform-native] Running in 'test listener' mode" + outputContains """ +[ 3 containers found ] +[ 0 containers skipped ] +[ 3 containers started ] +[ 0 containers aborted ] +[ 3 containers successful ] +[ 0 containers failed ] +[ 6 tests found ] +[ 0 tests skipped ] +[ 6 tests started ] +[ 0 tests aborted ] +[ 6 tests successful ] +[ 0 tests failed ] """.trim() } } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java index dc13e63f7..6d5c121f7 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java @@ -63,6 +63,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -94,7 +95,10 @@ public class NativeBuildMojo extends AbstractNativeMojo { @Parameter(defaultValue = "${mojoExecution}") private MojoExecution mojoExecution; - private final List classpath = new ArrayList<>(); + @Parameter(property = "classpath") + private List classpath; + + private final List imageClasspath = new ArrayList<>(); private PluginParameterExpressionEvaluator evaluator; @@ -105,14 +109,18 @@ public void execute() throws MojoExecutionException { } evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); - classpath.clear(); - List imageClasspathScopes = Arrays.asList(Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME); - project.setArtifactFilter(artifact -> imageClasspathScopes.contains(artifact.getScope())); - for (Artifact dependency : project.getArtifacts()) { - addClasspath(dependency); + imageClasspath.clear(); + if (classpath != null && !classpath.isEmpty()) { + imageClasspath.addAll(classpath.stream().map(Paths::get).collect(Collectors.toList())); + } else { + List imageClasspathScopes = Arrays.asList(Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME); + project.setArtifactFilter(artifact -> imageClasspathScopes.contains(artifact.getScope())); + for (Artifact dependency : project.getArtifacts()) { + addClasspath(dependency); + } + addClasspath(project.getArtifact()); } - addClasspath(project.getArtifact()); - String classpathStr = classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)); + String classpathStr = imageClasspath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)); Path nativeImageExecutable = Utils.getNativeImage(); @@ -170,7 +178,7 @@ private void addClasspath(Artifact artifact) throws MojoExecutionException { throw new MojoExecutionException("Artifact " + artifact + "cannot be added to image classpath", e); } - classpath.add(jarFilePath); + imageClasspath.add(jarFilePath); } private Path getWorkingDirectory() { diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java index e980242a9..0ac5e4b49 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java @@ -79,6 +79,9 @@ public class NativeTestMojo extends AbstractNativeMojo { @Parameter(property = "skipTests", defaultValue = "false") private boolean skipTests; + @Parameter(property = "classpath") + private List classpath; + @Override public void execute() throws MojoExecutionException, MojoFailureException { if (skipTests) { @@ -182,6 +185,9 @@ private void runTests(Path targetFolder) throws MojoExecutionException { } private String getClassPath() throws MojoFailureException { + if (classpath != null && !classpath.isEmpty()) { + return String.join(File.pathSeparator, classpath); + } try { List pluginDependencies = pluginArtifacts.stream() .filter(it -> it.getGroupId().startsWith(Utils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit")) diff --git a/samples/java-application-with-tests/pom.xml b/samples/java-application-with-tests/pom.xml index 6724b1bcc..c96affdb8 100644 --- a/samples/java-application-with-tests/pom.xml +++ b/samples/java-application-with-tests/pom.xml @@ -109,10 +109,97 @@ + + shaded + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + true + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4.1 + + + src/assembly/test-jar-with-dependencies.xml + + + + + make-test-jar + package + + single + + + + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + + + test-native + + test + + integration-test + + + ${project.build.directory}/${project.artifactId}-${project.version}-tests.jar + + + + + build-native + + build + + package + + + ${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar + + + + + + false + ${imageName} + + --no-fallback + + + + + + + - ${project.artifactId} org.apache.maven.plugins diff --git a/samples/java-application-with-tests/src/assembly/test-jar-with-dependencies.xml b/samples/java-application-with-tests/src/assembly/test-jar-with-dependencies.xml new file mode 100644 index 000000000..319f48403 --- /dev/null +++ b/samples/java-application-with-tests/src/assembly/test-jar-with-dependencies.xml @@ -0,0 +1,71 @@ + + + + + + tests + + jar + + + + ${project.build.directory}/test-classes + / + + + ${project.build.outputDirectory} + / + + + false + + + / + true + true + test + + + + diff --git a/samples/java-application/pom.xml b/samples/java-application/pom.xml index 000a1ce14..ededfa220 100644 --- a/samples/java-application/pom.xml +++ b/samples/java-application/pom.xml @@ -95,6 +95,58 @@ + + shaded + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + true + + + + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native.maven.plugin.version} + + + build-native + + build + + package + + + + false + ${imageName} + + --no-fallback + + + ${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar + + + + + + +