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

org.openjfx.JavaFXBaseMojo Maven / Gradle / Ivy

/*
 * Copyright 2019 Gluon
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openjfx;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.OS;
import org.apache.commons.exec.ProcessDestroyer;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
import org.codehaus.plexus.languages.java.jpms.LocationManager;
import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;

abstract class JavaFXBaseMojo extends AbstractMojo {

    @Parameter(defaultValue = "${project}", readonly = true)
    MavenProject project;

    @Parameter(defaultValue = "${session}", readonly = true)
    private MavenSession session;

    @Component
    private BuildPluginManager pluginManager;

    @Component
    private LocationManager locationManager;

    @Parameter(property = "javafx.mainClass", required = true)
    String mainClass;

    /**
     * Skip the execution.
     */
    @Parameter(property = "javafx.skip", defaultValue = "false")
    boolean skip;

    @Parameter(readonly = true, required = true, defaultValue = "${basedir}")
    File basedir;

    @Parameter(readonly = true, required = true, defaultValue = "${project.build.directory}")
    File builddir;

    /**
     * The current working directory. Optional. If not specified, basedir will be used.
     */
    @Parameter(property = "javafx.workingDirectory")
    File workingDirectory;

    @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true)
    private List compilePath;

    @Parameter(property = "javafx.outputFile")
    File outputFile;

    /**
     * If set to true the child process executes asynchronously and build execution continues in parallel.
     */
    @Parameter(property = "javafx.async", defaultValue = "false")
    private boolean async;

    /**
     * If set to true, the asynchronous child process is destroyed upon JVM shutdown. If set to false, asynchronous
     * child process continues execution after JVM shutdown. Applies only to asynchronous processes; ignored for
     * synchronous processes.
     */
    @Parameter(property = "javafx.asyncDestroyOnShutdown", defaultValue = "true")
    private boolean asyncDestroyOnShutdown;

    /**
     * 

* A list of vm options passed to the {@code executable}. *

* */ @Parameter List options; /** * Arguments separated by space for the executed program. For example: "-j 20" */ @Parameter(property = "javafx.args") String commandlineArgs; /** *

The -source argument for the Java compiler.

*/ @Parameter(property = "javafx.source", defaultValue = "11") private String source; /** *

The -target argument for the Java compiler.

*/ @Parameter(property = "javafx.target", defaultValue = "11") private String target; /** * The -release argument for the Java compiler */ @Parameter(property = "javafx.release", defaultValue = "11") private String release; List classpathElements; List modulepathElements; Map pathElements; JavaModuleDescriptor moduleDescriptor; private ProcessDestroyer processDestroyer; void preparePaths() throws MojoExecutionException, MojoFailureException { if (project == null) { return; } String outputDirectory = project.getBuild().getOutputDirectory(); if (outputDirectory == null || outputDirectory.isEmpty()) { throw new MojoExecutionException("Output directory doesn't exist, compile first"); } File[] classes = new File(outputDirectory).listFiles(); if (classes == null || classes.length == 0) { getLog().debug("Output directory was empty, compiling..."); compile(); classes = new File(outputDirectory).listFiles(); if (classes == null || classes.length == 0) { throw new MojoExecutionException("Output directory is empty, compile first"); } } else { // TODO: verify if classes require compiling } File moduleDescriptorPath = Stream .of(classes) .filter(file -> "module-info.class".equals(file.getName())) .findFirst() .orElse(null); modulepathElements = new ArrayList<>(compilePath.size()); classpathElements = new ArrayList<>(compilePath.size()); pathElements = new LinkedHashMap<>(compilePath.size()); try { Collection dependencyArtifacts = getCompileClasspathElements(project); getLog().debug("Total dependencyArtifacts: " + dependencyArtifacts.size()); ResolvePathsRequest fileResolvePathsRequest = ResolvePathsRequest.ofFiles(dependencyArtifacts); ResolvePathsResult resolvePathsResult; if (moduleDescriptorPath != null) { getLog().debug("module descriptor: " + moduleDescriptorPath); fileResolvePathsRequest = fileResolvePathsRequest.setMainModuleDescriptor(moduleDescriptorPath); } resolvePathsResult = locationManager.resolvePaths(fileResolvePathsRequest); for (Map.Entry pathException : resolvePathsResult.getPathExceptions().entrySet()) { Throwable cause = pathException.getValue(); while (cause.getCause() != null) { cause = cause.getCause(); } String fileName = pathException.getKey().getName(); getLog().warn("Can't extract module name from " + fileName + ": " + cause.getMessage()); } if (moduleDescriptorPath != null) { moduleDescriptor = resolvePathsResult.getMainModuleDescriptor(); } for (Map.Entry entry : resolvePathsResult.getModulepathElements().entrySet()) { if (ModuleNameSource.FILENAME.equals(entry.getValue())) { final String message = "Required filename-based automodules detected. " + "Please don't publish this project to a public artifact repository!"; if (moduleDescriptor != null && moduleDescriptor.exports().isEmpty()) { // application getLog().info(message); } else { // library getLog().warn(message); } break; } } getLog().debug("pathElements: " + resolvePathsResult.getPathElements().size()); resolvePathsResult.getPathElements().entrySet() .forEach(entry -> pathElements.put(entry.getKey().getPath(), entry.getValue())); getLog().debug("classpathElements: " + resolvePathsResult.getClasspathElements().size()); resolvePathsResult.getClasspathElements() .forEach(file -> classpathElements.add(file.getPath())); getLog().debug("modulepathElements: " + resolvePathsResult.getModulepathElements().size()); resolvePathsResult.getModulepathElements().keySet() .forEach(file -> modulepathElements.add(file.getPath())); if (moduleDescriptorPath == null) { pathElements.forEach((k, v) -> { if (v != null && v.name() != null) { modulepathElements.add(k); } else { classpathElements.add(k); } }); } } catch (Exception e) { getLog().warn(e.getMessage()); } getLog().debug("Classpath:" + classpathElements.size()); classpathElements.forEach(s -> getLog().debug(" " + s)); getLog().debug("Modulepath: " + modulepathElements.size()); modulepathElements.forEach(s -> getLog().debug(" " + s)); getLog().debug("pathElements: " + pathElements.size()); pathElements.forEach((k, v) -> getLog().debug(" " + k + " :: " + (v != null && v.name() != null ? v.name() : v))); } private List getCompileClasspathElements(MavenProject project) { List list = project.getArtifacts().stream() .sorted((a1, a2) -> { int compare = a1.compareTo(a2); if (compare == 0) { // give precedence to classifiers return a1.hasClassifier() ? 1 : (a2.hasClassifier() ? -1 : 0); } return compare; }) .map(Artifact::getFile) .collect(Collectors.toList()); list.add(0, new File(project.getBuild().getOutputDirectory())); return list; } void compile() throws MojoExecutionException { Compile.compile(project, session, pluginManager, source, target, release); } void handleWorkingDirectory() throws MojoExecutionException { if (workingDirectory == null) { workingDirectory = basedir; } if (!workingDirectory.exists()) { getLog().debug("Making working directory '" + workingDirectory.getAbsolutePath() + "'."); if (!workingDirectory.mkdirs()) { throw new MojoExecutionException("Could not make working directory: '" + workingDirectory.getAbsolutePath() + "'"); } } } Map handleSystemEnvVariables() { Map enviro = new HashMap<>(); try { Properties systemEnvVars = CommandLineUtils.getSystemEnvVars(); for (Map.Entry entry : systemEnvVars.entrySet()) { enviro.put((String) entry.getKey(), (String) entry.getValue()); } } catch (IOException x) { getLog().error("Could not assign default system environment variables.", x); } return enviro; } CommandLine getExecutablePath(String executable, Map enviro, File dir) { File execFile = new File(executable); String exec = null; if (execFile.isFile()) { getLog().debug("Toolchains are ignored, 'executable' parameter is set to " + executable); exec = execFile.getAbsolutePath(); } if (exec == null && OS.isFamilyWindows()) { List paths = this.getExecutablePaths(enviro); paths.add(0, dir.getAbsolutePath()); exec = findExecutable(executable, paths); } if (exec == null) { String javaHome = System.getProperty("java.home", getJavaHome(enviro)); if (javaHome != null && ! javaHome.isEmpty()) { exec = findExecutable(executable, Arrays.asList(javaHome.concat(File.separator).concat("bin"))); } } if (exec == null) { exec = executable; } CommandLine toRet; if (OS.isFamilyWindows() && !hasNativeExtension(exec) && hasExecutableExtension(exec) ) { // run the windows batch script in isolation and exit at the end final String comSpec = System.getenv( "ComSpec" ); toRet = new CommandLine( comSpec == null ? "cmd" : comSpec ); toRet.addArgument( "/c" ); toRet.addArgument( exec ); } else { toRet = new CommandLine(exec); } getLog().debug("Executable " + toRet.toString()); return toRet; } private static String findExecutable(final String executable, final List paths) { File f = null; search: for (final String path : paths) { f = new File(path, executable); if (!OS.isFamilyWindows() && f.isFile()) { break; } else { for (final String extension : getExecutableExtensions()) { f = new File(path, executable + extension); if (f.isFile()) { break search; } } } } if (f == null || !f.exists()) { return null; } return f.getAbsolutePath(); } private static boolean hasNativeExtension(final String exec) { final String lowerCase = exec.toLowerCase(); return lowerCase.endsWith(".exe") || lowerCase.endsWith(".com"); } private static boolean hasExecutableExtension(final String exec) { final String lowerCase = exec.toLowerCase(); for (final String ext : getExecutableExtensions()) { if (lowerCase.endsWith(ext)) { return true; } } return false; } private static List getExecutableExtensions() { final String pathExt = System.getenv("PATHEXT"); return pathExt == null ? Arrays.asList(".bat", ".cmd") : Arrays.asList(StringUtils.split(pathExt.toLowerCase(), File.pathSeparator)); } private List getExecutablePaths(Map enviro) { List paths = new ArrayList<>(); paths.add(""); String path = enviro.get("PATH"); if (path != null) { paths.addAll(Arrays.asList(StringUtils.split(path, File.pathSeparator))); } return paths; } private String getJavaHome(Map enviro) { return enviro.get("JAVA_HOME"); } int executeCommandLine(Executor exec, CommandLine commandLine, Map enviro, OutputStream out, OutputStream err) throws ExecuteException, IOException { // note: don't use BufferedOutputStream here since it delays the outputs MEXEC-138 PumpStreamHandler psh = new PumpStreamHandler(out, err, System.in); return executeCommandLine(exec, commandLine, enviro, psh); } int executeCommandLine(Executor exec, CommandLine commandLine, Map enviro, FileOutputStream outputFile) throws ExecuteException, IOException { BufferedOutputStream bos = new BufferedOutputStream(outputFile); PumpStreamHandler psh = new PumpStreamHandler(bos); return executeCommandLine(exec, commandLine, enviro, psh); } private int executeCommandLine(Executor exec, final CommandLine commandLine, Map enviro, final PumpStreamHandler psh) throws ExecuteException, IOException { exec.setStreamHandler(psh); int result; try { psh.start(); if (async) { if (asyncDestroyOnShutdown) { exec.setProcessDestroyer(getProcessDestroyer()); } exec.execute(commandLine, enviro, new ExecuteResultHandler() { public void onProcessFailed(ExecuteException e) { getLog().error("Async process failed for: " + commandLine, e); } public void onProcessComplete(int exitValue) { getLog().debug("Async process complete, exit value = " + exitValue + " for: " + commandLine); try { psh.stop(); } catch (IOException e) { getLog().error("Error stopping async process stream handler for: " + commandLine, e); } } }); result = 0; } else { result = exec.execute(commandLine, enviro); } } finally { if (!async) { psh.stop(); } } return result; } private ProcessDestroyer getProcessDestroyer() { if (processDestroyer == null) { processDestroyer = new ShutdownHookProcessDestroyer(); } return processDestroyer; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy