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(); }