From 01a2ef0b8e617ea0088887e982eabde982156493 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 21 Dec 2018 10:34:39 +0100 Subject: [PATCH] Fixes #374 remove legacy build and native-image mojos --- .../shamrock/maven/AnalyseCallTreeMojo.java | 1 + .../maven/ErrorReplacingProcessReader.java | 136 ------ .../jboss/shamrock/maven/LegacyBuildMojo.java | 459 ------------------ .../shamrock/maven/LegacyNativeImageMojo.java | 303 ------------ .../jboss/shamrock/maven/ReportAnalyzer.java | 184 ------- 5 files changed, 1 insertion(+), 1082 deletions(-) delete mode 100644 maven/src/main/java/org/jboss/shamrock/maven/ErrorReplacingProcessReader.java delete mode 100644 maven/src/main/java/org/jboss/shamrock/maven/LegacyBuildMojo.java delete mode 100644 maven/src/main/java/org/jboss/shamrock/maven/LegacyNativeImageMojo.java delete mode 100644 maven/src/main/java/org/jboss/shamrock/maven/ReportAnalyzer.java diff --git a/maven/src/main/java/org/jboss/shamrock/maven/AnalyseCallTreeMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/AnalyseCallTreeMojo.java index a82b0c60cb519..bfd5238cf7749 100644 --- a/maven/src/main/java/org/jboss/shamrock/maven/AnalyseCallTreeMojo.java +++ b/maven/src/main/java/org/jboss/shamrock/maven/AnalyseCallTreeMojo.java @@ -23,6 +23,7 @@ import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; +import org.jboss.shamrock.creator.phase.nativeimage.ReportAnalyzer; @Mojo(name = "analyze-call-tree") public class AnalyseCallTreeMojo extends AbstractMojo { diff --git a/maven/src/main/java/org/jboss/shamrock/maven/ErrorReplacingProcessReader.java b/maven/src/main/java/org/jboss/shamrock/maven/ErrorReplacingProcessReader.java deleted file mode 100644 index a3b7dcc03622f..0000000000000 --- a/maven/src/main/java/org/jboss/shamrock/maven/ErrorReplacingProcessReader.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jboss.shamrock.maven; - -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.concurrent.CountDownLatch; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Substrate prints incomprehensible and useless 'potential call paths' that look like stack traces - *

- * This class intercepts them and prints meaningful output instead, so users don't waste hours going on wild goose - * chases - */ -public final class ErrorReplacingProcessReader implements Runnable { - - private static final String LINE_START = "Call path from entry point to "; - private final InputStream inputStream; - private final File reportdir; - private final CountDownLatch doneLatch; - - public ErrorReplacingProcessReader(InputStream inputStream, File reportdir, CountDownLatch doneLatch) { - this.inputStream = inputStream; - this.reportdir = reportdir; - this.doneLatch = doneLatch; - } - - @Override - public void run() { - try { - Deque fullBuffer = new ArrayDeque<>(); - boolean buffering = false; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - if (line.startsWith(LINE_START)) { - buffering = true; - } - if (buffering) { - fullBuffer.add(line); - } else { - System.err.println(line); - } - } - File reportFile = null; - if (reportdir.exists()) { - File[] files = reportdir.listFiles(); - if (files != null) { - for (File j : files) { - if (j.getName().startsWith("call_tree")) { - reportFile = j; - break; - } - } - - } - } - if (reportFile == null) { - for (String j : fullBuffer) { - System.err.println(j); - } - } else { - while (!fullBuffer.isEmpty()) { - String line = fullBuffer.pop(); - if (line.startsWith(LINE_START)) { - handleErrorState(reportFile, line, fullBuffer); - } else { - System.err.println(line); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } finally { - doneLatch.countDown(); - } - } - - private static void handleErrorState(File report, String firstLine, Deque queue) { - System.err.println(firstLine); - String remainder = firstLine.substring(LINE_START.length()); - Matcher m = Pattern.compile("([^(]*).*").matcher(remainder); - if (!m.find()) { - return; - } - String line = ""; - while (!queue.isEmpty()) { - line = queue.pop(); - if (line.trim().startsWith("at")) { - System.err.println(line); - } else { - break; - } - } - - System.err.println("--------------------------------------------------------------------------------------------"); - System.err.println("-- WARNING: The above stack trace is not a real stack trace, it is a theoretical call tree---"); - System.err.println("-- If an interface has multiple implementations SVM will just display one potential call ---"); - System.err.println("-- path to the interface. This is often meaningless, and what you actually need to know is---"); - System.err.println("-- the path to the constructor of the object that implements this interface. ---"); - System.err.println("-- Shamrock has attempted to generate a more meaningful call flow analysis below ---"); - System.err.println("---------------------------------------------------------------------------------------------\n"); - try { - String fullName = m.group(1); - - int idex = fullName.lastIndexOf('.'); - String clazz = fullName.substring(0, idex); - String method = fullName.substring(idex + 1); - - System.err.println(ReportAnalyzer.analyse(report.getAbsolutePath(), clazz, method)); - } catch (Exception e) { - e.printStackTrace(); - } - System.err.println(line); - } -} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/LegacyBuildMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/LegacyBuildMojo.java deleted file mode 100644 index 75c26258f7e60..0000000000000 --- a/maven/src/main/java/org/jboss/shamrock/maven/LegacyBuildMojo.java +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jboss.shamrock.maven; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.jar.Attributes; -import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; -import org.eclipse.microprofile.config.Config; -import org.jboss.builder.BuildResult; -import org.jboss.shamrock.deployment.ClassOutput; -import org.jboss.shamrock.deployment.ShamrockAugmentor; -import org.jboss.shamrock.deployment.builditem.BytecodeTransformerBuildItem; -import org.jboss.shamrock.deployment.builditem.MainClassBuildItem; -import org.jboss.shamrock.deployment.builditem.substrate.SubstrateOutputBuildItem; -import org.jboss.shamrock.deployment.index.ResolvedArtifact; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ClassWriter; - -import io.smallrye.config.PropertiesConfigSource; -import io.smallrye.config.SmallRyeConfigProviderResolver; - -@Mojo(name = "legacy-build", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class LegacyBuildMojo extends AbstractMojo { - - private static final String DEPENDENCIES_RUNTIME = "dependencies.runtime"; - private static final String PROVIDED = "provided"; - private static final Set UBER_JAR_IGNORED_FILES = new HashSet<>(Arrays.asList( - "META-INF/MANIFEST.MF", - "META-INF/INDEX.LIST", - "META-INF/LICENSE", - "META-INF/LICENSE.txt", - "META-INF/NOTICE", - "META-INF/NOTICE.txt", - "META-INF/DEPENDENCIES", - "META-INF/README", - "dependencies.runtime", - "module-info.class")); - - /** - * The directory for compiled classes. - */ - @Parameter(readonly = true, required = true, defaultValue = "${project.build.outputDirectory}") - private File outputDirectory; - - @Parameter(defaultValue = "${project}", readonly = true, required = true) - protected MavenProject project; - - /** - * The directory for classes generated by processing. - */ - @Parameter(defaultValue = "${project.build.directory}/wiring-classes") - private File wiringClassesDirectory; - - @Parameter(defaultValue = "${project.build.directory}") - private File buildDir; - /** - * The directory for library jars - */ - @Parameter(defaultValue = "${project.build.directory}/lib") - private File libDir; - - @Parameter(defaultValue = "${project.build.finalName}") - private String finalName; - - @Parameter(defaultValue = "org.jboss.shamrock.runner.GeneratedMain") - private String mainClass; - - @Parameter(defaultValue = "true") - private boolean useStaticInit; - - @Parameter(defaultValue = "false") - private boolean uberJar; - - public LegacyBuildMojo() { - MojoLogger.logSupplier = this::getLog; - } - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - //first lets look for some config, as it is not on the current class path - //and we need to load it to run the build process - File config = new File(outputDirectory, "META-INF/microprofile-config.properties"); - if(config.exists()) { - try { - Config built = SmallRyeConfigProviderResolver.instance().getBuilder() - .addDefaultSources() - .addDiscoveredConverters() - .addDiscoveredSources() - .withSources(new PropertiesConfigSource(config.toURL())).build(); - SmallRyeConfigProviderResolver.instance().registerConfig(built, Thread.currentThread().getContextClassLoader()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - libDir.mkdirs(); - wiringClassesDirectory.mkdirs(); - try { - StringBuilder classPath = new StringBuilder(); - List problems = new ArrayList<>(); - Set whitelist = new HashSet<>(); - for (Artifact a : project.getArtifacts()) { - if (!"jar".equals(a.getType())) { - continue; - } - try (ZipFile zip = openZipFile(a)) { - if (!a.getScope().equals(PROVIDED) && zip.getEntry("META-INF/services/org.jboss.shamrock.deployment.ShamrockSetup") != null) { - problems.add("Artifact " + a + " is a deployment artifact, however it does not have scope required. This will result in unnecessary jars being included in the final image"); - } - ZipEntry deps = zip.getEntry(DEPENDENCIES_RUNTIME); - if (deps != null) { - whitelist.add(a.getDependencyConflictId()); - try (InputStream in = zip.getInputStream(deps)) { - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = reader.readLine()) != null) { - String[] parts = line.trim().split(":"); - if (parts.length < 5) { - continue; - } - StringBuilder sb = new StringBuilder(); - //the last two bits are version and scope - //which we don't want - for (int i = 0; i < parts.length - 2; ++i) { - if (i > 0) { - sb.append(':'); - } - sb.append(parts[i]); - } - whitelist.add(sb.toString()); - } - } - } - - } - } - if (!problems.isEmpty()) { - //TODO: add a config option to just log an error instead - throw new MojoFailureException(problems.toString()); - } - Set seen = new HashSet<>(); - try (ZipOutputStream runner = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(new File(buildDir, finalName + "-runner.jar"))))) { - Map> services = new HashMap<>(); - - - for (Artifact a : project.getArtifacts()) { - if (a.getScope().equals(PROVIDED) && !whitelist.contains(a.getDependencyConflictId())) { - continue; - } - if (a.getArtifactId().equals("svm") && a.getGroupId().equals("com.oracle.substratevm")) { - continue; - } - final File artifactFile = a.getFile(); - if (uberJar) { - try (ZipInputStream in = new ZipInputStream(new FileInputStream(artifactFile))) { - for (ZipEntry e = in.getNextEntry(); e != null; e = in.getNextEntry()) { - if (e.getName().startsWith("META-INF/services/") && e.getName().length() > 18) { - services.computeIfAbsent(e.getName(), (u) -> new ArrayList<>()).add(read(in)); - continue; - } else if (UBER_JAR_IGNORED_FILES.contains(e.getName())) { - continue; - } - if (!seen.add(e.getName())) { - if (!e.getName().endsWith("/")) { - getLog().warn("Duplicate entry " + e.getName() + " entry from " + a + " will be ignored"); - } - continue; - } - runner.putNextEntry(new ZipEntry(e.getName())); - doCopy(runner, in); - } - } - } else { - final String fileName = a.getGroupId() + "." + artifactFile.getName(); - final Path targetPath = libDir.toPath().resolve(fileName); - - Files.copy(artifactFile.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING); - classPath.append(" lib/" + fileName); - } - } - - List artifactList = new ArrayList<>(); - List classPathUrls = new ArrayList<>(); - for (Artifact artifact : project.getArtifacts()) { - classPathUrls.add(artifact.getFile().toURL()); - artifactList.add(new ResolvedArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getClassifier(), Paths.get(artifact.getFile().getAbsolutePath()))); - } - - //we need to make sure all the deployment artifacts are on the class path - //to do this we need to create a new class loader to actually use for the runner - List cpCopy = new ArrayList<>(); - - cpCopy.add(outputDirectory.toURL()); - cpCopy.addAll(classPathUrls); - - URLClassLoader runnerClassLoader = new URLClassLoader(cpCopy.toArray(new URL[0]), getClass().getClassLoader()); - ClassOutput classOutput = new ClassOutput() { - @Override - public void writeClass(boolean applicationClass, String className, byte[] data) throws IOException { - String location = className.replace('.', '/'); - File file = new File(wiringClassesDirectory, location + ".class"); - file.getParentFile().mkdirs(); - try (FileOutputStream out = new FileOutputStream(file)) { - out.write(data); - } - } - - @Override - public void writeResource(String name, byte[] data) throws IOException { - File file = new File(wiringClassesDirectory, name); - file.getParentFile().mkdirs(); - try (FileOutputStream out = new FileOutputStream(file)) { - out.write(data); - } - } - - }; - - - ClassLoader old = Thread.currentThread().getContextClassLoader(); - BuildResult result; - try { - Thread.currentThread().setContextClassLoader(runnerClassLoader); - - ShamrockAugmentor.Builder builder = ShamrockAugmentor.builder(); - builder.setRoot(outputDirectory.toPath()); - builder.setClassLoader(runnerClassLoader); - builder.setOutput(classOutput); - builder.addFinal(BytecodeTransformerBuildItem.class) - .addFinal(MainClassBuildItem.class) - .addFinal(SubstrateOutputBuildItem.class); - result = builder.build().run(); - } finally { - Thread.currentThread().setContextClassLoader(old); - } - - Map>> bytecodeTransformers = new HashMap<>(); - List bytecodeTransformerBuildItems = result.consumeMulti(BytecodeTransformerBuildItem.class); - if (!bytecodeTransformerBuildItems.isEmpty()) { - for (BytecodeTransformerBuildItem i : bytecodeTransformerBuildItems) { - bytecodeTransformers.computeIfAbsent(i.getClassToTransform(), (h) -> new ArrayList<>()).add(i.getVisitorFunction()); - } - } - - Path wiringJar = Paths.get(wiringClassesDirectory.getAbsolutePath()); - Files.walk(wiringJar).forEach(new Consumer() { - @Override - public void accept(Path path) { - try { - String pathName = wiringJar.relativize(path).toString(); - if (Files.isDirectory(path)) { - String p = pathName + "/"; - if (seen.contains(p)) { - return; - } - seen.add(p); - if (!pathName.isEmpty()) { - runner.putNextEntry(new ZipEntry(p)); - } - } else if (pathName.startsWith("META-INF/services/") && pathName.length() > 18) { - services.computeIfAbsent(pathName, (u) -> new ArrayList<>()).add(CopyUtils.readFileContent(path)); - } else { - seen.add(pathName); - runner.putNextEntry(new ZipEntry(pathName)); - try (FileInputStream in = new FileInputStream(path.toFile())) { - doCopy(runner, in); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - - Manifest manifest = new Manifest(); - manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, classPath.toString()); - manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, mainClass); - runner.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF")); - manifest.write(runner); - //now copy all the contents to the runner jar - //I am not 100% sure about this idea, but if we are going to support bytecode transforms it seems - //like the cleanest way to do it - //at the end of the PoC phase all this needs review - Path appJar = Paths.get(outputDirectory.getAbsolutePath()); - ExecutorService executorPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - ConcurrentLinkedDeque> transformed = new ConcurrentLinkedDeque<>(); - try { - Files.walk(appJar).forEach(new Consumer() { - @Override - public void accept(Path path) { - try { - final String pathName = appJar.relativize(path).toString(); - if (Files.isDirectory(path)) { -// if (!pathName.isEmpty()) { -// out.putNextEntry(new ZipEntry(pathName + "/")); -// } - } else if (pathName.endsWith(".class") && !bytecodeTransformers.isEmpty()) { - String className = pathName.substring(0, pathName.length() - 6).replace('/', '.'); - List> visitors = bytecodeTransformers.get(className); - - if (visitors == null || visitors.isEmpty()) { - runner.putNextEntry(new ZipEntry(pathName)); - try (FileInputStream in = new FileInputStream(path.toFile())) { - doCopy(runner, in); - } - } else { - transformed.add(executorPool.submit(new Callable() { - @Override - public FutureEntry call() throws Exception { - final byte[] fileContent = CopyUtils.readFileContent(path); - ClassReader cr = new ClassReader(fileContent); - ClassWriter writer = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - ClassVisitor visitor = writer; - for (BiFunction i : visitors) { - visitor = i.apply(className, visitor); - } - cr.accept(visitor, 0); - return new FutureEntry(writer.toByteArray(), pathName); - } - })); - } - } else { - runner.putNextEntry(new ZipEntry(pathName)); - try (FileInputStream in = new FileInputStream(path.toFile())) { - doCopy(runner, in); - } - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }); - for (Future i : transformed) { - - FutureEntry res = i.get(); - runner.putNextEntry(new ZipEntry(res.location)); - runner.write(res.data); - } - } finally { - executorPool.shutdown(); - } - for (Map.Entry> entry : services.entrySet()) { - runner.putNextEntry(new ZipEntry(entry.getKey())); - for (byte[] i : entry.getValue()) { - runner.write(i); - runner.write('\n'); - } - } - } - - - } catch (Exception e) { - throw new MojoFailureException("Failed to run", e); - } - } - - private ZipFile openZipFile(final Artifact a) { - final File file = a.getFile(); - if (file == null) { - throw new RuntimeException("No file for Artifact:" + a.toString()); - } - if (!Files.isReadable(file.toPath())) { - throw new RuntimeException("File not existing or not allowed for reading: " + file.getAbsolutePath()); - } - try { - return new ZipFile(file); - } catch (IOException e) { - throw new RuntimeException("Error opening zip stream from artifact: " + a.toString()); - } - } - - private static void doCopy(OutputStream out, InputStream in) throws IOException { - byte[] buffer = new byte[1024]; - int r; - while ((r = in.read(buffer)) > 0) { - out.write(buffer, 0, r); - } - } - - private static byte[] read(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int r; - while ((r = in.read(buffer)) > 0) { - out.write(buffer, 0, r); - } - return out.toByteArray(); - } - - private static final class FutureEntry { - final byte[] data; - final String location; - - private FutureEntry(byte[] data, String location) { - this.data = data; - this.location = location; - } - } -} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/LegacyNativeImageMojo.java b/maven/src/main/java/org/jboss/shamrock/maven/LegacyNativeImageMojo.java deleted file mode 100644 index 7c7d2d1cf2455..0000000000000 --- a/maven/src/main/java/org/jboss/shamrock/maven/LegacyNativeImageMojo.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jboss.shamrock.maven; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Properties; -import java.util.concurrent.CountDownLatch; -import java.util.stream.Collectors; - -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; -import org.eclipse.microprofile.config.Config; - -import io.smallrye.config.SmallRyeConfigProviderResolver; - -@Mojo(name = "legacy-native-image", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME) -public class LegacyNativeImageMojo extends AbstractMojo { - - @Parameter(defaultValue = "${project}", readonly = true, required = true) - protected MavenProject project; - - /** - * The directory for compiled classes. - */ - @Parameter(readonly = true, required = true, defaultValue = "${project.build.directory}") - private File outputDirectory; - - @Parameter(defaultValue = "${project.build.directory}/wiring-classes") - private File wiringClassesDirectory; - - @Parameter(defaultValue = "false") - private boolean reportErrorsAtRuntime; - - @Parameter(defaultValue = "false") - private boolean debugSymbols; - - @Parameter(defaultValue = "${native-image.debug-build-process}") - private boolean debugBuildProcess; - - - @Parameter(readonly = true, required = true, defaultValue = "${project.build.finalName}") - private String finalName; - - @Parameter(defaultValue = "${native-image.new-server}") - private boolean cleanupServer; - - @Parameter - private boolean enableHttpUrlHandler; - - @Parameter - private boolean enableHttpsUrlHandler; - - @Parameter - private boolean enableAllSecurityServices; - - @Parameter - private boolean enableRetainedHeapReporting; - - @Parameter - private boolean enableCodeSizeReporting; - - @Parameter - private boolean enableIsolates; - - @Parameter(defaultValue = "${env.GRAALVM_HOME}") - private String graalvmHome; - - @Parameter(defaultValue = "false") - private boolean enableServer; - - @Parameter(defaultValue = "false") - private boolean enableJni; - - @Parameter(defaultValue = "false") - private boolean autoServiceLoaderRegistration; - - @Parameter(defaultValue = "false") - private boolean dumpProxies; - - @Parameter(defaultValue = "${native-image.xmx}") - private String nativeImageXmx; - - @Parameter(defaultValue = "${native-image.docker-build}") - private boolean dockerBuild; - - @Parameter(defaultValue = "false") - private boolean enableVMInspection; - - @Parameter(defaultValue = "true") - private boolean fullStackTraces; - - @Parameter(defaultValue = "${native-image.disable-reports}") - private boolean disableReports; - - @Parameter - private List additionalBuildArgs; - - public LegacyNativeImageMojo() { - MojoLogger.logSupplier = this::getLog; - } - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - - Config config = SmallRyeConfigProviderResolver.instance().getConfig(); - - boolean vmVersionOutOfDate = isThisGraalVMRCObsolete(); - - HashMap env = new HashMap<>(System.getenv()); - List nativeImage; - if (dockerBuild) { - - // E.g. "/usr/bin/docker run -v {{PROJECT_DIR}}:/project --rm protean/graalvm-native-image" - nativeImage = new ArrayList<>(); - //TODO: use an 'official' image - Collections.addAll(nativeImage, "docker", "run", "-v",outputDirectory.getAbsolutePath() + ":/project:z", "--rm", "swd847/centos-graal-native-image"); - - } else { - if (graalvmHome == null) { - throw new MojoFailureException("GRAALVM_HOME was not set"); - } - env.put("GRAALVM_HOME", graalvmHome); - nativeImage = Collections.singletonList(graalvmHome + File.separator + "bin" + File.separator + "native-image"); - } - - try { - List command = new ArrayList<>(); - command.addAll(nativeImage); - if (cleanupServer) { - List cleanup = new ArrayList<>(nativeImage); - cleanup.add("--server-shutdown"); - ProcessBuilder pb = new ProcessBuilder(cleanup.toArray(new String[0])); - pb.directory(outputDirectory); - pb.redirectInput(ProcessBuilder.Redirect.INHERIT); - pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); - pb.redirectError(ProcessBuilder.Redirect.INHERIT); - Process process = pb.start(); - process.waitFor(); - } - // TODO this is a temp hack - final File propsFile = new File(outputDirectory, "classes/native-image.properties"); - if (propsFile.exists()) { - final Properties properties = new Properties(); - try (FileInputStream is = new FileInputStream(propsFile)) { - try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) { - properties.load(isr); - } - } - for (String propertyName : properties.stringPropertyNames()) { - final String propertyValue = properties.getProperty(propertyName); - // todo maybe just -D is better than -J-D in this case - if (propertyValue == null) { - command.add("-J-D" + propertyName); - } else { - command.add("-J-D" + propertyName + "=" + propertyValue); - } - } - } - if(config != null) { - if(config.getOptionalValue("shamrock.ssl.native", Boolean.class).orElse(false)) { - enableHttpsUrlHandler = true; - enableJni = true; - enableAllSecurityServices = true; - } - } - if (additionalBuildArgs != null) { - additionalBuildArgs.forEach(command::add); - } - 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("-jar"); - command.add(finalName + "-runner.jar"); - //https://github.com/oracle/graal/issues/660 - command.add("-J-Djava.util.concurrent.ForkJoinPool.common.parallelism=1"); - if (reportErrorsAtRuntime) { - command.add("-H:+ReportUnsupportedElementsAtRuntime"); - } - if (debugSymbols) { - command.add("-g"); - } - if (debugBuildProcess) { - command.add("-J-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=y"); - } - if(!disableReports) { - command.add("-H:+PrintAnalysisCallTree"); - } - if (dumpProxies) { - command.add("-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true"); - if (enableServer) { - getLog().warn( "Options dumpProxies and enableServer are both enabled: this will get the proxies dumped in an unknown external working directory" ); - } - } - if(nativeImageXmx != null) { - command.add("-J-Xmx" + nativeImageXmx); - } - List protocols = new ArrayList<>(2); - if(enableHttpUrlHandler) { - protocols.add("http"); - } - if(enableHttpsUrlHandler) { - protocols.add("https"); - } - if(!protocols.isEmpty()) { - command.add("-H:EnableURLProtocols="+String.join(",", protocols)); - } - if(enableAllSecurityServices) { - command.add("--enable-all-security-services"); - } - if (enableRetainedHeapReporting) { - command.add("-H:+PrintRetainedHeapHistogram"); - } - if (enableCodeSizeReporting) { - command.add("-H:+PrintCodeSizeReport"); - } - if (! enableIsolates) { - command.add("-H:-SpawnIsolates"); - } - if (enableJni) { - command.add("-H:+JNI"); - } - else { - command.add("-H:-JNI"); - } - if(!enableServer) { - command.add("--no-server"); - } - if (enableVMInspection) { - command.add("-H:+AllowVMInspection"); - } - if (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 (fullStackTraces) { - command.add("-H:+StackTrace"); - } - else { - command.add("-H:-StackTrace"); - } - - getLog().info(command.stream().collect(Collectors.joining(" "))); - CountDownLatch errorReportLatch = new CountDownLatch(1); - - ProcessBuilder pb = new ProcessBuilder(command.toArray(new String[0])); - pb.directory(outputDirectory); - pb.redirectInput(ProcessBuilder.Redirect.INHERIT); - pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); - - Process process = pb.start(); - new Thread(new ErrorReplacingProcessReader(process.getErrorStream(), new File(outputDirectory, "reports"), errorReportLatch)).start(); - errorReportLatch.await(); - if (process.waitFor() != 0) { - throw new RuntimeException("Image generation failed"); - } - System.setProperty("native.image.path", finalName + "-runner"); - - } catch (Exception e) { - throw new MojoFailureException("Failed to build native image", e); - } - } - - //FIXME remove after transition period - private boolean isThisGraalVMRCObsolete() { - final String vmName = System.getProperty( "java.vm.name" ); - getLog().info( "Running Shamrock native-image plugin on " + vmName ); - if (vmName.contains( "-rc9" ) || vmName.contains( "-rc8")) { - getLog().error( "Out of date RC build of GraalVM detected! Please upgrade to RC10" ); - return true; - } - return false; - } - -} diff --git a/maven/src/main/java/org/jboss/shamrock/maven/ReportAnalyzer.java b/maven/src/main/java/org/jboss/shamrock/maven/ReportAnalyzer.java deleted file mode 100644 index 103bfbc69b810..0000000000000 --- a/maven/src/main/java/org/jboss/shamrock/maven/ReportAnalyzer.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jboss.shamrock.maven; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class ReportAnalyzer { - - - static Pattern PATTERN = Pattern.compile("(.*?)([^\\s(]+)\\.([^.]+\\(.*?\\):[^\\s])"); - - /** - * Analyze the contents of the call tree report produced by Substrate when using -H:+PrintAnalysisCallTree, - * and does a more meaningful analysis of what is causing a type to be retained. - * - * In particular for virtual or interface methods that have multuple implementations what is calling this method - * is not really important, its what caused this particular instance of the class to be created that is important - * (e.g. if you have an instance of Runnable, you don't care about all the different parts that call runnable, you - * care about what created this particular instance). - * - * If a virtual or interface call is detected with multiple implementations then printing the current call flow - * is abandonded, and instead the call flow for the constructor of the current object is printed instead. - * - */ - public static String analyse(String report, String className, String methodName) throws Exception { - Deque lines = new ArrayDeque<>(); - try (BufferedReader in = new BufferedReader(new FileReader(report))) { - for (String re = in.readLine(); re != null; re = in.readLine()) { - lines.add(re); - } - } - - String first = lines.pop(); - if (!first.equals("VM Entry Points")) { - throw new IllegalArgumentException("Unexpected first line in file " + first); - } - List parents = new ArrayList<>(); - Map> byClassMap = new HashMap<>(); - Map> constructors = new HashMap<>(); - Node last = null; - for (String line : lines) { - if (line.trim().isEmpty()) { - continue; - } - int start = 0; - int lc = 0; - for (; start < line.length(); ++start) { - char c = line.charAt(start); - if (c == '├' || c == '└') { - break; - } else { - lc++; - } - } - Matcher matcher = PATTERN.matcher(line.substring(start + 3)); - if (!matcher.find()) { - throw new RuntimeException("Failed " + line); - } - String type = matcher.group(1).trim(); - String clz = matcher.group(2); - String method = matcher.group(3); - Node parent; - if (last == null) { - parent = null; - } else if (last.indent < lc) { - parent = last; - } else { - parent = last; - while (parent != null) { - parent = parent.parent; - if (parent == null || parent.indent < lc) { - break; - } - } - } - Node n = new Node(lc, type, clz, method, parent); - if (parent == null) { - parents.add(n); - } else { - n.parent.children.add(n); - } - byClassMap.computeIfAbsent(clz, (k) -> new ArrayList<>()).add(n); - if (method.startsWith("")) { - constructors.computeIfAbsent(clz, (k) -> new ArrayList<>()).add(n); - } - last = n; - } - - List dm = byClassMap.getOrDefault(className, new ArrayList<>()).stream().filter((s) -> s.method.startsWith(methodName + "(")).collect(Collectors.toList()); - - - Deque runQueue = new ArrayDeque<>(dm); - Set attemptedClasses = new HashSet<>(); - if (methodName.equals("")) { - attemptedClasses.add(className); - } - StringBuilder ret = new StringBuilder(); - StringBuilder sb = new StringBuilder(); - while (!runQueue.isEmpty()) { - Node current = runQueue.pop(); - sb.append("Possible path to " + current.className + "." + current.method); - while (current != null) { - sb.append("\t" + current.className + "." + current.method + '\n'); - - String reason = null; - if(current.parent == null || current.parent.children.size() > 1) { - if (current.type.equals("is overridden by")) { - reason = "This is an implementation of " + current.parent.className + " printing path to constructors of " + current.className; - } else if (current.type.equals("is implemented by")) { - reason = "This is an implementation of " + current.parent.className + " printing path to constructors of " + current.className; - } - } - if (reason != null) { - if (!attemptedClasses.contains(current.className)) { - attemptedClasses.add(current.className); - List toAdd = constructors.getOrDefault(current.className, new ArrayList<>()); - runQueue.addAll(toAdd); - sb.append(reason + '\n'); - sb.append("\n"); - ret.append(sb); - } - //note that we discard the string builder if it is part of attemptedClasses, as this basically - //represents an alternate path that we have already displayed - sb.setLength(0); - break; - } - - current = current.parent; - - } - } - ret.append(sb); - return ret.toString(); - } - - public static class Node { - final int indent; - final String type; - final String className; - final String method; - final Node parent; - List children = new ArrayList<>(); - - Node(int indent, String type, String className, String method, Node parent) { - this.indent = indent; - this.type = type; - this.className = className; - this.method = method; - this.parent = parent; - } - - @Override - public String toString() { - return className + '.' + method; - } - } -} -