| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.geronimo.arthur.maven.mojo; |
| |
| import lombok.Getter; |
| import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageConfiguration; |
| import org.apache.geronimo.arthur.impl.nativeimage.ArthurNativeImageExecutor; |
| import org.apache.geronimo.arthur.impl.nativeimage.generator.extension.AnnotationExtension; |
| import org.apache.geronimo.arthur.impl.nativeimage.installer.SdkmanGraalVMInstaller; |
| import org.apache.geronimo.arthur.maven.extension.MavenArthurExtension; |
| import org.apache.geronimo.arthur.spi.ArthurExtension; |
| import org.apache.geronimo.arthur.spi.model.ClassReflectionModel; |
| import org.apache.geronimo.arthur.spi.model.DynamicProxyModel; |
| import org.apache.geronimo.arthur.spi.model.ResourceBundleModel; |
| import org.apache.geronimo.arthur.spi.model.ResourceModel; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.handler.DefaultArtifactHandler; |
| import org.apache.maven.model.Dependency; |
| import org.apache.maven.plugins.annotations.Component; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.DefaultDependencyResolutionRequest; |
| import org.apache.maven.project.DependencyResolutionException; |
| import org.apache.maven.project.DependencyResolutionRequest; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectHelper; |
| import org.apache.maven.project.ProjectDependenciesResolver; |
| import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; |
| import org.apache.xbean.finder.AnnotationFinder; |
| import org.apache.xbean.finder.archive.Archive; |
| import org.apache.xbean.finder.archive.CompositeArchive; |
| import org.apache.xbean.finder.archive.FilteredArchive; |
| import org.apache.xbean.finder.filter.Filter; |
| import org.apache.xbean.finder.filter.Filters; |
| import org.eclipse.aether.graph.DependencyNode; |
| import org.eclipse.aether.graph.DependencyVisitor; |
| |
| import javax.json.bind.Jsonb; |
| import javax.json.bind.JsonbBuilder; |
| import javax.json.bind.JsonbConfig; |
| import javax.json.bind.config.PropertyOrderStrategy; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Field; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Properties; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.stream.Stream; |
| import java.util.stream.StreamSupport; |
| |
| import static java.lang.ClassLoader.getSystemClassLoader; |
| import static java.util.Comparator.comparing; |
| import static java.util.Optional.ofNullable; |
| import static java.util.stream.Collectors.joining; |
| import static java.util.stream.Collectors.toList; |
| import static java.util.stream.Collectors.toMap; |
| import static lombok.AccessLevel.PROTECTED; |
| import static org.apache.maven.plugins.annotations.LifecyclePhase.PACKAGE; |
| import static org.apache.maven.plugins.annotations.ResolutionScope.TEST; |
| import static org.apache.xbean.finder.archive.ClasspathArchive.archive; |
| |
| /** |
| * Generates a native binary from current project. |
| */ |
| @Mojo(name = "native-image", defaultPhase = PACKAGE, requiresDependencyResolution = TEST, threadSafe = true) |
| public class NativeImageMojo extends ArthurMojo { |
| // |
| // ArthurNativeImageConfiguration |
| // |
| |
| /** |
| * custom native-image arguments. |
| */ |
| @Parameter(property = "arthur.customOptions") |
| private List<String> customOptions; |
| |
| /** |
| * custom pre-built classpath, if not set it defaults on the project dependencies. |
| */ |
| @Parameter(property = "arthur.classpath") |
| private List<String> classpath; |
| |
| /** |
| * JSON java.lang.reflect.Proxy configuration. |
| */ |
| @Parameter(property = "arthur.dynamicProxyConfigurationFiles") |
| private List<String> dynamicProxyConfigurationFiles; |
| |
| /** |
| * JSON reflection configuration. |
| */ |
| @Parameter(property = "arthur.reflectionConfigurationFiles") |
| private List<String> reflectionConfigurationFiles; |
| |
| /** |
| * JSON resources configuration. |
| */ |
| @Parameter(property = "arthur.resourcesConfigurationFiles") |
| private List<String> resourcesConfigurationFiles; |
| |
| /** |
| * resource bundle qualified names to include. |
| */ |
| @Parameter(property = "arthur.includeResourceBundles") |
| private List<String> includeResourceBundles; |
| |
| /** |
| * Classes to intiialize at run time. |
| */ |
| @Parameter(property = "arthur.initializeAtRunTime") |
| private List<String> initializeAtRunTime; |
| |
| /** |
| * Classes to initialize at build time. |
| */ |
| @Parameter(property = "arthur.initializeAtBuildTime") |
| private List<String> initializeAtBuildTime; |
| |
| /** |
| * Limit the number of compilable methods. |
| */ |
| @Parameter(property = "arthur.maxRuntimeCompileMethods", defaultValue = "1000") |
| private int maxRuntimeCompileMethods; |
| |
| /** |
| * Enforce `maxRuntimeCompileMethods`. |
| */ |
| @Parameter(property = "arthur.enforceMaxRuntimeCompileMethods", defaultValue = "true") |
| private boolean enforceMaxRuntimeCompileMethods; |
| |
| /** |
| * Should all charsets be added. |
| */ |
| @Parameter(property = "arthur.addAllCharsets", defaultValue = "true") |
| private boolean addAllCharsets; |
| |
| /** |
| * Should exception stacks be reported. |
| */ |
| @Parameter(property = "arthur.reportExceptionStackTraces", defaultValue = "true") |
| private boolean reportExceptionStackTraces; |
| |
| /** |
| * Should initialiation of classes be printed - mainly for debug purposes. |
| */ |
| @Parameter(property = "arthur.printClassInitialization", defaultValue = "false") |
| private boolean printClassInitialization; |
| |
| /** |
| * Behavior when native compilation fails, it is recommended to keep it to "no". |
| * Supported values are `no`, `force` and `auto`. |
| */ |
| @Parameter(property = "arthur.fallbackMode", defaultValue = "no") |
| private ArthurNativeImageConfiguration.FallbackMode fallbackMode; |
| |
| /** |
| * Should the image be static or dynamic (jvm part). |
| */ |
| @Parameter(property = "arthur.buildStaticImage", defaultValue = "true") |
| private boolean buildStaticImage; |
| |
| /** |
| * Should incomplete classpath be tolerated. |
| */ |
| @Parameter(property = "arthur.allowIncompleteClasspath") |
| private Boolean allowIncompleteClasspath; |
| |
| /** |
| * Should unsupported element be reported at runtime or not. It is not a recommended option but it is often needed. |
| */ |
| @Parameter(property = "arthur.reportUnsupportedElementsAtRuntime", defaultValue = "true") |
| private boolean reportUnsupportedElementsAtRuntime; |
| |
| /** |
| * Should security services be included. |
| */ |
| @Parameter(property = "arthur.enableAllSecurityServices") |
| private Boolean enableAllSecurityServices; |
| |
| /** |
| * Which main to compile. |
| */ |
| @Parameter(property = "arthur.main", required = true) |
| private String main; |
| |
| /** |
| * Where to put the output binary. |
| */ |
| @Parameter(property = "arthur.output", defaultValue = "${project.build.directory}/${project.artifactId}.graal.bin") |
| private String output; |
| |
| /** |
| * The execution will fork native-image process, should IO be inherited from maven process (recommended). |
| */ |
| @Getter(PROTECTED) |
| @Parameter(property = "arthur.inheritIO", defaultValue = "true") |
| private boolean inheritIO; |
| |
| /** |
| * Should graal build server be used (a bit like gradle daemon), it is very discouraged to be used cause invalidation is not yet well handled. |
| * Deprecated in recent graalvm versions. |
| */ |
| @Deprecated |
| @Parameter(property = "arthur.noServer", defaultValue = "false") |
| private boolean noServer; |
| |
| // |
| // Other maven injections |
| // |
| |
| /** |
| * Inline configuration model (appended to `reflectionConfigurationFiles`). |
| */ |
| @Parameter |
| private List<ClassReflectionModel> reflections; |
| |
| /** |
| * Inline resource bundle model (appended to `reflectionConfigurationFiles`). |
| */ |
| @Parameter |
| private List<ResourceBundleModel> bundles; |
| |
| /** |
| * Inline resources model (appended to `resourcesConfigurationFiles`). |
| */ |
| @Parameter |
| private List<ResourceModel> resources; |
| |
| /** |
| * Inline dynamic proxy configuration (appended to `dynamicProxyConfigurationFiles`). |
| */ |
| @Parameter |
| private List<DynamicProxyModel> dynamicProxies; |
| |
| /** |
| * Should this mojo be skipped. |
| */ |
| @Parameter(property = "arthur.skip") |
| private boolean skip; |
| |
| /** |
| * Should the build be done with test dependencies (and binaries). |
| */ |
| @Parameter(property = "arthur.supportTestArtifacts", defaultValue = "false") |
| private boolean supportTestArtifacts; |
| |
| /** |
| * By default arthur runs the extension with a dedicated classloader built from the project having as parent the JVM, |
| * this enables to use the mojo as parent instead). |
| */ |
| @Parameter(property = "arthur.useTcclAsScanningParentClassLoader", defaultValue = "false") |
| private boolean useTcclAsScanningParentClassLoader; |
| |
| /** |
| * groupId:artifactId list of ignored artifact during the pre-build phase. |
| */ |
| @Parameter(property = "arthur.excludedArtifacts") |
| private List<String> excludedArtifacts; |
| |
| /** |
| * Classes or packages (startsWith is used to test entries). |
| */ |
| @Parameter(property = "arthur.scanningClassesOrPackagesExcludes") |
| private List<String> scanningClassesOrPackagesExcludes; |
| |
| /** |
| * groupId:artifactId list of ignored artifact during the scanning phase. |
| * Compared to `excludedArtifacts`, it keeps the jar in the scanning/extension classloader |
| * but it does not enable to find any annotation in it. |
| * Note that putting `*` will disable the scanning completely. |
| */ |
| @Parameter(property = "arthur.scanningExcludedArtifacts") |
| private List<String> scanningExcludedArtifacts; |
| |
| /** |
| * `<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>` list of artifacts appended to graal build. |
| * If you don't want transitive dependencies to be included, you can append to the coordinates `?transitive=false`. |
| */ |
| @Parameter(property = "arthur.graalExtensions") |
| private List<String> graalExtensions; |
| |
| /** |
| * List of types used in the build classpath, typically enables to ignore tar.gz/natives for example. |
| */ |
| @Parameter(property = "arthur.supportedTypes", defaultValue = "jar,zip") |
| private List<String> supportedTypes; |
| |
| /** |
| * Should jar be used instead of exploded folder (target/classes). |
| * Note this option disable the support of module test classes. |
| */ |
| @Parameter(property = "arthur.usePackagedArtifact", defaultValue = "false") |
| private boolean usePackagedArtifact; |
| |
| /** |
| * Should binary artifact be attached. |
| */ |
| @Parameter(property = "arthur.attach", defaultValue = "true") |
| private boolean attach; |
| |
| /** |
| * If `attach` is true, the classifier to use the binary file, `none` will skip the classifier. |
| */ |
| @Parameter(property = "arthur.attachClassifier", defaultValue = "arthur") |
| private String attachClassifier; |
| |
| /** |
| * If `attach` is true, the type to use to attach the binary file. |
| */ |
| @Parameter(property = "arthur.attachType", defaultValue = "bin") |
| private String attachType; |
| |
| /** |
| * Properties passed to the extensions if needed. |
| */ |
| @Parameter |
| private Map<String, String> extensionProperties; |
| |
| @Parameter(defaultValue = "${project.packaging}", readonly = true) |
| private String packaging; |
| |
| @Parameter(defaultValue = "${project.version}", readonly = true) |
| private String version; |
| |
| @Parameter(defaultValue = "${project.artifactId}", readonly = true) |
| private String artifactId; |
| |
| @Parameter(defaultValue = "${project.groupId}", readonly = true) |
| private String groupId; |
| |
| @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}") |
| private File jar; |
| |
| @Parameter(defaultValue = "${project.build.outputDirectory}") |
| private File classes; |
| |
| @Parameter(defaultValue = "${project.build.testOutputDirectory}") |
| private File testClasses; |
| |
| @Component |
| private ProjectDependenciesResolver dependenciesResolver; |
| |
| @Component |
| private DependencyGraphBuilder graphBuilder; |
| |
| @Component |
| private MavenProjectHelper helper; |
| |
| private String cachedVersion; |
| |
| @Override |
| public void execute() { |
| if (skip) { |
| getLog().info("Skipping execution as requested"); |
| return; |
| } |
| if ("pom".equals(packaging)) { |
| getLog().info("Skipping packaging pom"); |
| return; |
| } |
| |
| final Map<Artifact, Path> classpathEntries = findClasspathFiles().collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a)); |
| |
| final ArthurNativeImageConfiguration configuration = getConfiguration(classpathEntries.values()); |
| configuration.complete(graalVersion); |
| |
| if (nativeImage == null) { |
| final SdkmanGraalVMInstaller graalInstaller = createInstaller(); |
| final Path graalHome = graalInstaller.install(); |
| getLog().info("Using GRAAL: " + graalHome); |
| configuration.setNativeImage(graalInstaller.installNativeImage().toAbsolutePath().toString()); |
| } |
| |
| final URL[] urls = classpathEntries.values().stream() |
| .map(it -> { |
| try { |
| return it.toUri().toURL(); |
| } catch (final MalformedURLException e) { |
| throw new IllegalStateException(e); |
| } |
| }).toArray(URL[]::new); |
| final Thread thread = Thread.currentThread(); |
| final ClassLoader parentLoader = useTcclAsScanningParentClassLoader ? |
| thread.getContextClassLoader() : getSystemClassLoader(); |
| final ClassLoader oldLoader = thread.getContextClassLoader(); |
| if (getLog().isDebugEnabled()) { |
| getLog().debug("Classpath:\n" + Stream.of(urls).map(URL::toExternalForm).collect(joining("\n"))); |
| } |
| try (final URLClassLoader loader = new URLClassLoader(urls, parentLoader) { |
| @Override |
| protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { |
| if (name != null) { |
| if (name.startsWith("org.")) { |
| final String org = name.substring("org.".length()); |
| if (org.startsWith("slf4j.")) { |
| return oldLoader.loadClass(name); |
| } |
| if (org.startsWith("apache.geronimo.arthur.")) { |
| final String arthur = org.substring("apache.geronimo.arthur.".length()); |
| if (arthur.startsWith("api.") || arthur.startsWith("spi") || arthur.startsWith("impl")) { |
| return oldLoader.loadClass(name); |
| } |
| } |
| } |
| } |
| return super.loadClass(name, resolve); |
| } |
| }; final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() |
| .setProperty("johnzon.cdi.activated", false) |
| .withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL))) { |
| thread.setContextClassLoader(loader); |
| final Predicate<Artifact> scanningFilter = createScanningFilter(); |
| final Function<Archive, Archive> archiveProcessor = createArchiveFilter(); |
| final AnnotationFinder finder = new AnnotationFinder(archiveProcessor.apply(new CompositeArchive(classpathEntries.entrySet().stream() |
| .filter(e -> scanningFilter.test(e.getKey())) |
| .map(Map.Entry::getValue) |
| .map(path -> { |
| try { |
| return archive(loader, path.toUri().toURL()); |
| } catch (final MalformedURLException e) { // unlikely |
| throw new IllegalStateException(e); |
| } |
| }) |
| .collect(toList())))); |
| final AtomicBoolean finderLinked = new AtomicBoolean(); |
| MavenArthurExtension.with( |
| reflections, resources, bundles, dynamicProxies, |
| () -> new ArthurNativeImageExecutor( |
| ArthurNativeImageExecutor.ExecutorConfiguration.builder() |
| .jsonSerializer(jsonb::toJson) |
| .annotatedClassFinder(finder::findAnnotatedClasses) |
| .annotatedFieldFinder(finder::findAnnotatedFields) |
| .annotatedMethodFinder(finder::findAnnotatedMethods) |
| .extensionProperties(getExtensionProperties()) |
| .implementationFinder(p -> { |
| if (finderLinked.compareAndSet(false, true)) { |
| finder.enableFindImplementations().enableFindSubclasses(); |
| } |
| final Class parent = Class.class.cast(p); |
| final List<Class<?>> implementations = finder.findImplementations(parent); |
| final List<Class<?>> subclasses = finder.findSubclasses(parent); |
| if (implementations.size() + subclasses.size() == 0) { |
| return implementations; // empty |
| } |
| final List<Class<?>> output = new ArrayList<>(implementations.size() + subclasses.size()); |
| output.addAll(implementations); |
| output.addAll(subclasses); |
| return output; |
| }) |
| .configuration(configuration) |
| .workingDirectory(workdir.toPath().resolve("generated_configuration")) |
| .build()) { |
| @Override |
| protected Iterable<ArthurExtension> loadExtensions() { |
| return Stream.concat( |
| // classloading bypasses them since TCCL is a fake loader with the JVM as parent |
| Stream.of(new AnnotationExtension(), new MavenArthurExtension()), |
| // graalextensions |
| StreamSupport.stream(super.loadExtensions().spliterator(), false)) |
| // ensure we dont duplicate any extension |
| .distinct() |
| .sorted(comparing(ArthurExtension::order)) |
| .collect(toList()); |
| } |
| } |
| .run()); |
| } catch (final Exception e) { |
| throw new IllegalStateException(e); |
| } finally { |
| thread.setContextClassLoader(oldLoader); |
| } |
| |
| if (propertiesPrefix != null) { |
| project.getProperties().setProperty(propertiesPrefix + "binary.path", output); |
| } |
| |
| if (attach) { |
| if (!"none".equals(attachClassifier) && attachClassifier != null && !attachClassifier.isEmpty()) { |
| helper.attachArtifact(project, attachType, attachClassifier, new File(output)); |
| } else { |
| helper.attachArtifact(project, attachType, new File(output)); |
| } |
| } |
| } |
| |
| private Function<Archive, Archive> createArchiveFilter() { |
| if (scanningClassesOrPackagesExcludes == null || scanningClassesOrPackagesExcludes.isEmpty()) { |
| return Function.identity(); |
| } |
| final Filter filter = Filters.invert(Filters.prefixes( |
| scanningClassesOrPackagesExcludes.stream() |
| .map(String::trim) |
| .filter(it -> !it.isEmpty()) |
| .distinct() |
| .toArray(String[]::new))); |
| return a -> new FilteredArchive(a, filter); |
| } |
| |
| private Map<String, String> getExtensionProperties() { |
| final Map<String, String> props = extensionProperties == null ? new HashMap<>() : new HashMap<>(extensionProperties); |
| props.putIfAbsent("classes", project.getBuild().getOutputDirectory()); |
| return props; |
| } |
| |
| private Predicate<Artifact> createScanningFilter() { |
| if (scanningExcludedArtifacts != null && scanningExcludedArtifacts.contains("*")) { |
| return a -> false; |
| } |
| if (scanningExcludedArtifacts == null || scanningExcludedArtifacts.isEmpty()) { |
| return a -> true; |
| } |
| return a -> { |
| final String coord = a.getGroupId() + ':' + a.getArtifactId(); |
| return scanningExcludedArtifacts.stream().noneMatch(it -> it.equals(coord)); |
| }; |
| } |
| |
| private Stream<? extends Map.Entry<? extends Artifact, Path>> findClasspathFiles() { |
| final Artifact artifactGav = new org.apache.maven.artifact.DefaultArtifact( |
| groupId, artifactId, version, "compile", packaging, null, new DefaultArtifactHandler()); |
| return Stream.concat(Stream.concat( |
| usePackagedArtifact ? |
| Stream.of(jar).map(j -> new AbstractMap.SimpleImmutableEntry<>(artifactGav, j.toPath())) : |
| Stream.concat( |
| Stream.of(classes).map(j -> new AbstractMap.SimpleImmutableEntry<>(artifactGav, j.toPath())), |
| supportTestArtifacts ? Stream.of(testClasses).<Map.Entry<Artifact, Path>>map(j -> |
| new AbstractMap.SimpleImmutableEntry<>(new org.apache.maven.artifact.DefaultArtifact( |
| groupId, artifactId, version, "compile", packaging, "test", new DefaultArtifactHandler()), |
| j.toPath())) : |
| Stream.empty()), |
| project.getArtifacts().stream() |
| .filter(a -> !excludedArtifacts.contains(a.getGroupId() + ':' + a.getArtifactId())) |
| .filter(this::handleTestInclusion) |
| .filter(this::isNotSvm) |
| .filter(a -> supportedTypes.contains(a.getType())) |
| .map(a -> new AbstractMap.SimpleImmutableEntry<>(a, a.getFile().toPath()))), |
| resolveExtension()) |
| .filter(e -> Files.exists(e.getValue())); |
| } |
| |
| private boolean handleTestInclusion(final Artifact a) { |
| return !"test".equals(a.getScope()) || supportTestArtifacts; |
| } |
| |
| private boolean isNotSvm(final Artifact artifact) { |
| return !"com.oracle.substratevm".equals(artifact.getGroupId()) && |
| !"org.graalvm.nativeimage".equals(artifact.getGroupId()); |
| } |
| |
| private Stream<? extends Map.Entry<? extends Artifact, Path>> resolveExtension() { |
| return ofNullable(graalExtensions) |
| .map(e -> e.stream() |
| .map(this::prepareExtension) |
| .flatMap(art -> art.endsWith("?transitive=false") ? |
| Stream.of(toArtifact(art.substring(0, art.length() - "?transitive=false".length()))) : |
| resolveTransitiveDependencies(toArtifact(art))) |
| .map(it -> new AbstractMap.SimpleImmutableEntry<>( |
| toMavenArtifact(it), |
| resolve(it).getFile().toPath()))) |
| .orElseGet(Stream::empty); |
| } |
| |
| private Stream<org.eclipse.aether.artifact.Artifact> resolveTransitiveDependencies(final org.eclipse.aether.artifact.Artifact artifact) { |
| final Dependency rootDependency = new Dependency(); |
| rootDependency.setGroupId(artifact.getGroupId()); |
| rootDependency.setArtifactId(artifact.getArtifactId()); |
| rootDependency.setVersion(artifact.getVersion()); |
| rootDependency.setClassifier(artifact.getClassifier()); |
| rootDependency.setType(artifact.getExtension()); |
| |
| final MavenProject fakeProject = new MavenProject(); |
| fakeProject.setRemoteArtifactRepositories(project.getRemoteArtifactRepositories()); |
| fakeProject.setSnapshotArtifactRepository(project.getDistributionManagementArtifactRepository()); |
| fakeProject.setPluginArtifactRepositories(project.getPluginArtifactRepositories()); |
| fakeProject.getDependencies().add(rootDependency); |
| |
| final DependencyResolutionRequest request = new DefaultDependencyResolutionRequest(); |
| request.setMavenProject(fakeProject); |
| request.setRepositorySession(repositorySystemSession); |
| |
| final Collection<org.eclipse.aether.artifact.Artifact> artifacts = new ArrayList<>(); |
| try { |
| dependenciesResolver.resolve(request).getDependencyGraph().accept(new DependencyVisitor() { |
| @Override |
| public boolean visitEnter(org.eclipse.aether.graph.DependencyNode node) { |
| return true; |
| } |
| |
| @Override |
| public boolean visitLeave(org.eclipse.aether.graph.DependencyNode node) { |
| final org.eclipse.aether.artifact.Artifact artifact = node.getArtifact(); |
| if (artifact == null) { |
| if (node.getChildren() != null) { |
| node.getChildren().stream() |
| .map(DependencyNode::getArtifact) |
| .filter(Objects::nonNull) |
| .forEach(artifacts::add); |
| } else { |
| getLog().warn(node + " has no artifact"); |
| } |
| } else { |
| artifacts.add(artifact); |
| } |
| return true; |
| } |
| }); |
| return artifacts.stream().map(NativeImageMojo.this::resolve); |
| } catch (final DependencyResolutionException e) { |
| throw new IllegalStateException(e.getMessage(), e); |
| } |
| } |
| |
| // support short name for our knights |
| private String prepareExtension(final String ext) { |
| if (ext.contains(":")) { |
| return ext; |
| } |
| return "org.apache.geronimo.arthur.knights:" + ext + (ext.endsWith("-knight") ? "" : "-knight") + ':' + lookupVersion(); |
| } |
| |
| private String lookupVersion() { |
| if (cachedVersion == null) { |
| final Properties properties = new Properties(); |
| try (final InputStream stream = Thread.currentThread().getContextClassLoader() |
| .getResourceAsStream("META-INF/maven/org.apache.geronimo.arthur/arthur-maven-plugin/pom.properties")) { |
| properties.load(stream); |
| } catch (final IOException e) { |
| throw new IllegalStateException(e); |
| } |
| cachedVersion = properties.getProperty("version"); |
| } |
| return cachedVersion; |
| } |
| |
| private org.apache.maven.artifact.DefaultArtifact toMavenArtifact(final org.eclipse.aether.artifact.Artifact it) { |
| return new org.apache.maven.artifact.DefaultArtifact( |
| it.getGroupId(), it.getArtifactId(), it.getVersion(), "compile", |
| it.getExtension(), it.getClassifier(), new DefaultArtifactHandler()); |
| } |
| |
| private ArthurNativeImageConfiguration getConfiguration(final Collection<Path> classpathFiles) { |
| final ArthurNativeImageConfiguration configuration = new ArthurNativeImageConfiguration(); |
| Stream.of(ArthurNativeImageConfiguration.class.getDeclaredFields()) |
| .filter(field -> field.isAnnotationPresent(ArthurNativeImageConfiguration.GraalCommandPart.class)) |
| .map(this::asAccessible) |
| .forEach(field -> { |
| final Class<?> fieldHolder; |
| if ("nativeImage".equals(field.getName())) { |
| fieldHolder = ArthurMojo.class; |
| } else { |
| fieldHolder = NativeImageMojo.class; |
| } |
| try { |
| final Field mojoField = asAccessible(fieldHolder.getDeclaredField(field.getName())); |
| field.set(configuration, mojoField.get(NativeImageMojo.this)); |
| } catch (final NoSuchFieldException | IllegalAccessException e) { |
| throw new IllegalArgumentException(e); |
| } |
| }); |
| if (configuration.getClasspath() == null || configuration.getClasspath().isEmpty()) { |
| configuration.setClasspath(classpathFiles.stream().map(Path::toAbsolutePath).map(Object::toString).collect(toList())); |
| } |
| configuration.setInheritIO(inheritIO); |
| return configuration; |
| } |
| |
| private Field asAccessible(final Field field) { |
| if (!field.isAccessible()) { |
| field.setAccessible(true); |
| } |
| return field; |
| } |
| } |