diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java
index faff6f520f770..205c0be2ab3bf 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java
@@ -21,12 +21,14 @@ public class PackageConfig {
public static final String LEGACY = "legacy";
public static final String LEGACY_JAR = "legacy-jar";
public static final String NATIVE = "native";
+ // does everything 'native' but stops short of actually executing the 'native-image' command
+ public static final String NATIVE_SOURCES = "native-sources";
/**
* The requested output type.
*
* The default built in types are 'jar' (which will use 'fast-jar'), 'legacy-jar' for the pre-1.12 default jar
- * packaging, 'uber-jar' and 'native'.
+ * packaging, 'uber-jar', 'native' and 'native-sources'.
*/
@ConfigItem(defaultValue = JAR)
public String type;
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
index 502bbcb1d5ccf..85d8cd4da22ef 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildStep.java
@@ -1,8 +1,10 @@
package io.quarkus.deployment.pkg.steps;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -29,6 +31,7 @@
import io.quarkus.deployment.pkg.NativeConfig;
import io.quarkus.deployment.pkg.PackageConfig;
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
+import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.deployment.pkg.builditem.NativeImageSourceJarBuildItem;
@@ -67,6 +70,55 @@ ArtifactResultBuildItem result(NativeImageBuildItem image) {
return new ArtifactResultBuildItem(image.getPath(), PackageConfig.NATIVE, Collections.emptyMap());
}
+ @BuildStep(onlyIf = NativeSourcesBuild.class)
+ ArtifactResultBuildItem nativeSourcesResult(NativeConfig nativeConfig,
+ BuildSystemTargetBuildItem buildSystemTargetBuildItem,
+ NativeImageSourceJarBuildItem nativeImageSourceJarBuildItem,
+ OutputTargetBuildItem outputTargetBuildItem,
+ PackageConfig packageConfig,
+ List nativeImageProperties) {
+
+ Path outputDir;
+ try {
+ outputDir = buildSystemTargetBuildItem.getOutputDirectory().resolve("native-sources");
+ IoUtils.createOrEmptyDir(outputDir);
+ IoUtils.copy(nativeImageSourceJarBuildItem.getPath().getParent(), outputDir);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Unable to create native-sources output directory", e);
+ }
+
+ Path runnerJar = outputDir.resolve(nativeImageSourceJarBuildItem.getPath().getFileName());
+
+ String nativeImageName = getResultingBinaryName(outputTargetBuildItem, packageConfig, isContainerBuild(nativeConfig));
+
+ NativeImageInvokerInfo nativeImageArgs = new NativeImageInvokerInfo.Builder()
+ .setNativeConfig(nativeConfig)
+ .setOutputTargetBuildItem(outputTargetBuildItem)
+ .setNativeImageProperties(nativeImageProperties)
+ .setOutputDir(outputDir)
+ .setRunnerJarName(runnerJar.getFileName().toString())
+ // the path to native-image is not known now, it is only known at the time the native-sources will be consumed
+ .setResultingBinaryName(nativeImageName)
+ .setContainerBuild(nativeConfig.containerRuntime.isPresent() || nativeConfig.containerBuild)
+ .build();
+ List command = nativeImageArgs.getArgs();
+ try (FileOutputStream commandFOS = new FileOutputStream(outputDir.resolve("native-image.args").toFile())) {
+ String commandStr = String.join(" ", command);
+ commandFOS.write(commandStr.getBytes(StandardCharsets.UTF_8));
+
+ log.info("The sources for a subsequent native-image run along with the necessary arguments can be found in "
+ + outputDir);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to build native image sources", e);
+ }
+
+ // drop the original output to avoid confusion
+ IoUtils.recursiveDelete(nativeImageSourceJarBuildItem.getPath().getParent());
+
+ return new ArtifactResultBuildItem(nativeImageSourceJarBuildItem.getPath(), PackageConfig.NATIVE_SOURCES,
+ Collections.emptyMap());
+ }
+
@BuildStep
public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJarBuildItem nativeImageSourceJarBuildItem,
OutputTargetBuildItem outputTargetBuildItem,
@@ -92,11 +144,7 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
noPIE = detectNoPIE();
}
- String nativeImageName = outputTargetBuildItem.getBaseName() + packageConfig.runnerSuffix;
- if (SystemUtils.IS_OS_WINDOWS && !(isContainerBuild)) {
- //once image is generated it gets added .exe on Windows
- nativeImageName = nativeImageName + ".exe";
- }
+ String nativeImageName = getResultingBinaryName(outputTargetBuildItem, packageConfig, isContainerBuild);
NativeImageBuildRunner buildRunner = getNativeImageBuildRunner(nativeConfig, outputDir, nativeImageName);
buildRunner.setup(processInheritIODisabled.isPresent());
@@ -109,148 +157,28 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
}
try {
- List command = new ArrayList<>();
if (nativeConfig.cleanupServer && !graalVMVersion.isMandrel()) {
buildRunner.cleanupServer(outputDir.toFile(), processInheritIODisabled.isPresent());
}
- boolean enableSslNative = false;
- for (NativeImageSystemPropertyBuildItem prop : nativeImageProperties) {
- //todo: this should be specific build items
- if (prop.getKey().equals("quarkus.ssl.native") && prop.getValue() != null) {
- enableSslNative = Boolean.parseBoolean(prop.getValue());
- } else if (prop.getKey().equals("quarkus.jni.enable") && prop.getValue().equals("false")) {
- log.warn("Your application is setting the deprecated 'quarkus.jni.enable' configuration key to false."
- + " Please consider removing this configuration key as it is ignored (JNI is always enabled) and it"
- + " will be removed in a future Quarkus version.");
- } else if (prop.getKey().equals("quarkus.native.enable-all-security-services") && prop.getValue() != null) {
- nativeConfig.enableAllSecurityServices |= Boolean.parseBoolean(prop.getValue());
- } else if (prop.getKey().equals("quarkus.native.enable-all-charsets") && prop.getValue() != null) {
- nativeConfig.addAllCharsets |= Boolean.parseBoolean(prop.getValue());
- } else {
- // todo maybe just -D is better than -J-D in this case
- if (prop.getValue() == null) {
- command.add("-J-D" + prop.getKey());
- } else {
- command.add("-J-D" + prop.getKey() + "=" + prop.getValue());
- }
- }
- }
- if (nativeConfig.userLanguage.isPresent()) {
- command.add("-J-Duser.language=" + nativeConfig.userLanguage.get());
- }
- if (nativeConfig.userCountry.isPresent()) {
- command.add("-J-Duser.country=" + nativeConfig.userCountry.get());
- }
- command.add("-J-Dfile.encoding=" + nativeConfig.fileEncoding);
- if (enableSslNative) {
- nativeConfig.enableHttpsUrlHandler = true;
- nativeConfig.enableAllSecurityServices = true;
- }
+ NativeImageInvokerInfo commandAndExecutable = new NativeImageInvokerInfo.Builder()
+ .setNativeConfig(nativeConfig)
+ .setOutputTargetBuildItem(outputTargetBuildItem)
+ .setNativeImageProperties(nativeImageProperties)
+ .setOutputDir(outputDir)
+ .setRunnerJarName(runnerJarName)
+ .setResultingBinaryName(nativeImageName)
+ .setNoPIE(noPIE)
+ .setContainerBuild(isContainerBuild)
+ .setGraalVMVersion(graalVMVersion)
+ .build();
- handleAdditionalProperties(nativeConfig, command, isContainerBuild, outputDir);
- command.add("--initialize-at-build-time=");
- command.add(
- "-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime"); //the default collection policy results in full GC's 50% of the time
- command.add("-H:+JNI");
- command.add("-H:+AllowFoldMethods");
- command.add("-jar");
- command.add(runnerJarName);
+ List nativeImageArgs = commandAndExecutable.args;
- if (nativeConfig.enableFallbackImages) {
- command.add("-H:FallbackThreshold=5");
- } else {
- //Default: be strict as those fallback images aren't very useful
- //and tend to cover up real problems.
- command.add("-H:FallbackThreshold=0");
- }
-
- if (nativeConfig.reportErrorsAtRuntime) {
- command.add("-H:+ReportUnsupportedElementsAtRuntime");
- }
- if (nativeConfig.reportExceptionStackTraces) {
- command.add("-H:+ReportExceptionStackTraces");
- }
- if (nativeConfig.debug.enabled) {
- command.add("-g");
- command.add("-H:DebugInfoSourceSearchPath=" + APP_SOURCES);
- }
- if (nativeConfig.debugBuildProcess) {
- command.add("-J-Xrunjdwp:transport=dt_socket,address=" + DEBUG_BUILD_PROCESS_PORT + ",server=y,suspend=y");
- }
- if (nativeConfig.enableReports) {
- command.add("-H:+PrintAnalysisCallTree");
- }
- if (nativeConfig.dumpProxies) {
- command.add("-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true");
- if (nativeConfig.enableServer) {
- log.warn(
- "Options dumpProxies and enableServer are both enabled: this will get the proxies dumped in an unknown external working directory");
- }
- }
- if (nativeConfig.nativeImageXmx.isPresent()) {
- command.add("-J-Xmx" + nativeConfig.nativeImageXmx.get());
- }
- List protocols = new ArrayList<>(2);
- if (nativeConfig.enableHttpUrlHandler) {
- protocols.add("http");
- }
- if (nativeConfig.enableHttpsUrlHandler) {
- protocols.add("https");
- }
- if (nativeConfig.addAllCharsets) {
- command.add("-H:+AddAllCharsets");
- } else {
- command.add("-H:-AddAllCharsets");
- }
- if (!protocols.isEmpty()) {
- command.add("-H:EnableURLProtocols=" + String.join(",", protocols));
- }
- if (nativeConfig.enableAllSecurityServices) {
- command.add("--enable-all-security-services");
- }
- if (!noPIE.isEmpty()) {
- command.add("-H:NativeLinkerOption=" + noPIE);
- }
-
- if (!nativeConfig.enableIsolates) {
- command.add("-H:-SpawnIsolates");
- }
- if (!nativeConfig.enableJni) {
- log.warn("Your application is setting the deprecated 'quarkus.native.enable-jni' configuration key to false."
- + " Please consider removing this configuration key as it is ignored (JNI is always enabled) and it"
- + " will be removed in a future Quarkus version.");
- }
- if (!nativeConfig.enableServer && !SystemUtils.IS_OS_WINDOWS && !graalVMVersion.isMandrel()) {
- command.add("--no-server");
- }
- if (nativeConfig.enableVmInspection) {
- command.add("-H:+AllowVMInspection");
- }
- if (nativeConfig.autoServiceLoaderRegistration) {
- command.add("-H:+UseServiceLoaderFeature");
- //When enabling, at least print what exactly is being added:
- command.add("-H:+TraceServiceLoaderFeature");
- } else {
- command.add("-H:-UseServiceLoaderFeature");
- }
- if (nativeConfig.fullStackTraces) {
- command.add("-H:+StackTrace");
- } else {
- command.add("-H:-StackTrace");
- }
-
- if (nativeConfig.enableDashboardDump) {
- command.add("-H:DashboardDump=" + outputTargetBuildItem.getBaseName() + "_dashboard.dump");
- command.add("-H:+DashboardAll");
- }
-
- command.add(nativeImageName);
-
- log.info(String.join(" ", command).replace("$", "\\$"));
- int exitCode = buildRunner.build(command, outputDir, processInheritIODisabled.isPresent());
+ log.info(String.join(" ", nativeImageArgs).replace("$", "\\$"));
+ int exitCode = buildRunner.build(nativeImageArgs, outputDir, processInheritIODisabled.isPresent());
if (exitCode != 0) {
- throw imageGenerationFailed(exitCode, command);
+ throw imageGenerationFailed(exitCode, nativeImageArgs);
}
Path generatedImage = outputDir.resolve(nativeImageName);
Path finalPath = outputTargetBuildItem.getOutputDirectory().resolve(nativeImageName);
@@ -287,6 +215,16 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
}
}
+ private String getResultingBinaryName(OutputTargetBuildItem outputTargetBuildItem, PackageConfig packageConfig,
+ boolean isContainerBuild) {
+ String nativeImageName = outputTargetBuildItem.getBaseName() + packageConfig.runnerSuffix;
+ if (SystemUtils.IS_OS_WINDOWS && !isContainerBuild) {
+ //once image is generated it gets added .exe on Windows
+ nativeImageName = nativeImageName + ".exe";
+ }
+ return nativeImageName;
+ }
+
public static boolean isContainerBuild(NativeConfig nativeConfig) {
return nativeConfig.containerRuntime.isPresent() || nativeConfig.containerBuild || nativeConfig.remoteContainerBuild;
}
@@ -394,40 +332,6 @@ private static void copySourcesToSourceCache(OutputTargetBuildItem outputTargetB
}
}
- private void handleAdditionalProperties(NativeConfig nativeConfig, List command, boolean isContainerBuild,
- Path outputDir) {
- if (nativeConfig.additionalBuildArgs.isPresent()) {
- List strings = nativeConfig.additionalBuildArgs.get();
- for (String buildArg : strings) {
- String trimmedBuildArg = buildArg.trim();
- if (trimmedBuildArg.contains(TRUST_STORE_SYSTEM_PROPERTY_MARKER) && isContainerBuild) {
- /*
- * When the native binary is being built with a docker container, because a volume is created,
- * we need to copy the trustStore file into the output directory (which is the root of volume)
- * and change the value of 'javax.net.ssl.trustStore' property to point to this value
- *
- * TODO: we might want to introduce a dedicated property in order to overcome this ugliness
- */
- int index = trimmedBuildArg.indexOf(TRUST_STORE_SYSTEM_PROPERTY_MARKER);
- if (trimmedBuildArg.length() > index + 2) {
- String configuredTrustStorePath = trimmedBuildArg
- .substring(index + TRUST_STORE_SYSTEM_PROPERTY_MARKER.length());
- try {
- IoUtils.copy(Paths.get(configuredTrustStorePath), outputDir.resolve(MOVED_TRUST_STORE_NAME));
- command.add(trimmedBuildArg.substring(0, index) + TRUST_STORE_SYSTEM_PROPERTY_MARKER
- + CONTAINER_BUILD_VOLUME_PATH + "/" + MOVED_TRUST_STORE_NAME);
- } catch (IOException e) {
- throw new UncheckedIOException("Unable to copy trustStore file '" + configuredTrustStorePath
- + "' to volume root directory '" + outputDir.toAbsolutePath().toString() + "'", e);
- }
- }
- } else {
- command.add(trimmedBuildArg);
- }
- }
- }
- }
-
private RuntimeException imageGenerationFailed(int exitValue, List command) {
if (exitValue == OOM_ERROR_VALUE) {
if (command.contains("docker") && !SystemUtils.IS_OS_LINUX) {
@@ -687,4 +591,252 @@ enum Distribution {
MANDREL;
}
}
+
+ private static class NativeImageInvokerInfo {
+ private final List args;
+
+ private NativeImageInvokerInfo(List args) {
+ this.args = args;
+ }
+
+ List getArgs() {
+ return args;
+ }
+
+ static class Builder {
+ private NativeConfig nativeConfig;
+ private OutputTargetBuildItem outputTargetBuildItem;
+ private List nativeImageProperties;
+ private Path outputDir;
+ private String runnerJarName;
+ private String noPIE = "";
+ private boolean isContainerBuild = false;
+ private GraalVM.Version graalVMVersion = GraalVM.Version.UNVERSIONED;
+ private String resultingBinaryName;
+
+ public Builder setNativeConfig(NativeConfig nativeConfig) {
+ this.nativeConfig = nativeConfig;
+ return this;
+ }
+
+ public Builder setOutputTargetBuildItem(OutputTargetBuildItem outputTargetBuildItem) {
+ this.outputTargetBuildItem = outputTargetBuildItem;
+ return this;
+ }
+
+ public Builder setNativeImageProperties(List nativeImageProperties) {
+ this.nativeImageProperties = nativeImageProperties;
+ return this;
+ }
+
+ public Builder setOutputDir(Path outputDir) {
+ this.outputDir = outputDir;
+ return this;
+ }
+
+ public Builder setRunnerJarName(String runnerJarName) {
+ this.runnerJarName = runnerJarName;
+ return this;
+ }
+
+ public Builder setNoPIE(String noPIE) {
+ this.noPIE = noPIE;
+ return this;
+ }
+
+ public Builder setContainerBuild(boolean containerBuild) {
+ isContainerBuild = containerBuild;
+ return this;
+ }
+
+ public Builder setGraalVMVersion(GraalVM.Version graalVMVersion) {
+ this.graalVMVersion = graalVMVersion;
+ return this;
+ }
+
+ public Builder setResultingBinaryName(String resultingBinaryName) {
+ this.resultingBinaryName = resultingBinaryName;
+ return this;
+ }
+
+ public NativeImageInvokerInfo build() {
+ List nativeImageArgs = new ArrayList<>();
+ boolean enableSslNative = false;
+ boolean enableAllSecurityServices = nativeConfig.enableAllSecurityServices;
+ boolean addAllCharsets = nativeConfig.addAllCharsets;
+ boolean enableHttpsUrlHandler = nativeConfig.enableHttpsUrlHandler;
+ for (NativeImageSystemPropertyBuildItem prop : nativeImageProperties) {
+ //todo: this should be specific build items
+ if (prop.getKey().equals("quarkus.ssl.native") && prop.getValue() != null) {
+ enableSslNative = Boolean.parseBoolean(prop.getValue());
+ } else if (prop.getKey().equals("quarkus.jni.enable") && prop.getValue().equals("false")) {
+ log.warn("Your application is setting the deprecated 'quarkus.jni.enable' configuration key to false."
+ + " Please consider removing this configuration key as it is ignored (JNI is always enabled) and it"
+ + " will be removed in a future Quarkus version.");
+ } else if (prop.getKey().equals("quarkus.native.enable-all-security-services") && prop.getValue() != null) {
+ enableAllSecurityServices |= Boolean.parseBoolean(prop.getValue());
+ } else if (prop.getKey().equals("quarkus.native.enable-all-charsets") && prop.getValue() != null) {
+ addAllCharsets |= Boolean.parseBoolean(prop.getValue());
+ } else {
+ // todo maybe just -D is better than -J-D in this case
+ if (prop.getValue() == null) {
+ nativeImageArgs.add("-J-D" + prop.getKey());
+ } else {
+ nativeImageArgs.add("-J-D" + prop.getKey() + "=" + prop.getValue());
+ }
+ }
+ }
+ if (nativeConfig.userLanguage.isPresent()) {
+ nativeImageArgs.add("-J-Duser.language=" + nativeConfig.userLanguage.get());
+ }
+ if (nativeConfig.userCountry.isPresent()) {
+ nativeImageArgs.add("-J-Duser.country=" + nativeConfig.userCountry.get());
+ }
+ nativeImageArgs.add("-J-Dfile.encoding=" + nativeConfig.fileEncoding);
+
+ if (enableSslNative) {
+ enableHttpsUrlHandler = true;
+ enableAllSecurityServices = true;
+ }
+
+ handleAdditionalProperties(nativeConfig, nativeImageArgs, isContainerBuild, outputDir);
+ nativeImageArgs.add("--initialize-at-build-time=");
+ nativeImageArgs.add(
+ "-H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime"); //the default collection policy results in full GC's 50% of the time
+ nativeImageArgs.add("-H:+JNI");
+ nativeImageArgs.add("-H:+AllowFoldMethods");
+ nativeImageArgs.add("-jar");
+ nativeImageArgs.add(runnerJarName);
+
+ if (nativeConfig.enableFallbackImages) {
+ nativeImageArgs.add("-H:FallbackThreshold=5");
+ } else {
+ //Default: be strict as those fallback images aren't very useful
+ //and tend to cover up real problems.
+ nativeImageArgs.add("-H:FallbackThreshold=0");
+ }
+
+ if (nativeConfig.reportErrorsAtRuntime) {
+ nativeImageArgs.add("-H:+ReportUnsupportedElementsAtRuntime");
+ }
+ if (nativeConfig.reportExceptionStackTraces) {
+ nativeImageArgs.add("-H:+ReportExceptionStackTraces");
+ }
+ if (nativeConfig.debug.enabled) {
+ nativeImageArgs.add("-g");
+ nativeImageArgs.add("-H:DebugInfoSourceSearchPath=" + APP_SOURCES);
+ }
+ if (nativeConfig.debugBuildProcess) {
+ nativeImageArgs
+ .add("-J-Xrunjdwp:transport=dt_socket,address=" + DEBUG_BUILD_PROCESS_PORT + ",server=y,suspend=y");
+ }
+ if (nativeConfig.enableReports) {
+ nativeImageArgs.add("-H:+PrintAnalysisCallTree");
+ }
+ if (nativeConfig.dumpProxies) {
+ nativeImageArgs.add("-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true");
+ if (nativeConfig.enableServer) {
+ log.warn(
+ "Options dumpProxies and enableServer are both enabled: this will get the proxies dumped in an unknown external working directory");
+ }
+ }
+ if (nativeConfig.nativeImageXmx.isPresent()) {
+ nativeImageArgs.add("-J-Xmx" + nativeConfig.nativeImageXmx.get());
+ }
+ List protocols = new ArrayList<>(2);
+ if (nativeConfig.enableHttpUrlHandler) {
+ protocols.add("http");
+ }
+ if (enableHttpsUrlHandler) {
+ protocols.add("https");
+ }
+ if (addAllCharsets) {
+ nativeImageArgs.add("-H:+AddAllCharsets");
+ } else {
+ nativeImageArgs.add("-H:-AddAllCharsets");
+ }
+ if (!protocols.isEmpty()) {
+ nativeImageArgs.add("-H:EnableURLProtocols=" + String.join(",", protocols));
+ }
+ if (enableAllSecurityServices) {
+ nativeImageArgs.add("--enable-all-security-services");
+ }
+ if (!noPIE.isEmpty()) {
+ nativeImageArgs.add("-H:NativeLinkerOption=" + noPIE);
+ }
+
+ if (!nativeConfig.enableIsolates) {
+ nativeImageArgs.add("-H:-SpawnIsolates");
+ }
+ if (!nativeConfig.enableJni) {
+ log.warn(
+ "Your application is setting the deprecated 'quarkus.native.enable-jni' configuration key to false."
+ + " Please consider removing this configuration key as it is ignored (JNI is always enabled) and it"
+ + " will be removed in a future Quarkus version.");
+ }
+ if (!nativeConfig.enableServer && !SystemUtils.IS_OS_WINDOWS && !graalVMVersion.isMandrel()) {
+ nativeImageArgs.add("--no-server");
+ }
+ if (nativeConfig.enableVmInspection) {
+ nativeImageArgs.add("-H:+AllowVMInspection");
+ }
+ if (nativeConfig.autoServiceLoaderRegistration) {
+ nativeImageArgs.add("-H:+UseServiceLoaderFeature");
+ //When enabling, at least print what exactly is being added:
+ nativeImageArgs.add("-H:+TraceServiceLoaderFeature");
+ } else {
+ nativeImageArgs.add("-H:-UseServiceLoaderFeature");
+ }
+ if (nativeConfig.fullStackTraces) {
+ nativeImageArgs.add("-H:+StackTrace");
+ } else {
+ nativeImageArgs.add("-H:-StackTrace");
+ }
+
+ if (nativeConfig.enableDashboardDump) {
+ nativeImageArgs.add("-H:DashboardDump=" + outputTargetBuildItem.getBaseName() + "_dashboard.dump");
+ nativeImageArgs.add("-H:+DashboardAll");
+ }
+
+ nativeImageArgs.add(resultingBinaryName);
+
+ return new NativeImageInvokerInfo(nativeImageArgs);
+ }
+
+ private void handleAdditionalProperties(NativeConfig nativeConfig, List command, boolean isContainerBuild,
+ Path outputDir) {
+ if (nativeConfig.additionalBuildArgs.isPresent()) {
+ List strings = nativeConfig.additionalBuildArgs.get();
+ for (String buildArg : strings) {
+ String trimmedBuildArg = buildArg.trim();
+ if (trimmedBuildArg.contains(TRUST_STORE_SYSTEM_PROPERTY_MARKER) && isContainerBuild) {
+ /*
+ * When the native binary is being built with a docker container, because a volume is created,
+ * we need to copy the trustStore file into the output directory (which is the root of volume)
+ * and change the value of 'javax.net.ssl.trustStore' property to point to this value
+ *
+ * TODO: we might want to introduce a dedicated property in order to overcome this ugliness
+ */
+ int index = trimmedBuildArg.indexOf(TRUST_STORE_SYSTEM_PROPERTY_MARKER);
+ if (trimmedBuildArg.length() > index + 2) {
+ String configuredTrustStorePath = trimmedBuildArg
+ .substring(index + TRUST_STORE_SYSTEM_PROPERTY_MARKER.length());
+ try {
+ IoUtils.copy(Paths.get(configuredTrustStorePath),
+ outputDir.resolve(MOVED_TRUST_STORE_NAME));
+ command.add(trimmedBuildArg.substring(0, index) + TRUST_STORE_SYSTEM_PROPERTY_MARKER
+ + CONTAINER_BUILD_VOLUME_PATH + "/" + MOVED_TRUST_STORE_NAME);
+ } catch (IOException e) {
+ throw new UncheckedIOException("Unable to copy trustStore file '" + configuredTrustStorePath
+ + "' to volume root directory '" + outputDir.toAbsolutePath().toString() + "'", e);
+ }
+ }
+ } else {
+ command.add(trimmedBuildArg);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeSourcesBuild.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeSourcesBuild.java
new file mode 100644
index 0000000000000..ecac3a5fb9f22
--- /dev/null
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeSourcesBuild.java
@@ -0,0 +1,23 @@
+package io.quarkus.deployment.pkg.steps;
+
+import java.util.function.BooleanSupplier;
+
+import io.quarkus.deployment.pkg.PackageConfig;
+
+/**
+ * Supplier that can be used to only run build steps in the
+ * native sources build.
+ */
+public class NativeSourcesBuild implements BooleanSupplier {
+
+ private final PackageConfig packageConfig;
+
+ NativeSourcesBuild(PackageConfig packageConfig) {
+ this.packageConfig = packageConfig;
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return packageConfig.type.equalsIgnoreCase(PackageConfig.NATIVE_SOURCES);
+ }
+}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/PackageTypeVerificationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/PackageTypeVerificationBuildStep.java
index eeeb669232926..d1df71dfe5814 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/PackageTypeVerificationBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/PackageTypeVerificationBuildStep.java
@@ -11,15 +11,17 @@
import io.quarkus.deployment.pkg.builditem.PackageTypeBuildItem;
/**
- * verifies that the requested package type can actually be produced
+ * Verifies that the requested package type can actually be produced
*/
public class PackageTypeVerificationBuildStep {
@BuildStep
List builtins() {
- return Arrays.asList(new PackageTypeBuildItem(PackageConfig.NATIVE), new PackageTypeBuildItem(PackageConfig.JAR),
- new PackageTypeBuildItem(PackageConfig.LEGACY_JAR), new PackageTypeBuildItem(PackageConfig.UBER_JAR),
- new PackageTypeBuildItem(PackageConfig.FAST_JAR),
+ return Arrays.asList(new PackageTypeBuildItem(PackageConfig.NATIVE),
+ new PackageTypeBuildItem(PackageConfig.NATIVE_SOURCES),
+ new PackageTypeBuildItem(PackageConfig.JAR), new PackageTypeBuildItem(PackageConfig.FAST_JAR),
+ new PackageTypeBuildItem(PackageConfig.LEGACY_JAR),
+ new PackageTypeBuildItem(PackageConfig.UBER_JAR),
new PackageTypeBuildItem(PackageConfig.MUTABLE_JAR));
}
@@ -27,7 +29,7 @@ List builtins() {
ServiceStartBuildItem verify(List items, PackageConfig config) {
Set registered = items.stream().map(PackageTypeBuildItem::getType).collect(Collectors.toSet());
if (!registered.contains(config.type)) {
- throw new IllegalStateException("Unknown packaging type " + config.type + " known types are " + registered);
+ throw new IllegalStateException("Unknown packaging type '" + config.type + "' known types are " + registered);
}
return null;
}
diff --git a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java
index b8740cc9d757a..788517d4f86b3 100644
--- a/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java
+++ b/integration-tests/maven/src/test/java/io/quarkus/maven/it/PackageIT.java
@@ -8,6 +8,7 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -254,6 +255,26 @@ public void testQuarkusIndexDependencyOnLocalModule() throws Exception {
assertZipEntriesCanBeOpenedAndClosed(runnerJar);
}
+ @Test
+ public void testNativeSourcesPackage() throws Exception {
+ testDir = initProject("projects/uberjar-check", "projects/project-native-sources");
+
+ running = new RunningInvoker(testDir, false);
+ final MavenProcessInvocationResult result = running.execute(
+ Arrays.asList("package", "-Dquarkus.package.type=native-sources"),
+ Collections.emptyMap());
+
+ assertThat(result.getProcess().waitFor()).isEqualTo(0);
+
+ final File targetDir = getTargetDir();
+
+ final Path nativeSourcesDir = targetDir.toPath().resolve("native-sources");
+ assertThat(nativeSourcesDir).exists()
+ .isDirectoryContaining(p -> "native-image.args".equals(p.getFileName().toString()))
+ .isDirectoryContaining(p -> "acme-1.0-SNAPSHOT-runner.jar".equals(p.getFileName().toString()));
+
+ }
+
private int getNumberOfFilesEndingWith(File dir, String suffix) {
return getFilesEndingWith(dir, suffix).size();
}