From efe041cdc05e6acd55e794a4126ac14e2ef3e6bb Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Tue, 2 Mar 2021 15:34:03 +0200 Subject: [PATCH] Run objcopy in builder-image when using it --- .../NativeImageBuildContainerRunner.java | 11 +++ .../steps/NativeImageBuildLocalRunner.java | 32 +++++++- ...NativeImageBuildRemoteContainerRunner.java | 2 + .../pkg/steps/NativeImageBuildRunner.java | 35 ++++++++- .../pkg/steps/NativeImageBuildStep.java | 75 +++---------------- 5 files changed, 88 insertions(+), 67 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java index bd734cb694d879..b20e35e8b0106f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java @@ -70,6 +70,17 @@ protected String[] getBuildCommand(List args) { return buildCommand("run", getContainerRuntimeBuildArgs(), args); } + @Override + protected void objcopy(String... args) { + final List containerRuntimeBuildArgs = getContainerRuntimeBuildArgs(); + Collections.addAll(containerRuntimeBuildArgs, "--entrypoint", "/bin/bash"); + final ArrayList objcopyCommand = new ArrayList<>(2); + objcopyCommand.add("-c"); + objcopyCommand.add("objcopy " + String.join(" ", args)); + final String[] command = buildCommand("run", containerRuntimeBuildArgs, objcopyCommand); + runCommand(command, null, null); + } + protected List getContainerRuntimeBuildArgs() { List containerRuntimeArgs = new ArrayList<>(); nativeConfig.containerRuntimeOptions.ifPresent(containerRuntimeArgs::addAll); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalRunner.java index bbea0c4099a9e5..69a8652fa321e5 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalRunner.java @@ -8,9 +8,11 @@ public class NativeImageBuildLocalRunner extends NativeImageBuildRunner { private final String nativeImageExecutable; + private final File workingDirectory; - public NativeImageBuildLocalRunner(String nativeImageExecutable) { + public NativeImageBuildLocalRunner(String nativeImageExecutable, File workingDirectory) { this.nativeImageExecutable = nativeImageExecutable; + this.workingDirectory = workingDirectory; } @Override @@ -29,6 +31,34 @@ protected String[] getBuildCommand(List args) { return buildCommand(args); } + @Override + protected void objcopy(String... args) { + final String[] command = new String[args.length + 1]; + command[0] = "objcopy"; + System.arraycopy(args, 0, command, 1, args.length); + runCommand(command, null, workingDirectory); + } + + @Override + protected boolean objcopyExists() { + // System path + String systemPath = System.getenv("PATH"); + if (systemPath != null) { + String[] pathDirs = systemPath.split(File.pathSeparator); + for (String pathDir : pathDirs) { + File dir = new File(pathDir); + if (dir.isDirectory()) { + File file = new File(dir, "objcopy"); + if (file.exists()) { + return true; + } + } + } + } + + return false; + } + private String[] buildCommand(List args) { return Stream.concat(Stream.of(nativeImageExecutable), args.stream()).toArray(String[]::new); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java index 54415e41bcae3a..433907b10b24f3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRemoteContainerRunner.java @@ -57,6 +57,8 @@ protected void postBuild() { copyFromContainerVolume(nativeImageName, "Failed to copy native image from container volume back to the host."); if (nativeConfig.debug.enabled) { copyFromContainerVolume("sources", "Failed to copy sources from container volume back to the host."); + String symbols = String.format("%s.debug", nativeImageName); + copyFromContainerVolume(symbols, "Failed to copy debug symbols from container volume back to the host."); } // docker container rm final String[] rmTempContainerCommand = new String[] { containerRuntime.getExecutableName(), "container", "rm", diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java index d3adaf2d0d2203..05ef4c0ba9730b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java @@ -46,7 +46,8 @@ public void setup(boolean processInheritIODisabled) { public void cleanupServer(File outputDir) throws InterruptedException, IOException { } - public int build(List args, Path outputDir, boolean processInheritIODisabled) + public int build(List args, String nativeImageName, Path outputDir, boolean debugEnabled, + boolean processInheritIODisabled) throws InterruptedException, IOException { preBuild(args); try { @@ -61,16 +62,44 @@ public int build(List args, Path outputDir, boolean processInheritIODisa errorReportLatch)); executor.shutdown(); errorReportLatch.await(); - return process.waitFor(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + return exitCode; + } + + if (objcopyExists()) { + if (debugEnabled) { + splitDebugSymbols(nativeImageName); + } else { + // Strip debug symbols regardless, because the underlying JDK might contain them + objcopy("--strip-debug", nativeImageName); + } + } else { + log.warn("objcopy executable not found in PATH. Debug symbols will not be separated from executable."); + log.warn("That will result in a larger native image with debug symbols embedded in it."); + } + return 0; } finally { postBuild(); } } + private void splitDebugSymbols(String executable) { + String symbols = String.format("%s.debug", executable); + objcopy("--only-keep-debug", executable, symbols); + objcopy(String.format("--add-gnu-debuglink=%s", symbols), executable); + } + protected abstract String[] getGraalVMVersionCommand(List args); protected abstract String[] getBuildCommand(List args); + protected boolean objcopyExists() { + return true; + } + + protected abstract void objcopy(String... args); + protected void preBuild(List buildArgs) throws IOException, InterruptedException { } @@ -85,7 +114,7 @@ protected void postBuild() throws InterruptedException, IOException { * If {@code null} the failure is ignored, but logged. * @param workingDirectory The directory in which to run the command */ - void runCommand(String[] command, String errorMsg, File workingDirectory) { + static void runCommand(String[] command, String errorMsg, File workingDirectory) { log.info(String.join(" ", command).replace("$", "\\$")); Process process = null; try { 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 369d584827898b..fda93b5fd4b06f 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 @@ -10,7 +10,6 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -175,7 +174,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa List nativeImageArgs = commandAndExecutable.args; - int exitCode = buildRunner.build(nativeImageArgs, outputDir, processInheritIODisabled.isPresent()); + int exitCode = buildRunner.build(nativeImageArgs, nativeImageName, outputDir, nativeConfig.debug.enabled, + processInheritIODisabled.isPresent()); if (exitCode != 0) { throw imageGenerationFailed(exitCode, nativeImageArgs); } @@ -184,6 +184,11 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa IoUtils.copy(generatedImage, finalPath); Files.delete(generatedImage); if (nativeConfig.debug.enabled) { + final String symbolsName = String.format("%s.debug", nativeImageName); + Path generatedSymbols = outputDir.resolve(symbolsName); + Path finalSymbolsPath = outputTargetBuildItem.getOutputDirectory().resolve(symbolsName); + IoUtils.copy(generatedSymbols, finalSymbolsPath); + Files.delete(generatedSymbols); final String sources = "sources"; final Path generatedSources = outputDir.resolve(sources); final Path finalSources = outputTargetBuildItem.getOutputDirectory().resolve(sources); @@ -192,17 +197,6 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa } System.setProperty("native.image.path", finalPath.toAbsolutePath().toString()); - if (objcopyExists()) { - if (nativeConfig.debug.enabled) { - splitDebugSymbols(finalPath); - } - // Strip debug symbols regardless, because the underlying JDK might contain them - objcopy("--strip-debug", finalPath.toString()); - } else { - log.warn("objcopy executable not found in PATH. Debug symbols will not be separated from executable."); - log.warn("That will result in a larger native image with debug symbols embedded in it."); - } - return new NativeImageBuildItem(finalPath); } catch (Exception e) { throw new RuntimeException("Failed to build native image", e); @@ -231,7 +225,7 @@ public static boolean isContainerBuild(NativeConfig nativeConfig) { private static NativeImageBuildRunner getNativeImageBuildRunner(NativeConfig nativeConfig, Path outputDir, String nativeImageName) { if (!isContainerBuild(nativeConfig)) { - NativeImageBuildLocalRunner localRunner = getNativeImageBuildLocalRunner(nativeConfig); + NativeImageBuildLocalRunner localRunner = getNativeImageBuildLocalRunner(nativeConfig, outputDir.toFile()); if (localRunner != null) { return localRunner; } @@ -357,12 +351,12 @@ private void checkGraalVMVersion(GraalVM.Version version) { } } - private static NativeImageBuildLocalRunner getNativeImageBuildLocalRunner(NativeConfig nativeConfig) { + private static NativeImageBuildLocalRunner getNativeImageBuildLocalRunner(NativeConfig nativeConfig, File outputDir) { String executableName = getNativeImageExecutableName(); if (nativeConfig.graalvmHome.isPresent()) { File file = Paths.get(nativeConfig.graalvmHome.get(), "bin", executableName).toFile(); if (file.exists()) { - return new NativeImageBuildLocalRunner(file.getAbsolutePath()); + return new NativeImageBuildLocalRunner(file.getAbsolutePath(), outputDir); } } @@ -384,7 +378,7 @@ private static NativeImageBuildLocalRunner getNativeImageBuildLocalRunner(Native if (javaHome != null) { File file = new File(javaHome, "bin/" + executableName); if (file.exists()) { - return new NativeImageBuildLocalRunner(file.getAbsolutePath()); + return new NativeImageBuildLocalRunner(file.getAbsolutePath(), outputDir); } } @@ -397,7 +391,7 @@ private static NativeImageBuildLocalRunner getNativeImageBuildLocalRunner(Native if (dir.isDirectory()) { File file = new File(dir, executableName); if (file.exists()) { - return new NativeImageBuildLocalRunner(file.getAbsolutePath()); + return new NativeImageBuildLocalRunner(file.getAbsolutePath(), outputDir); } } } @@ -431,51 +425,6 @@ private static String testGCCArgument(String argument) { return ""; } - private boolean objcopyExists() { - // System path - String systemPath = System.getenv(PATH); - if (systemPath != null) { - String[] pathDirs = systemPath.split(File.pathSeparator); - for (String pathDir : pathDirs) { - File dir = new File(pathDir); - if (dir.isDirectory()) { - File file = new File(dir, "objcopy"); - if (file.exists()) { - return true; - } - } - } - } - - return false; - } - - private void splitDebugSymbols(Path executable) { - Path symbols = Paths.get(String.format("%s.debug", executable.toString())); - objcopy("--only-keep-debug", executable.toString(), symbols.toString()); - objcopy(String.format("--add-gnu-debuglink=%s", symbols.toString()), executable.toString()); - } - - private static void objcopy(String... args) { - final List command = new ArrayList<>(args.length + 1); - command.add("objcopy"); - command.addAll(Arrays.asList(args)); - if (log.isDebugEnabled()) { - log.debugf("Execute %s", String.join(" ", command)); - } - Process process = null; - try { - process = new ProcessBuilder(command).start(); - process.waitFor(); - } catch (IOException | InterruptedException e) { - throw new RuntimeException("Unable to invoke objcopy", e); - } finally { - if (process != null) { - process.destroy(); - } - } - } - protected static final class GraalVM { static final class Version implements Comparable { private static final Pattern PATTERN = Pattern.compile(