Skip to content

Commit

Permalink
Run objcopy in builder-image when using it
Browse files Browse the repository at this point in the history
  • Loading branch information
zakkak committed Apr 12, 2021
1 parent 609d216 commit 1bc84bc
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ protected String[] getBuildCommand(List<String> args) {
return buildCommand("run", getContainerRuntimeBuildArgs(), args);
}

@Override
protected void objcopy(String... args) {
final List<String> containerRuntimeBuildArgs = getContainerRuntimeBuildArgs();
Collections.addAll(containerRuntimeBuildArgs, "--entrypoint", "/bin/bash");
final ArrayList<String> 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<String> getContainerRuntimeBuildArgs() {
List<String> containerRuntimeArgs = new ArrayList<>();
nativeConfig.containerRuntimeOptions.ifPresent(containerRuntimeArgs::addAll);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package io.quarkus.deployment.pkg.steps;

import java.io.File;
import java.util.List;
import java.util.stream.Stream;

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
Expand All @@ -21,6 +24,34 @@ protected String[] getBuildCommand(List<String> 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<String> args) {
return Stream.concat(Stream.of(nativeImageExecutable), args.stream()).toArray(String[]::new);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ protected void postBuild() {
copyFromContainerVolume(resultingExecutableName, "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 <containerID>
final String[] rmTempContainerCommand = new String[] { containerRuntime.getExecutableName(), "container", "rm",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public GraalVM.Version getGraalVMVersion() {
public void setup(boolean processInheritIODisabled) {
}

public int build(List<String> args, Path outputDir, boolean processInheritIODisabled)
public int build(List<String> args, String nativeImageName, Path outputDir, boolean debugEnabled,
boolean processInheritIODisabled)
throws InterruptedException, IOException {
preBuild(args);
try {
Expand All @@ -57,16 +58,44 @@ public int build(List<String> 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<String> args);

protected abstract String[] getBuildCommand(List<String> args);

protected boolean objcopyExists() {
return true;
}

protected abstract void objcopy(String... args);

protected void preBuild(List<String> buildArgs) throws IOException, InterruptedException {
}

Expand All @@ -81,7 +110,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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.HashMap;
import java.util.List;
Expand Down Expand Up @@ -184,7 +183,8 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa

List<String> 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);
}
Expand All @@ -193,6 +193,11 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
IoUtils.copy(generatedExecutablePath, finalExecutablePath);
Files.delete(generatedExecutablePath);
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);
Expand All @@ -201,17 +206,6 @@ public NativeImageBuildItem build(NativeConfig nativeConfig, NativeImageSourceJa
}
System.setProperty("native.image.path", finalExecutablePath.toAbsolutePath().toString());

if (objcopyExists()) {
if (nativeConfig.debug.enabled) {
splitDebugSymbols(finalExecutablePath);
}
// Strip debug symbols regardless, because the underlying JDK might contain them
objcopy("--strip-debug", finalExecutablePath.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(finalExecutablePath,
new NativeImageBuildItem.GraalVMVersion(graalVMVersion.fullVersion, graalVMVersion.major,
graalVMVersion.minor,
Expand Down Expand Up @@ -246,7 +240,7 @@ public static boolean isContainerBuild(NativeConfig nativeConfig) {
private static NativeImageBuildRunner getNativeImageBuildRunner(NativeConfig nativeConfig, Path outputDir,
String resultingExecutableName) {
if (!isContainerBuild(nativeConfig)) {
NativeImageBuildLocalRunner localRunner = getNativeImageBuildLocalRunner(nativeConfig);
NativeImageBuildLocalRunner localRunner = getNativeImageBuildLocalRunner(nativeConfig, outputDir.toFile());
if (localRunner != null) {
return localRunner;
}
Expand Down Expand Up @@ -372,12 +366,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);
}
}

Expand All @@ -399,7 +393,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);
}
}

Expand All @@ -412,7 +406,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);
}
}
}
Expand Down Expand Up @@ -446,51 +440,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<String> 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();
}
}
}

private static class NativeImageInvokerInfo {
private final List<String> args;

Expand Down

0 comments on commit 1bc84bc

Please sign in to comment.