From fb8edf277e56e2779408612dd16a7121c545d62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lazar=20Mitrovi=C4=87?= Date: Tue, 7 Jun 2022 02:07:52 +0200 Subject: [PATCH] Rework Maven plugin, add excludeConfig option, set useBuildArg to false on nix systems. - Completely reworked Maven plugin (should fix many of previous issues and inconsistencies between main and test builds). - Added `classesDirectory`, `debug`, `fallback`, `verbose`, `sharedLibrary`, `configurationFileDirectories`, `excludeConfig` and `jvmArgs` properties in order to match those present in the Gradle plugin. - `useArgFile` is now set to true by default only on Windows - Added `excludeConfig` configuration option that allows skipping of configuration files that are present in classpath `jar` s. --- docs/src/docs/asciidoc/index.adoc | 7 + .../buildtools/gradle/NativeImagePlugin.java | 5 +- .../gradle/dsl/GraalVMExtension.java | 4 +- .../gradle/dsl/NativeImageOptions.java | 14 +- .../NativeImageCommandLineProvider.java | 8 +- .../buildtools/maven/AbstractNativeMojo.java | 441 +++++++++++++++++- .../buildtools/maven/NativeBuildMojo.java | 286 +----------- .../buildtools/maven/NativeTestMojo.java | 215 ++++----- .../config/ExcludeConfigConfiguration.java | 66 +++ 9 files changed, 613 insertions(+), 433 deletions(-) create mode 100644 native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/config/ExcludeConfigConfiguration.java diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 091352487..ac4847e5e 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -23,6 +23,13 @@ If you are interested in contributing, please refer to our https://github.com/gr * Introduced the `metadataCopy` task. * Introduced the concept of agent modes. ** Under the hood, the agent mode dictates what options are passed to the agent and how metadata produced by multiple runs get merged. +* Added `excludeConfig` configuration option that allows skipping of configuration files that are present in classpath `jar` s. +* `useArgFile` is now set to true by default only on Windows. + +==== Maven plugin +* Completely reworked Maven plugin (should fix many of previous issues and inconsistencies between main and test builds). +* Added `classesDirectory`, `debug`, `fallback`, `verbose`, `sharedLibrary`, `configurationFileDirectories`, `excludeConfig` and `jvmArgs` properties in order to match those present in the Gradle plugin. +* `useArgFile` is now set to true by default only on Windows. === Release 0.9.11 diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java index 4c054ff0b..6673e0c23 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -126,6 +126,7 @@ import static org.graalvm.buildtools.gradle.internal.GradleUtils.transitiveProjectArtifacts; import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.graalvmHomeProvider; import static org.graalvm.buildtools.utils.SharedConstants.AGENT_PROPERTY; +import static org.graalvm.buildtools.utils.SharedConstants.IS_WINDOWS; /** * Gradle plugin for GraalVM Native Image. @@ -170,7 +171,7 @@ public void apply(Project project) { logger = GraalVMLogger.of(project.getLogger()); DefaultGraalVmExtension graalExtension = (DefaultGraalVmExtension) registerGraalVMExtension(project); - graalExtension.getUseArgFile().convention(true); + graalExtension.getUseArgFile().convention(IS_WINDOWS); project.getPlugins() .withType(JavaPlugin.class, javaPlugin -> configureJavaProject(project, nativeImageServiceProvider, graalExtension)); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java index e9f1ee2b3..dd7089704 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -99,7 +99,7 @@ public interface GraalVMExtension { /** * Property driving the use of @-arg files when invoking native image. - * This is enabled by default. For older native-image versions, this + * This is enabled by default on Windows. For older native-image versions, this * needs to be disabled. * * @return the argument file property diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java index 45025c11d..7335ba8ca 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -171,7 +171,7 @@ public interface NativeImageOptions extends Named { Property getJavaLauncher(); /** - * Returns the list of configuration file directories (e.g resource-config.json, ...) which need + * Returns the list of configuration file directories (e.g. resource-config.json, ...) which need * to be passed to native-image. * * @return a collection of directories @@ -179,6 +179,16 @@ public interface NativeImageOptions extends Named { @InputFiles ConfigurableFileCollection getConfigurationFileDirectories(); + /** + * Returns the map that as contains information about configuration that should be excluded + * during image building. It consists of a jar regular expression as a key and a resource + * regular expression as a value. + * + * @return a map of filters for configuration exclusion + */ + @Input + MapProperty getExcludeConfig(); + @Nested NativeResourcesOptions getResources(); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java index 7a12094f4..3c40684ce 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -104,6 +104,12 @@ public List asArguments() { NativeImageOptions options = getOptions().get(); List cliArgs = new ArrayList<>(20); + options.getExcludeConfig().get().forEach((jarPath, resourcePattern) -> { + cliArgs.add("--exclude-config"); + cliArgs.add(jarPath); + cliArgs.add(resourcePattern); + }); + cliArgs.add("-cp"); String classpathString = buildClasspathString(options); cliArgs.add(classpathString); diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeMojo.java index 4a4e8f8d7..4ed4a5b0b 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeMojo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -44,15 +44,42 @@ import org.apache.maven.artifact.Artifact; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.ToolchainManager; import org.codehaus.plexus.logging.Logger; +import org.graalvm.buildtools.Utils; +import org.graalvm.buildtools.maven.config.ExcludeConfigConfiguration; +import org.graalvm.buildtools.maven.config.MetadataRepositoryConfiguration; +import org.graalvm.buildtools.utils.FileUtils; +import org.graalvm.buildtools.utils.NativeImageUtils; +import org.graalvm.buildtools.utils.SharedConstants; +import org.graalvm.reachability.JvmReachabilityMetadataRepository; +import org.graalvm.reachability.internal.FileSystemRepository; +import javax.inject.Inject; import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,41 +87,318 @@ * @author Sebastien Deleuze */ public abstract class AbstractNativeMojo extends AbstractMojo { + + protected static final String NATIVE_IMAGE_META_INF = "META-INF/native-image"; + protected static final String NATIVE_IMAGE_PROPERTIES_FILENAME = "native-image.properties"; + + @Parameter(defaultValue = "${plugin}", readonly = true) // Maven 3 only + protected PluginDescriptor plugin; + + @Parameter(defaultValue = "${session}", readonly = true) + protected MavenSession session; + @Parameter(defaultValue = "${project}", readonly = true, required = true) protected MavenProject project; + @Parameter(defaultValue = "${mojoExecution}") + protected MojoExecution mojoExecution; + @Parameter(property = "plugin.artifacts", required = true, readonly = true) protected List pluginArtifacts; + @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true) + protected File outputDirectory; + + @Parameter(property = "mainClass") + protected String mainClass; + + @Parameter(property = "imageName", defaultValue = "${project.artifactId}") + protected String imageName; + + @Parameter(property = "classpath") + protected List classpath; + + @Parameter + private File classesDirectory; + + @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true, required = true) + private File defaultClassesDirectory; + + protected final List imageClasspath; + + protected final Set metadataRepositoryPaths; + + @Parameter(property = "debug", defaultValue = "false") + protected boolean debug; + + @Parameter(property = "fallback", defaultValue = "false") + protected boolean fallback; + + @Parameter(property = "verbose", defaultValue = "false") + protected boolean verbose; + + @Parameter(property = "sharedLibrary", defaultValue = "false") + protected boolean sharedLibrary; + + @Parameter(property = "useArgFile") + protected Boolean useArgFile; + @Parameter(property = "buildArgs") protected List buildArgs; - @Parameter(defaultValue = "${session}", readonly = true) - protected MavenSession session; - @Parameter(defaultValue = "${project.build.directory}/native/generated", property = "resourcesConfigDirectory", required = true) - private File resourcesConfigDirectory; + protected File resourcesConfigDirectory; @Parameter(property = "agentResourceDirectory") - private File agentResourceDirectory; + protected File agentResourceDirectory; - @Component - protected ToolchainManager toolchainManager; + @Parameter(property = "excludeConfig") + protected List excludeConfig; + + @Parameter(property = "environmentVariables") + protected Map environment; + + @Parameter(property = "systemPropertyVariables") + protected Map systemProperties; + + @Parameter(property = "configurationFileDirectories") + protected List configFiles; + + @Parameter(property = "jvmArgs") + protected List jvmArgs; + + @Parameter(alias = "metadataRepository") + protected MetadataRepositoryConfiguration metadataRepositoryConfiguration; + + protected JvmReachabilityMetadataRepository metadataRepository; @Component protected Logger logger; + @Component + protected ToolchainManager toolchainManager; + + @Inject + protected AbstractNativeMojo() { + imageClasspath = new ArrayList<>(); + metadataRepositoryPaths = new HashSet<>(); + if (useArgFile == null) { + useArgFile = SharedConstants.IS_WINDOWS; + } + } + + protected List getBuildArgs() throws MojoExecutionException { + final List cliArgs = new ArrayList<>(); + + if (excludeConfig != null) { + excludeConfig.forEach(entry -> { + cliArgs.add("--exclude-config"); + cliArgs.add(entry.getJarPath()); + cliArgs.add(entry.getResourcePattern()); + }); + } + + cliArgs.add("-cp"); + cliArgs.add(getClasspath()); + + if (debug) { + cliArgs.add("-H:GenerateDebugInfo=1"); + } + if (!fallback) { + cliArgs.add("--no-fallback"); + } + if (verbose) { + cliArgs.add("--verbose"); + } + if (sharedLibrary) { + cliArgs.add("--shared"); + } + + cliArgs.add("-H:Path=" + outputDirectory.toPath().toAbsolutePath()); + cliArgs.add("-H:Name=" + imageName); + + if (systemProperties != null) { + for (Map.Entry entry : systemProperties.entrySet()) { + cliArgs.add("-D" + entry.getKey() + "=" + entry.getValue()); + } + } + + if (jvmArgs != null) { + jvmArgs.forEach(jvmArg -> cliArgs.add("-J" + jvmArg)); + } + + maybeAddGeneratedResourcesConfig(buildArgs); + maybeAddReachabilityMetadata(cliArgs); + + if (configFiles != null && !configFiles.isEmpty()) { + cliArgs.add("-H:ConfigurationFileDirectories=" + + configFiles.stream() + .map(Paths::get) + .map(Path::toAbsolutePath) + .map(Path::toString) + .collect(Collectors.joining(",")) + ); + } + + if (mainClass != null && !mainClass.equals(".")) { + cliArgs.add("-H:Class=" + mainClass); + } + + if (buildArgs != null && !buildArgs.isEmpty()) { + for (String buildArg : buildArgs) { + cliArgs.addAll(Arrays.asList(buildArg.split("\\s+"))); + } + } + + if (useArgFile) { + return NativeImageUtils.convertToArgsFile(cliArgs); + } + return Collections.unmodifiableList(cliArgs); + } + + protected void addArtifactToClasspath(Artifact artifact) throws MojoExecutionException { + if (!"jar".equals(artifact.getType())) { + logger.warn("Ignoring non-jar type ImageClasspath Entry " + artifact); + return; + } + + File artifactFile = artifact.getFile(); + if (artifactFile == null) { + throw new MojoExecutionException("Missing jar-file for " + artifact + ". " + + "Ensure that " + plugin.getArtifactId() + " runs in package phase."); + } + + Path jarFilePath = artifactFile.toPath(); + logger.info("ImageClasspath Entry: " + artifact + " (" + jarFilePath.toUri() + ")"); + + warnIfWrongMetaInfLayout(jarFilePath, artifact); + imageClasspath.add(jarFilePath); + } + + protected void warnIfWrongMetaInfLayout(Path jarFilePath, Artifact artifact) throws MojoExecutionException { + if (jarFilePath.toFile().isDirectory()) { + logger.info("Artifact `" + jarFilePath + "` is a directory."); + return; + } + URI jarFileURI = URI.create("jar:" + jarFilePath.toUri()); + try (FileSystem jarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap())) { + Path nativeImageMetaInfBase = jarFS.getPath("/" + NATIVE_IMAGE_META_INF); + if (Files.isDirectory(nativeImageMetaInfBase)) { + try (Stream stream = Files.walk(nativeImageMetaInfBase)) { + List nativeImageProperties = stream + .filter(p -> p.endsWith(NATIVE_IMAGE_PROPERTIES_FILENAME)).collect(Collectors.toList()); + for (Path nativeImageProperty : nativeImageProperties) { + Path relativeSubDir = nativeImageMetaInfBase.relativize(nativeImageProperty).getParent(); + boolean valid = relativeSubDir != null && (relativeSubDir.getNameCount() == 2); + valid = valid && relativeSubDir.getName(0).toString().equals(artifact.getGroupId()); + valid = valid && relativeSubDir.getName(1).toString().equals(artifact.getArtifactId()); + if (!valid) { + String example = NATIVE_IMAGE_META_INF + "/${groupId}/${artifactId}/" + NATIVE_IMAGE_PROPERTIES_FILENAME; + logger.warn(nativeImageProperty.toUri() + " does not match recommended " + example + " layout."); + } + } + } + } + } catch (IOException e) { + throw new MojoExecutionException("Artifact " + artifact + "cannot be added to image classpath", e); + } + } + + protected abstract List getDependencyScopes(); + + protected void addDependenciesToClasspath() throws MojoExecutionException { + configureMetadataRepository(); + for (Artifact dependency : project.getArtifacts().stream() + .filter(artifact -> getDependencyScopes().contains(artifact.getScope())) + .collect(Collectors.toSet())) { + addArtifactToClasspath(dependency); + maybeAddDependencyMetadata(dependency); + } + } + + /** + * Returns path to where application classes are stored, or jar artifact if it is produced. + * @return Path to application classes + */ + protected Path getMainBuildPath() { + if (classesDirectory != null) { + return classesDirectory.toPath(); + } else { + File artifactFile = project.getArtifact().getFile(); + if (artifactFile.getName().toLowerCase().endsWith(".jar")) { + return artifactFile.toPath(); + } else { + return defaultClassesDirectory.toPath(); + } + } + } + + protected void populateApplicationClasspath() throws MojoExecutionException { + imageClasspath.add(getMainBuildPath()); + } + + protected void populateClasspath() throws MojoExecutionException { + if (classpath != null && !classpath.isEmpty()) { + imageClasspath.addAll(classpath.stream() + .map(Paths::get) + .map(Path::toAbsolutePath) + .collect(Collectors.toSet()) + ); + } else { + populateApplicationClasspath(); + addDependenciesToClasspath(); + } + } + + protected String getClasspath() throws MojoExecutionException { + populateClasspath(); + + if (imageClasspath.isEmpty()) { + throw new MojoExecutionException("Image classpath is empty. " + + "Check if your classpath configuration is correct."); + } + + return imageClasspath.stream() + .map(Path::toString) + .collect(Collectors.joining(File.pathSeparator)); + } + + protected void buildImage() throws MojoExecutionException { + Path nativeImageExecutable = Utils.getNativeImage(); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); + processBuilder.command().addAll(getBuildArgs()); + + if (environment != null) { + processBuilder.environment().putAll(environment); + } + + if (!outputDirectory.exists() && !outputDirectory.mkdirs()) { + throw new MojoExecutionException("Failed creating output directory"); + } + processBuilder.directory(outputDirectory); + processBuilder.inheritIO(); + + String commandString = String.join(" ", processBuilder.command()); + logger.info("Executing: " + commandString); + Process imageBuildProcess = processBuilder.start(); + if (imageBuildProcess.waitFor() != 0) { + throw new MojoExecutionException("Execution of " + commandString + " returned non-zero result"); + } + } catch (IOException | InterruptedException e) { + throw new MojoExecutionException("Building image with " + nativeImageExecutable + " failed", e); + } + } + protected void maybeAddGeneratedResourcesConfig(List into) { if (resourcesConfigDirectory.exists() || agentResourceDirectory != null) { File[] dirs = resourcesConfigDirectory.listFiles(); - Stream configDirs = Stream.concat( - dirs == null ? Stream.empty() : Arrays.stream(dirs), - agentResourceDirectory == null ? Stream.empty() : Stream.of(agentResourceDirectory).filter(File::isDirectory) - ); - into.add("-H:ConfigurationFileDirectories=" + - configDirs - .map(File::getAbsolutePath) - .collect(Collectors.joining(","))); + Stream configDirs = + Stream.concat(dirs == null ? Stream.empty() : Arrays.stream(dirs), + agentResourceDirectory == null ? Stream.empty() : Stream.of(agentResourceDirectory).filter(File::isDirectory)); + + into.add("-H:ConfigurationFileDirectories=" + configDirs.map(File::getAbsolutePath).collect(Collectors.joining(","))); if (agentResourceDirectory != null && agentResourceDirectory.isDirectory()) { // The generated reflect config file contains references to java.* // and org.apache.maven.surefire that we'd need to remove using @@ -103,4 +407,109 @@ protected void maybeAddGeneratedResourcesConfig(List into) { } } } + + protected boolean isMetadataRepositoryEnabled() { + return metadataRepositoryConfiguration != null && metadataRepositoryConfiguration.isEnabled(); + } + + protected void configureMetadataRepository() { + if (isMetadataRepositoryEnabled()) { + Path repoPath = null; + if (metadataRepositoryConfiguration.getVersion() != null) { + logger.warn("The official GraalVM reachability metadata repository is not released yet. Only local repositories are supported"); + } + if (metadataRepositoryConfiguration.getLocalPath() != null) { + Path localPath = metadataRepositoryConfiguration.getLocalPath().toPath(); + repoPath = unzipLocalMetadata(localPath); + } else if (metadataRepositoryConfiguration.getUrl() != null) { + Optional download = downloadMetadata(metadataRepositoryConfiguration.getUrl()); + if (download.isPresent()) { + logger.info("Downloaded GraalVM reachability metadata repository from " + metadataRepositoryConfiguration.getUrl()); + repoPath = unzipLocalMetadata(download.get()); + } + } + + if (repoPath == null) { + logger.warn("GraalVM reachability metadata repository is enabled, but no repository has been configured"); + } else { + metadataRepository = new FileSystemRepository(repoPath, new FileSystemRepository.Logger() { + @Override + public void log(String groupId, String artifactId, String version, Supplier message) { + logger.info(String.format("[graalvm reachability metadata repository for %s:%s:%s]: %s", groupId, artifactId, version, message.get())); + } + }); + } + } + } + + public boolean isArtifactExcludedFromMetadataRepository(Artifact dependency) { + if (metadataRepositoryConfiguration == null) { + return false; + } else { + return metadataRepositoryConfiguration.isArtifactExcluded(dependency); + } + } + + protected void maybeAddReachabilityMetadata(List args) { + if (isMetadataRepositoryEnabled() && !metadataRepositoryPaths.isEmpty()) { + String arg = metadataRepositoryPaths.stream() + .map(Path::toAbsolutePath) + .map(Path::toFile) + .map(File::getAbsolutePath) + .collect(Collectors.joining(",")); + + if (!arg.isEmpty()) { + args.add("-H:ConfigurationFileDirectories=" + arg); + } + } + } + + protected void maybeAddDependencyMetadata(Artifact dependency) { + if (isMetadataRepositoryEnabled() && metadataRepository != null && !isArtifactExcludedFromMetadataRepository(dependency)) { + metadataRepositoryPaths.addAll(metadataRepository.findConfigurationDirectoriesFor(q -> { + q.useLatestConfigWhenVersionIsUntested(); + q.forArtifact(artifact -> { + artifact.gav(String.join(":", + dependency.getGroupId(), + dependency.getArtifactId(), + dependency.getVersion())); + getMetadataVersion(dependency).ifPresent(artifact::forceConfigVersion); + }); + })); + } + } + + protected Optional getMetadataVersion(Artifact dependency) { + if (metadataRepositoryConfiguration == null) { + return Optional.empty(); + } else { + return metadataRepositoryConfiguration.getMetadataVersion(dependency); + } + } + + protected Optional downloadMetadata(URL url) { + Path destination = outputDirectory.toPath().resolve("graalvm-reachability-metadata"); + return FileUtils.download(url, destination, logger::error); + } + + protected Path unzipLocalMetadata(Path localPath) { + if (Files.exists(localPath)) { + if (FileUtils.isZip(localPath)) { + Path destination = outputDirectory.toPath().resolve("graalvm-reachability-metadata"); + if (!Files.exists(destination) && !destination.toFile().mkdirs()) { + throw new RuntimeException("Failed creating destination directory"); + } + FileUtils.extract(localPath, destination, logger::error); + return destination; + } else if (Files.isDirectory(localPath)) { + return localPath; + } else { + logger.warn("Unable to extract metadata repository from " + localPath + ". " + + "It needs to be either a ZIP file or an exploded directory"); + } + } else { + logger.error("GraalVM Reachability metadata repository path does not exist: " + localPath); + } + return null; + } } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java index 888ed78f7..6fc293197 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeBuildMojo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -45,83 +45,32 @@ import org.apache.maven.model.ConfigurationContainer; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginExecution; -import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.PluginParameterExpressionEvaluator; -import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.graalvm.buildtools.Utils; -import org.graalvm.buildtools.maven.config.MetadataRepositoryConfiguration; -import org.graalvm.buildtools.utils.FileUtils; -import org.graalvm.buildtools.utils.NativeImageUtils; -import org.graalvm.reachability.JvmReachabilityMetadataRepository; -import org.graalvm.reachability.internal.FileSystemRepository; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Optional; -import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.stream.Collectors; @Mojo(name = "build", defaultPhase = LifecyclePhase.PACKAGE) public class NativeBuildMojo extends AbstractNativeMojo { - private static final String NATIVE_IMAGE_META_INF = "META-INF/native-image"; - private static final String NATIVE_IMAGE_PROPERTIES_FILENAME = "native-image.properties"; - - @Parameter(defaultValue = "${plugin}", readonly = true) // Maven 3 only - private PluginDescriptor plugin; - - @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)// - private File outputDirectory; - - @Parameter(property = "mainClass") - private String mainClass; - - @Parameter(property = "imageName") - private String imageName; - @Parameter(property = "skipNativeBuild", defaultValue = "false") private boolean skip; - @Parameter(defaultValue = "${mojoExecution}") - private MojoExecution mojoExecution; - - @Parameter(property = "classpath") - private List classpath; - - @Parameter(property = "useArgFile", defaultValue = "true") - private boolean useArgFile; - - @Parameter(alias = "metadataRepository") - private MetadataRepositoryConfiguration metadataRepositoryConfiguration; - - private final List imageClasspath; - private PluginParameterExpressionEvaluator evaluator; - private JvmReachabilityMetadataRepository metadataRepository; - - public NativeBuildMojo() { - this.imageClasspath = new ArrayList<>(); + @Override + protected List getDependencyScopes() { + return Arrays.asList(Artifact.SCOPE_COMPILE, + Artifact.SCOPE_RUNTIME, + Artifact.SCOPE_COMPILE_PLUS_RUNTIME + ); } @Override @@ -130,201 +79,13 @@ public void execute() throws MojoExecutionException { getLog().info("Skipping native-image generation (parameter 'skipNativeBuild' is true)."); return; } - evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); - - configureMetadataRepository(); - Set metadataRepositoryPaths = new HashSet<>(); - - imageClasspath.clear(); - if (classpath != null && !classpath.isEmpty()) { - imageClasspath.addAll(classpath.stream().map(Paths::get).collect(Collectors.toList())); - } else { - List imageClasspathScopes = Arrays.asList(Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME); - project.setArtifactFilter(artifact -> imageClasspathScopes.contains(artifact.getScope())); - for (Artifact dependency : project.getArtifacts()) { - addClasspath(dependency); - maybeAddDependencyMetadata(metadataRepositoryPaths, dependency); - } - addClasspath(project.getArtifact(), project.getPackaging()); - } - String classpathStr = imageClasspath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)); - - Path nativeImageExecutable = Utils.getNativeImage(); + evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution); + maybeSetMainClassFromPlugin(this::consumeExecutionsNodeValue, "org.apache.maven.plugins:maven-shade-plugin", "transformers", "transformer", "mainClass"); + maybeSetMainClassFromPlugin(this::consumeConfigurationNodeValue, "org.apache.maven.plugins:maven-assembly-plugin", "archive", "manifest", "mainClass"); + maybeSetMainClassFromPlugin(this::consumeConfigurationNodeValue, "org.apache.maven.plugins:maven-jar-plugin", "archive", "manifest", "mainClass"); maybeAddGeneratedResourcesConfig(buildArgs); - maybeAddReachabilityMetadata(buildArgs, metadataRepositoryPaths); - - try { - List cliArgs = new ArrayList<>(); - cliArgs.add("-cp"); - cliArgs.add(classpathStr); - cliArgs.addAll(getBuildArgs()); - if (useArgFile) { - cliArgs = NativeImageUtils.convertToArgsFile(cliArgs); - } - ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); - processBuilder.command().addAll(cliArgs); - processBuilder.directory(getWorkingDirectory().toFile()); - processBuilder.inheritIO(); - - String commandString = String.join(" ", processBuilder.command()); - getLog().info("Executing: " + commandString); - Process imageBuildProcess = processBuilder.start(); - if (imageBuildProcess.waitFor() != 0) { - throw new MojoExecutionException("Execution of " + commandString + " returned non-zero result"); - } - } catch (IOException | InterruptedException e) { - throw new MojoExecutionException("Building image with " + nativeImageExecutable + " failed", e); - } - } - - private void configureMetadataRepository() { - if (isMetadataRepositoryEnabled()) { - Path repoPath = null; - if (metadataRepositoryConfiguration.getVersion() != null) { - getLog().warn("The official JVM reachability metadata repository is not released yet. Only local repositories are supported"); - } - if (metadataRepositoryConfiguration.getLocalPath() != null) { - Path localPath = metadataRepositoryConfiguration.getLocalPath().toPath(); - repoPath = unzipLocalPath(localPath); - } else if (metadataRepositoryConfiguration.getUrl() != null) { - Optional download = download(metadataRepositoryConfiguration.getUrl()); - if (download.isPresent()) { - getLog().info("Downloaded GraalVM reachability metadata repository from " + metadataRepositoryConfiguration.getUrl()); - repoPath = unzipLocalPath(download.get()); - } - } - - if (repoPath == null) { - getLog().warn("JVM reachability metadata repository is enabled, but no repository has been configured"); - } else { - metadataRepository = new FileSystemRepository(repoPath, new FileSystemRepository.Logger() { - @Override - public void log(String groupId, String artifactId, String version, Supplier message) { - getLog().info(String.format("[jvm reachability metadata repository for %s:%s:%s]: %s", groupId, artifactId, version, message.get())); - } - }); - } - } - } - - private Optional download(URL url) { - Path destination = outputDirectory.toPath().resolve("graalvm-reachability-metadata"); - return FileUtils.download(url, destination, getLog()::error); - } - - private Path unzipLocalPath(Path localPath) { - if (Files.exists(localPath)) { - if (FileUtils.isZip(localPath)) { - Path destination = outputDirectory.toPath().resolve("graalvm-reachability-metadata"); - if (!Files.exists(destination)) { - destination.toFile().mkdirs(); - } - - FileUtils.extract(localPath, destination, getLog()::error); - return destination; - } else if (Files.isDirectory(localPath)) { - return localPath; - } else { - getLog().warn("Unable to extract metadata repository from " + localPath + ". It needs to be either a ZIP file or an exploded directory"); - } - } else { - getLog().error("JVM reachability metadata repository path does not exist: " + localPath); - } - return null; - } - - private void maybeAddDependencyMetadata(Set metadataRepositoryPaths, Artifact dependency) { - if (isMetadataRepositoryEnabled() && metadataRepository != null && !isExcluded(dependency)) { - metadataRepositoryPaths.addAll(metadataRepository.findConfigurationDirectoriesFor(q -> { - q.useLatestConfigWhenVersionIsUntested(); - q.forArtifact(artifact -> { - artifact.gav(String.join(":", dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())); - getMetadataVersion(dependency).ifPresent(artifact::forceConfigVersion); - }); - })); - } - } - - private Optional getMetadataVersion(Artifact dependency) { - if (metadataRepositoryConfiguration == null) { - return Optional.empty(); - } else { - return metadataRepositoryConfiguration.getMetadataVersion(dependency); - } - } - - private boolean isExcluded(Artifact dependency) { - if (metadataRepositoryConfiguration == null) { - return false; - } else { - return metadataRepositoryConfiguration.isArtifactExcluded(dependency); - } - } - - private void maybeAddReachabilityMetadata(List buildArgs, Set paths) { - if (isMetadataRepositoryEnabled() && !paths.isEmpty()) { - String arg = paths.stream() - .map(Path::toAbsolutePath) - .map(Path::toFile) - .map(File::getAbsolutePath) - .collect(Collectors.joining(",")); - - if (!arg.isEmpty()) { - buildArgs.add("-H:ConfigurationFileDirectories=" + arg); - } - } - } - - private boolean isMetadataRepositoryEnabled() { - return metadataRepositoryConfiguration != null && metadataRepositoryConfiguration.isEnabled(); - } - - private void addClasspath(Artifact artifact) throws MojoExecutionException { - addClasspath(artifact, "jar"); - } - - private void addClasspath(Artifact artifact, String artifactType) throws MojoExecutionException { - if (!artifactType.equals(artifact.getType())) { - getLog().warn("Ignoring non-jar type ImageClasspath Entry " + artifact); - return; - } - File artifactFile = artifact.getFile(); - if (artifactFile == null) { - throw new MojoExecutionException("Missing jar-file for " + artifact + ". Ensure that" + plugin.getArtifactId() + " runs in package phase."); - } - Path jarFilePath = artifactFile.toPath(); - getLog().info("ImageClasspath Entry: " + artifact + " (" + jarFilePath.toUri() + ")"); - - URI jarFileURI = URI.create("jar:" + jarFilePath.toUri()); - try (FileSystem jarFS = FileSystems.newFileSystem(jarFileURI, Collections.emptyMap())) { - Path nativeImageMetaInfBase = jarFS.getPath("/" + NATIVE_IMAGE_META_INF); - if (Files.isDirectory(nativeImageMetaInfBase)) { - List nativeImageProperties = Files.walk(nativeImageMetaInfBase) - .filter(p -> p.endsWith(NATIVE_IMAGE_PROPERTIES_FILENAME)) - .collect(Collectors.toList()); - - for (Path nativeImageProperty : nativeImageProperties) { - Path relativeSubDir = nativeImageMetaInfBase.relativize(nativeImageProperty).getParent(); - boolean valid = relativeSubDir != null && (relativeSubDir.getNameCount() == 2); - valid = valid && relativeSubDir.getName(0).toString().equals(artifact.getGroupId()); - valid = valid && relativeSubDir.getName(1).toString().equals(artifact.getArtifactId()); - if (!valid) { - String example = NATIVE_IMAGE_META_INF + "/${groupId}/${artifactId}/" + NATIVE_IMAGE_PROPERTIES_FILENAME; - getLog().warn(nativeImageProperty.toUri() + " does not match recommended " + example + " layout."); - } - } - } - } catch (IOException e) { - throw new MojoExecutionException("Artifact " + artifact + "cannot be added to image classpath", e); - } - - imageClasspath.add(jarFilePath); - } - - private Path getWorkingDirectory() { - outputDirectory.mkdirs(); - return outputDirectory.toPath(); + buildImage(); } private String consumeConfigurationNodeValue(String pluginKey, String... nodeNames) { @@ -371,7 +132,7 @@ private String evaluateValue(String value) { if (evaluatedValue instanceof String) { return (String) evaluatedValue; } - } catch (ExpressionEvaluationException exception) { + } catch (ExpressionEvaluationException ignored) { } } @@ -387,25 +148,4 @@ private void maybeSetMainClassFromPlugin(BiFunction ma } } } - - private List getBuildArgs() { - maybeSetMainClassFromPlugin(this::consumeExecutionsNodeValue, "org.apache.maven.plugins:maven-shade-plugin", "transformers", "transformer", "mainClass"); - maybeSetMainClassFromPlugin(this::consumeConfigurationNodeValue, "org.apache.maven.plugins:maven-assembly-plugin", "archive", "manifest", "mainClass"); - maybeSetMainClassFromPlugin(this::consumeConfigurationNodeValue, "org.apache.maven.plugins:maven-jar-plugin", "archive", "manifest", "mainClass"); - - List list = new ArrayList<>(); - if (buildArgs != null && !buildArgs.isEmpty()) { - for (String buildArg : buildArgs) { - list.addAll(Arrays.asList(buildArg.split("\\s+"))); - } - } - if (mainClass != null && !mainClass.equals(".")) { - list.add("-H:Class=" + mainClass); - } - if (imageName == null) { - imageName = project.getArtifactId(); - } - list.add("-H:Name=" + imageName); - return list; - } } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java index 013ace42f..9d079f2f2 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeTestMojo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -42,18 +42,15 @@ package org.graalvm.buildtools.maven; import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.model.FileSet; import org.apache.maven.model.Plugin; 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.codehaus.plexus.util.xml.Xpp3Dom; import org.graalvm.buildtools.Utils; -import org.graalvm.buildtools.utils.NativeImageUtils; import org.graalvm.junit.platform.JUnitPlatformFeature; import java.io.File; @@ -67,9 +64,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.graalvm.buildtools.Utils.NATIVE_TESTS_EXE; @@ -88,82 +83,102 @@ public class NativeTestMojo extends AbstractNativeMojo { @Parameter(property = "skipNativeTests", defaultValue = "false") private boolean skipNativeTests; - @Parameter(property = "classpath") - private List classpath; - - @Parameter(property = "project.build.directory") - private File buildDirectory; + @Override + protected void populateApplicationClasspath() throws MojoExecutionException { + super.populateApplicationClasspath(); + imageClasspath.add(Paths.get(project.getBuild().getTestOutputDirectory())); + project.getBuild() + .getTestResources() + .stream() + .map(FileSet::getDirectory) + .map(Paths::get) + .forEach(imageClasspath::add); + } - @Parameter(property = "environmentVariables") - private Map environment; + @Override + protected List getDependencyScopes() { + return Arrays.asList( + Artifact.SCOPE_COMPILE, + Artifact.SCOPE_RUNTIME, + Artifact.SCOPE_TEST, + Artifact.SCOPE_COMPILE_PLUS_RUNTIME + ); + } - @Parameter(property = "systemPropertyVariables") - private Map systemProperties; + @Override + protected void addDependenciesToClasspath() throws MojoExecutionException { + super.addDependenciesToClasspath(); + pluginArtifacts.stream() + .filter(it -> it.getGroupId().startsWith(Utils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit")) + .map(it -> it.getFile().toPath()) + .forEach(imageClasspath::add); + findNativePlatformJar().ifPresent(imageClasspath::add); + } @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void execute() throws MojoExecutionException { if (skipTests || skipNativeTests) { logger.info("Skipping native-image tests (parameter 'skipTests' or 'skipNativeTests' is true)."); return; } - configureEnvironment(); if (!hasTests()) { logger.info("Skipped native-image tests since there are no test classes."); return; } - - String classpath = getClassPath(); - Optional nativePlatformJar = findNativePlatformJar(); - if (nativePlatformJar.isPresent()) { - classpath += File.pathSeparator + nativePlatformJar.get().toFile().getAbsolutePath(); + if (!hasTestIds()) { + logger.error("Test configuration file wasn't found. Make sure that test execution wasn't skipped."); + throw new IllegalStateException("Test configuration file wasn't found."); } - Path targetFolder = new File(project.getBuild().getDirectory()).toPath(); - targetFolder.toFile().mkdirs(); logger.info("===================="); logger.info("Initializing project: " + project.getName()); logger.info("===================="); - if (!hasTestIds()) { - logger.error("Test configuration file wasn't found. Make sure that test execution wasn't skipped."); - throw new IllegalStateException("Test configuration file wasn't found."); + configureEnvironment(); + buildArgs.add("--features=org.graalvm.junit.platform.JUnitPlatformFeature"); + + if (systemProperties == null) { + systemProperties = new HashMap<>(); } + systemProperties.put("junit.platform.listeners.uid.tracking.output.dir", + NativeExtension.testIdsDirectory(outputDirectory.getAbsolutePath())); - logger.debug("Classpath: " + classpath); - buildImage(classpath, targetFolder); + imageName = NATIVE_TESTS_EXE; + mainClass = "org.graalvm.junit.platform.NativeImageJUnitLauncher"; - runTests(targetFolder); + buildImage(); + runNativeTests(outputDirectory.toPath().resolve(NATIVE_TESTS_EXE)); } private void configureEnvironment() { - // inherit from surefire mojo - Plugin plugin = project.getPlugin("org.apache.maven.plugins:maven-surefire-plugin"); - if (plugin != null) { - Object configuration = plugin.getConfiguration(); - if (configuration instanceof Xpp3Dom) { - Xpp3Dom dom = (Xpp3Dom) configuration; - Xpp3Dom environmentVariables = dom.getChild("environmentVariables"); - if (environmentVariables != null) { - Xpp3Dom[] children = environmentVariables.getChildren(); - if (environment == null) { - environment = new HashMap<>(children.length); - } - for (Xpp3Dom child : children) { - environment.put(child.getName(), child.getValue()); - } + // inherit from surefire mojo + Plugin plugin = project.getPlugin("org.apache.maven.plugins:maven-surefire-plugin"); + if (plugin != null) { + Object configuration = plugin.getConfiguration(); + if (configuration instanceof Xpp3Dom) { + Xpp3Dom dom = (Xpp3Dom) configuration; + Xpp3Dom environmentVariables = dom.getChild("environmentVariables"); + if (environmentVariables != null) { + Xpp3Dom[] children = environmentVariables.getChildren(); + if (environment == null) { + environment = new HashMap<>(children.length); } - Xpp3Dom systemProps = dom.getChild("systemPropertyVariables"); - if (systemProps != null) { - Xpp3Dom[] children = systemProps.getChildren(); - if (systemProperties == null) { - systemProperties = new HashMap<>(children.length); - } - for (Xpp3Dom child : children) { - systemProperties.put(child.getName(), child.getValue()); - } + for (Xpp3Dom child : children) { + environment.put(child.getName(), child.getValue()); + } + } + Xpp3Dom systemProps = dom.getChild("systemPropertyVariables"); + if (systemProps != null) { + Xpp3Dom[] children = systemProps.getChildren(); + if (systemProperties == null) { + systemProperties = new HashMap<>(children.length); + } + for (Xpp3Dom child : children) { + systemProperties.put(child.getName(), child.getValue()); } } } + } } private boolean hasTests() { @@ -178,53 +193,22 @@ private boolean hasTests() { return false; } - private void buildImage(String classpath, Path targetFolder) throws MojoExecutionException { - Path nativeImageExecutable = Utils.getNativeImage(); - - List command = new ArrayList<>(Arrays.asList( - "-cp", classpath, - "--features=org.graalvm.junit.platform.JUnitPlatformFeature", - "-Djunit.platform.listeners.uid.tracking.output.dir=" + NativeExtension.testIdsDirectory(buildDirectory.getAbsolutePath()), - "-H:Path=" + targetFolder.toAbsolutePath(), - "-H:Name=" + NATIVE_TESTS_EXE)); - maybeAddGeneratedResourcesConfig(command); - - if (buildArgs != null) { - command.addAll(buildArgs); + private void runNativeTests(Path executable) throws MojoExecutionException { + Path xmlLocation = outputDirectory.toPath().resolve("native-test-reports"); + if (!xmlLocation.toFile().exists() && !xmlLocation.toFile().mkdirs()) { + throw new MojoExecutionException("Failed creating xml output directory"); } - try { - ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString()); - prepareVariables(processBuilder, command); - command = NativeImageUtils.convertToArgsFile(command); - processBuilder.command().addAll(command); - processBuilder.command().add("org.graalvm.junit.platform.NativeImageJUnitLauncher"); - processBuilder.directory(new File(project.getBuild().getDirectory())); + ProcessBuilder processBuilder = new ProcessBuilder(executable.toAbsolutePath().toString()); processBuilder.inheritIO(); - String commandString = String.join(" ", processBuilder.command()); - getLog().info("Executing: " + commandString); - Process imageBuildProcess = processBuilder.start(); - if (imageBuildProcess.waitFor() != 0) { - throw new MojoExecutionException("Execution of " + commandString + " returned non-zero result"); - } - } catch (IOException | InterruptedException e) { - throw new MojoExecutionException("Building image with " + nativeImageExecutable + " failed", e); - } - } - - private void runTests(Path targetFolder) throws MojoExecutionException { - Path xmlLocation = targetFolder.resolve("native-test-reports"); - xmlLocation.toFile().mkdirs(); - try { - ProcessBuilder processBuilder = new ProcessBuilder( - targetFolder.resolve(NATIVE_TESTS_EXE).toAbsolutePath().toString()); - processBuilder.inheritIO(); List command = new ArrayList<>(); - prepareVariables(processBuilder, command); command.add("--xml-output-dir"); command.add(xmlLocation.toString()); + systemProperties.forEach((key, value) -> command.add("-D" + key + "=" + value)); processBuilder.command().addAll(command); + processBuilder.environment().putAll(environment); + String commandString = String.join(" ", processBuilder.command()); getLog().info("Executing: " + commandString); Process imageBuildProcess = processBuilder.start(); @@ -236,48 +220,6 @@ private void runTests(Path targetFolder) throws MojoExecutionException { } } - private void prepareVariables(ProcessBuilder processBuilder, List command) { - if (environment != null) { - processBuilder.environment().putAll(environment); - } - if (systemProperties != null) { - for (Map.Entry entry : systemProperties.entrySet()) { - command.add("-D" + entry.getKey() + "=" + entry.getValue()); - } - } - } - - private String getClassPath() throws MojoFailureException { - if (classpath != null && !classpath.isEmpty()) { - return String.join(File.pathSeparator, classpath); - } - try { - List pluginDependencies = pluginArtifacts.stream() - .filter(it -> it.getGroupId().startsWith(Utils.MAVEN_GROUP_ID) || it.getGroupId().startsWith("org.junit")) - .collect(Collectors.toList()); - - List projectClassPath = new ArrayList<>(project - .getTestClasspathElements()); - - Stream allResources = Stream.concat( - project.getBuild() - .getResources() - .stream() - .map(FileSet::getDirectory), - project.getBuild() - .getTestResources() - .stream() - .map(FileSet::getDirectory) - ); - - return Stream.concat(Stream.concat(projectClassPath.stream(), allResources), pluginDependencies.stream() - .map(it -> it.getFile().toString())) - .collect(Collectors.joining(File.pathSeparator)); - } catch (DependencyResolutionRequiredException e) { - throw new MojoFailureException(e.getMessage(), e); - } - } - private boolean hasTestIds() { try { Path buildDir = Paths.get(project.getBuild().getDirectory()); @@ -288,6 +230,7 @@ private boolean hasTestIds() { } } + @SuppressWarnings("SameParameterValue") private Stream readAllFiles(Path dir, String prefix) throws IOException { return findFiles(dir, prefix).map(outputFile -> { try { @@ -307,7 +250,6 @@ private static Stream findFiles(Path dir, String prefix) throws IOExceptio && path.getFileName().toString().startsWith(prefix))); } - private static Optional findNativePlatformJar() { try { return Optional.of(new File(JUnitPlatformFeature.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath()); @@ -315,5 +257,4 @@ private static Optional findNativePlatformJar() { return Optional.empty(); } } - } diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/config/ExcludeConfigConfiguration.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/config/ExcludeConfigConfiguration.java new file mode 100644 index 000000000..c41d263b1 --- /dev/null +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/config/ExcludeConfigConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.buildtools.maven.config; + +import org.apache.maven.plugins.annotations.Parameter; + +public class ExcludeConfigConfiguration { + @Parameter + private String jarPath; + @Parameter + private String resourcePattern; + + public String getJarPath() { + return jarPath; + } + + public void setJarPath(String jarPath) { + this.jarPath = jarPath; + } + + public String getResourcePattern() { + return resourcePattern; + } + + public void setResourcePattern(String resourcePattern) { + this.resourcePattern = resourcePattern; + } +}