All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graalvm.buildtools.maven.NativeExtension Maven / Gradle / Ivy

/*
 * Copyright (c) 2020, 2021 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.AbstractMavenLifecycleParticipant;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.graalvm.buildtools.agent.AgentConfiguration;
import org.graalvm.buildtools.utils.AgentUtils;
import org.graalvm.buildtools.utils.SharedConstants;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static org.graalvm.buildtools.utils.NativeImageConfigurationUtils.getNativeImage;

/**
 * This extension is responsible for configuring the Surefire plugin to enable
 * the JUnit Platform test listener and registering the native dependency transparently.
 */
@Component(role = AbstractMavenLifecycleParticipant.class, hint = "native-build-tools")
public class NativeExtension extends AbstractMavenLifecycleParticipant implements LogEnabled {

    private static final String JUNIT_PLATFORM_LISTENERS_UID_TRACKING_ENABLED = "junit.platform.listeners.uid.tracking.enabled";
    private static final String JUNIT_PLATFORM_LISTENERS_UID_TRACKING_OUTPUT_DIR = "junit.platform.listeners.uid.tracking.output.dir";
    private static final String NATIVEIMAGE_IMAGECODE = "org.graalvm.nativeimage.imagecode";

    private static Logger logger;

    @Override
    public void enableLogging(Logger logger) {
        this.logger = logger;
    }

    /**
     * Enumeration of execution contexts.
     * 

Enum constants are intentionally lowercase for use as directory names * and within the Maven POM as values of the {@code name} attribute in * {@code }. */ enum Context {main, test} static String testIdsDirectory(String baseDir) { return baseDir + File.separator + "test-ids"; } static String buildAgentArgument(String baseDir, Context context, List agentOptions) { List options = new ArrayList<>(agentOptions); String effectiveOutputDir = agentOutputDirectoryFor(baseDir, context); if (context == Context.test) { // We need to patch the config dir IF, and only IF, we are running tests, because // test execution can be forked into a separate process and there's a race condition. // We have to special case testing here instead of using a generic strategy THEN // invoke the merging tool, because there's no way in Maven to do something as easy // as finalizing a goal (that is, let me do the merge AFTER you're done executing tests, // or invoking exec, or whatever, because what I need to do actually participates into // the same unit of work !). effectiveOutputDir = effectiveOutputDir + File.separator + SharedConstants.AGENT_SESSION_SUBDIR; } String finalEffectiveOutputDir = effectiveOutputDir; options = options.stream().map(option -> option.contains("{output_dir}") ? option.replace("{output_dir}", finalEffectiveOutputDir) : option).collect(Collectors.toList()); return "-agentlib:native-image-agent=" + String.join(",", options); } static String agentOutputDirectoryFor(String baseDir, Context context) { return (baseDir + "/native/agent-output/" + context).replace('/', File.separatorChar); } @Override public void afterProjectsRead(MavenSession session) { for (MavenProject project : session.getProjects()) { Build build = project.getBuild(); withPlugin(build, "native-maven-plugin", nativePlugin -> { String target = build.getDirectory(); String testIdsDir = testIdsDirectory(target); Xpp3Dom configurationRoot = (Xpp3Dom) nativePlugin.getConfiguration(); AgentConfiguration agent; try { agent = AgentUtils.collectAgentProperties(session, configurationRoot); } catch (Exception e) { throw new RuntimeException(e); } // Test configuration withPlugin(build, "maven-surefire-plugin", surefirePlugin -> { configureJunitListener(surefirePlugin, testIdsDir); if (agent.isEnabled()) { List agentOptions = agent.getAgentCommandLine(); configureAgentForSurefire(surefirePlugin, buildAgentArgument(target, Context.test, agentOptions)); } }); // Main configuration if (agent.isEnabled()) { withPlugin(build, "exec-maven-plugin", execPlugin -> updatePluginConfiguration(execPlugin, (exec, config) -> { if ("java-agent".equals(exec.getId())) { Xpp3Dom commandlineArgs = findOrAppend(config, "arguments"); Xpp3Dom[] arrayOfChildren = commandlineArgs.getChildren(); for (int i = 0; i < arrayOfChildren.length; i++) { commandlineArgs.removeChild(0); } List children = new ArrayList<>(); Collections.addAll(children, arrayOfChildren); // Agent argument Xpp3Dom arg = new Xpp3Dom("argument"); List agentOptions = agent.getAgentCommandLine(); arg.setValue(buildAgentArgument(target, Context.main, agentOptions)); children.add(0, arg); // System property for org.graalvm.nativeimage.imagecode arg = new Xpp3Dom("argument"); arg.setValue("-D" + NATIVEIMAGE_IMAGECODE + "=agent"); children.add(1, arg); for (Xpp3Dom child : children) { commandlineArgs.addChild(child); } findOrAppend(config, "executable").setValue(getGraalvmJava()); } }) ); updatePluginConfiguration(nativePlugin, (exec, configuration) -> { Context context = exec.getGoals().stream().anyMatch("test"::equals) ? Context.test : Context.main; Xpp3Dom agentResourceDirectory = findOrAppend(configuration, "agentResourceDirectory"); agentResourceDirectory.setValue(agentOutputDirectoryFor(target, context)); setupMergeAgentFiles(exec, configuration, context); }); } }); } } private static void setupMergeAgentFiles(PluginExecution exec, Xpp3Dom configuration, Context context) { List goals = new ArrayList<>(); goals.add("merge-agent-files"); goals.addAll(exec.getGoals()); exec.setGoals(goals); Xpp3Dom agentContext = findOrAppend(configuration, "context"); agentContext.setValue(context.name()); } private static void withPlugin(Build build, String artifactId, Consumer consumer) { build.getPlugins() .stream() .filter(p -> artifactId.equals(p.getArtifactId())) .findFirst() .ifPresent(consumer); } private static void configureAgentForSurefire(Plugin surefirePlugin, String agentArgument) { updatePluginConfiguration(surefirePlugin, (exec, configuration) -> { Xpp3Dom systemProperties = findOrAppend(configuration, "systemProperties"); Xpp3Dom agent = findOrAppend(systemProperties, NATIVEIMAGE_IMAGECODE); agent.setValue("agent"); Xpp3Dom argLine = new Xpp3Dom("argLine"); argLine.setValue(agentArgument); configuration.addChild(argLine); findOrAppend(configuration, "jvm").setValue(getGraalvmJava()); }); } private static void configureJunitListener(Plugin surefirePlugin, String testIdsDir) { updatePluginConfiguration(surefirePlugin, (exec, configuration) -> { Xpp3Dom systemProperties = findOrAppend(configuration, "systemProperties"); Xpp3Dom junitTracking = findOrAppend(systemProperties, JUNIT_PLATFORM_LISTENERS_UID_TRACKING_ENABLED); Xpp3Dom testIdsProperty = findOrAppend(systemProperties, JUNIT_PLATFORM_LISTENERS_UID_TRACKING_OUTPUT_DIR); junitTracking.setValue("true"); testIdsProperty.setValue(testIdsDir); }); } private static void updatePluginConfiguration(Plugin plugin, BiConsumer consumer) { plugin.getExecutions().forEach(exec -> { Xpp3Dom configuration = configurationBlockOf(exec); consumer.accept(exec, configuration); }); } private static Xpp3Dom configurationBlockOf(PluginExecution exec) { Xpp3Dom configuration = (Xpp3Dom) exec.getConfiguration(); if (configuration == null) { configuration = new Xpp3Dom("configuration"); exec.setConfiguration(configuration); } return configuration; } private static Xpp3Dom findOrAppend(Xpp3Dom parent, String childName) { Xpp3Dom child = parent.getChild(childName); if (child == null) { child = new Xpp3Dom(childName); parent.addChild(child); } return child; } private static String getGraalvmJava() { try { return getNativeImage(logger).getParent().resolve("java").toString(); } catch (MojoExecutionException e) { throw new RuntimeException(e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy