Skip to content

Commit

Permalink
Maven plugin fixes
Browse files Browse the repository at this point in the history
Allow running native:build from the command-line #273
NullPointerException fix #270
  • Loading branch information
lazar-mitrovic committed Jul 24, 2022
1 parent 89e8f62 commit f4da4d5
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 311 deletions.
2 changes: 1 addition & 1 deletion native-maven-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,5 @@ tasks {
}

tasks.withType<Checkstyle>().configureEach {
setConfigFile(layout.projectDirectory.dir("../config/checkstyle.xml").asFile)
configFile = layout.projectDirectory.dir("../config/checkstyle.xml").asFile
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ protected List<String> getBuildArgs() throws MojoExecutionException {

protected Path processArtifact(Artifact artifact, String artifactType) throws MojoExecutionException {
File artifactFile = artifact.getFile();

if (artifactFile == null) {
logger.debug("Missing artifact file for artifact " + artifact + " (type: " + artifact.getType() + ")");
return null;
}

if (!artifactType.equals(artifact.getType())) {
logger.warn("Ignoring non-jar type ImageClasspath Entry " + artifact);
return null;
Expand All @@ -288,7 +294,7 @@ protected Path processArtifact(Artifact artifact, String artifactType) throws Mo
}

Path jarFilePath = artifactFile.toPath();
logger.info("ImageClasspath Entry: " + artifact + " (" + jarFilePath.toUri() + ")");
logger.debug("ImageClasspath Entry: " + artifact + " (" + jarFilePath.toUri() + ")");

warnIfWrongMetaInfLayout(jarFilePath, artifact);
return jarFilePath;
Expand All @@ -300,7 +306,7 @@ protected void addArtifactToClasspath(Artifact artifact) throws MojoExecutionExc

protected void warnIfWrongMetaInfLayout(Path jarFilePath, Artifact artifact) throws MojoExecutionException {
if (jarFilePath.toFile().isDirectory()) {
logger.warn("Artifact `" + jarFilePath + "` is a directory.");
logger.debug("Artifact `" + jarFilePath + "` is a directory.");
return;
}
URI jarFileURI = URI.create("jar:" + jarFilePath.toUri());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,114 +41,21 @@

package org.graalvm.buildtools.maven;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.ConfigurationContainer;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugins.annotations.Execute;
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.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;

/**
* This goal builds and runs native images.
* It can be invoked using `mvn native:build`.
*/
@Mojo(name = "build", defaultPhase = LifecyclePhase.PACKAGE,
requiresDependencyResolution = ResolutionScope.RUNTIME,
requiresDependencyCollection = ResolutionScope.RUNTIME)
public class NativeBuildMojo extends AbstractNativeMojo {

@Parameter(property = "skipNativeBuild", defaultValue = "false")
private boolean skip;

private PluginParameterExpressionEvaluator evaluator;

@Override
protected List<String> getDependencyScopes() {
return Arrays.asList(Artifact.SCOPE_COMPILE,
Artifact.SCOPE_RUNTIME,
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
);
}

@Override
public void execute() throws MojoExecutionException {
if (skip) {
getLog().info("Skipping native-image generation (parameter 'skipNativeBuild' is true).");
return;
}

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);
buildImage();
}

private String consumeConfigurationNodeValue(String pluginKey, String... nodeNames) {
Plugin selectedPlugin = project.getPlugin(pluginKey);
if (selectedPlugin == null) {
return null;
}
return getConfigurationNodeValue(selectedPlugin, nodeNames);
}

private String consumeExecutionsNodeValue(String pluginKey, String... nodeNames) {
Plugin selectedPlugin = project.getPlugin(pluginKey);
if (selectedPlugin == null) {
return null;
}
for (PluginExecution execution : selectedPlugin.getExecutions()) {
String value = getConfigurationNodeValue(execution, nodeNames);
if (value != null) {
return value;
}
}
return null;
}

private String getConfigurationNodeValue(ConfigurationContainer container, String... nodeNames) {
if (container != null && container.getConfiguration() instanceof Xpp3Dom) {
Xpp3Dom node = (Xpp3Dom) container.getConfiguration();
for (String nodeName : nodeNames) {
node = node.getChild(nodeName);
if (node == null) {
return null;
}
}
String value = node.getValue();
return evaluateValue(value);
}
return null;
}

private String evaluateValue(String value) {
if (value != null) {
try {
Object evaluatedValue = evaluator.evaluate(value);
if (evaluatedValue instanceof String) {
return (String) evaluatedValue;
}
} catch (ExpressionEvaluationException ignored) {
}
}

return null;
}

private void maybeSetMainClassFromPlugin(BiFunction<String, String[], String> mainClassProvider, String pluginName, String... nodeNames) {
if (mainClass == null) {
mainClass = mainClassProvider.apply(pluginName, nodeNames);

if (mainClass != null) {
getLog().info("Obtained main class from plugin " + pluginName + " with the following path: " + String.join(" -> ", nodeNames));
}
}
}
@Execute(phase = LifecyclePhase.COMPILE)
@SuppressWarnings("unused")
public class NativeBuildMojo extends NativeBuildNoForkMojo {
// no-op
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* 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
*
* 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;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.ConfigurationContainer;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
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.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;

import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;


/**
* This goal runs native tests. It functions the same as the native:build goal, but it
* does not fork the build, so it is suitable for attaching to the build lifecycle.
*/
@Mojo(name = "build-no-fork", defaultPhase = LifecyclePhase.PACKAGE,
requiresDependencyResolution = ResolutionScope.RUNTIME,
requiresDependencyCollection = ResolutionScope.RUNTIME)
public class NativeBuildNoForkMojo extends AbstractNativeMojo {

@Parameter(property = "skipNativeBuild", defaultValue = "false")
private boolean skip;

private PluginParameterExpressionEvaluator evaluator;

@Override
protected List<String> getDependencyScopes() {
return Arrays.asList(Artifact.SCOPE_COMPILE,
Artifact.SCOPE_RUNTIME,
Artifact.SCOPE_COMPILE_PLUS_RUNTIME
);
}

@Override
public void execute() throws MojoExecutionException {
if (skip) {
logger.info("Skipping native-image generation (parameter 'skipNativeBuild' is true).");
return;
}

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);
buildImage();
}

private String consumeConfigurationNodeValue(String pluginKey, String... nodeNames) {
Plugin selectedPlugin = project.getPlugin(pluginKey);
if (selectedPlugin == null) {
return null;
}
return getConfigurationNodeValue(selectedPlugin, nodeNames);
}

private String consumeExecutionsNodeValue(String pluginKey, String... nodeNames) {
Plugin selectedPlugin = project.getPlugin(pluginKey);
if (selectedPlugin == null) {
return null;
}
for (PluginExecution execution : selectedPlugin.getExecutions()) {
String value = getConfigurationNodeValue(execution, nodeNames);
if (value != null) {
return value;
}
}
return null;
}

private String getConfigurationNodeValue(ConfigurationContainer container, String... nodeNames) {
if (container != null && container.getConfiguration() instanceof Xpp3Dom) {
Xpp3Dom node = (Xpp3Dom) container.getConfiguration();
for (String nodeName : nodeNames) {
node = node.getChild(nodeName);
if (node == null) {
return null;
}
}
String value = node.getValue();
return evaluateValue(value);
}
return null;
}

private String evaluateValue(String value) {
if (value != null) {
try {
Object evaluatedValue = evaluator.evaluate(value);
if (evaluatedValue instanceof String) {
return (String) evaluatedValue;
}
} catch (ExpressionEvaluationException ignored) {
}
}

return null;
}

private void maybeSetMainClassFromPlugin(BiFunction<String, String[], String> mainClassProvider, String pluginName, String... nodeNames) {
if (mainClass == null) {
mainClass = mainClassProvider.apply(pluginName, nodeNames);

if (mainClass != null) {
logger.info("Obtained main class from plugin " + pluginName + " with the following path: " + String.join(" -> ", nodeNames));
}
}
}
}
Loading

0 comments on commit f4da4d5

Please sign in to comment.