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

org.openjfx.JavaFXJLinkMojo Maven / Gradle / Ivy

There is a newer version: 0.0.8
Show newest version
/*
 * 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.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.OS;
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.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.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.zip.ZipArchiver;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Mojo(name = "jlink", requiresDependencyResolution = ResolutionScope.RUNTIME)
@Execute(phase = LifecyclePhase.PROCESS_CLASSES)
public class JavaFXJLinkMojo extends JavaFXBaseMojo {

    private static final Pattern JLINK_VERSION_PATTERN = Pattern.compile("(1[3-9]|[2-9][0-9]|\\d{3,})");

    /**
     * Strips debug information out, equivalent to -G, --strip-debug,
     * default false
     */
    @Parameter(property = "javafx.stripDebug", defaultValue = "false")
    private boolean stripDebug;

    /**
     * Strip Java debug attributes out, equivalent to --strip-java-debug-attributes,
     * default false
     */
    @Parameter(property = "javafx.stripJavaDebugAttributes", defaultValue = "false")
    private boolean stripJavaDebugAttributes;

    /**
     * Compression level of the resources being used, equivalent to:
     * -c, --compress=level. Valid values: 0, 1, 2,
     * default 0
     */
    @Parameter(property = "javafx.compress", defaultValue = "0")
    private Integer compress;

    /**
     * Remove the includes directory in the resulting runtime image,
     * equivalent to: --no-header-files, default false
     */
    @Parameter(property = "javafx.noHeaderFiles", defaultValue = "false")
    private boolean noHeaderFiles;

    /**
     * Remove the man directory in the resulting Java runtime image,
     * equivalent to: --no-man-pages, default false
     */
    @Parameter(property = "javafx.noManPages", defaultValue = "false")
    private boolean noManPages;

    /**
     * Add the option --bind-services or not, default false.
     */
    @Parameter(property = "javafx.bindServices", defaultValue = "false")
    private boolean bindServices;

    /**
     * --ignore-signing-information, default false
     */
    @Parameter(property = "javafx.ignoreSigningInformation", defaultValue = "false")
    private boolean ignoreSigningInformation;

    /**
     * Turn on verbose mode, equivalent to: --verbose, default false
     */
    @Parameter(property = "javafx.jlinkVerbose", defaultValue = "false")
    private boolean jlinkVerbose;

    /**
     * Add a launcher script, equivalent to:
     * --launcher <name>=<module>[/<mainclass>].
     */
    @Parameter(property = "javafx.launcher")
    private String launcher;

    /**
     * The name of the folder with the resulting runtime image,
     * equivalent to --output <path>
     */
    @Parameter(property = "javafx.jlinkImageName", defaultValue = "image")
    private String jlinkImageName;

    /**
     * When set, creates a zip of the resulting runtime image.
     */
    @Parameter(property = "javafx.jlinkZipName")
    private String jlinkZipName;

    /**
     * 

* The executable. Can be a full path or the name of the executable. * In the latter case, the executable must be in the PATH for the execution to work. *

*/ @Parameter(property = "javafx.jlinkExecutable", defaultValue = "jlink") private String jlinkExecutable; /** * Optional jmodsPath path for local builds. */ @Parameter(property = "javafx.jmodsPath") private String jmodsPath; /** * The JAR archiver needed for archiving the environments. */ @Component(role = Archiver.class, hint = "zip") private ZipArchiver zipArchiver; public void execute() throws MojoExecutionException { if (skip) { getLog().info( "skipping execute as per configuration" ); return; } if (jlinkExecutable == null) { throw new MojoExecutionException("The parameter 'jlinkExecutable' is missing or invalid"); } if (basedir == null) { throw new IllegalStateException( "basedir is null. Should not be possible." ); } handleWorkingDirectory(); Map enviro = handleSystemEnvVariables(); CommandLine commandLine = getExecutablePath(jlinkExecutable, enviro, workingDirectory); if (isTargetUsingJava8(commandLine)) { getLog().info("Jlink not supported with Java 1.8"); return; } if (stripJavaDebugAttributes && !isJLinkVersion13orHigher(commandLine.getExecutable())) { stripJavaDebugAttributes = false; getLog().warn("JLink parameter --strip-java-debug-attributes only supported for version 13 and higher"); getLog().warn("The option 'stripJavaDebugAttributes' was skipped"); } try { List commandArguments = createCommandArguments(); String[] args = commandArguments.toArray(new String[commandArguments.size()]); commandLine.addArguments(args, false); getLog().debug("Executing command line: " + commandLine); Executor exec = new DefaultExecutor(); exec.setWorkingDirectory(workingDirectory); try { int resultCode; if (outputFile != null) { if ( !outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) { getLog().warn( "Could not create non existing parent directories for log file: " + outputFile ); } FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(outputFile); resultCode = executeCommandLine(exec, commandLine, enviro, outputStream); } finally { IOUtil.close(outputStream); } } else { resultCode = executeCommandLine(exec, commandLine, enviro, System.out, System.err); } if (resultCode != 0) { String message = "Result of " + commandLine.toString() + " execution is: '" + resultCode + "'."; getLog().error(message); throw new MojoExecutionException(message); } if (launcher != null && ! launcher.isEmpty()) { patchLauncherScript(launcher); if (OS.isFamilyWindows()) { patchLauncherScript(launcher + ".bat"); } } if (jlinkZipName != null && ! jlinkZipName.isEmpty()) { getLog().debug("Creating zip of runtime image"); File createZipArchiveFromImage = createZipArchiveFromImage(); project.getArtifact().setFile(createZipArchiveFromImage); } } catch (ExecuteException e) { getLog().error("Command execution failed.", e); e.printStackTrace(); throw new MojoExecutionException("Command execution failed.", e); } catch (IOException e) { getLog().error("Command execution failed.", e); throw new MojoExecutionException("Command execution failed.", e); } } catch (Exception e) { throw new MojoExecutionException("Error", e); } } private void patchLauncherScript(String launcherFilename) throws IOException { Path launcherPath = Paths.get(builddir.getAbsolutePath(), jlinkImageName, "bin", launcherFilename); if (!Files.exists(launcherPath)) { getLog().debug("Launcher file not exist: " + launcherPath); return; } if (options != null) { String optionsString = options.stream() .filter(Objects::nonNull) .filter(String.class::isInstance) .map(String.class::cast) .collect(Collectors.joining(" ")); // Add vm options to launcher script List lines = Files.lines(launcherPath) .map(line -> { boolean unixOptionsLine = "JLINK_VM_OPTIONS=".equals(line); boolean winOptionsLine = "set JLINK_VM_OPTIONS=".equals(line); if (unixOptionsLine || winOptionsLine) { String lineWrapper = unixOptionsLine ? "\"" : ""; return line + lineWrapper + optionsString + lineWrapper; } return line; }) .collect(Collectors.toList()); Files.write(launcherPath, lines); } if (commandlineArgs != null) { // Add options to launcher script List lines = Files.lines(launcherPath) .map(line -> { if (line.endsWith("$@")) { return line.replace("$@", commandlineArgs + " $@"); } return line; }) .collect(Collectors.toList()); Files.write(launcherPath, lines); } } private List createCommandArguments() throws MojoExecutionException, MojoFailureException { List commandArguments = new ArrayList<>(); preparePaths(getParent(Paths.get(jlinkExecutable), 2)); if (modulepathElements != null && !modulepathElements.isEmpty()) { commandArguments.add(" --module-path"); String modulePath = StringUtils.join(modulepathElements.iterator(), File.pathSeparator); if (jmodsPath != null && ! jmodsPath.isEmpty()) { getLog().debug("Including jmods from local path: " + jmodsPath); modulePath = jmodsPath + File.pathSeparator + modulePath; } commandArguments.add(modulePath); commandArguments.add(" --add-modules"); if (moduleDescriptor != null) { commandArguments.add(" " + moduleDescriptor.name()); } else { throw new MojoExecutionException("jlink requires a module descriptor"); } } commandArguments.add(" --output"); File image = new File(builddir, jlinkImageName); getLog().debug("image output: " + image.getAbsolutePath()); if (image.exists()) { try { Files.walk(image.toPath()) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } catch (IOException e) { throw new MojoExecutionException("Image can't be removed " + image.getAbsolutePath(), e); } } commandArguments.add(" " + image.getAbsolutePath()); if (stripDebug) { commandArguments.add(" --strip-debug"); } if (stripJavaDebugAttributes) { commandArguments.add(" --strip-java-debug-attributes"); } if (bindServices) { commandArguments.add(" --bind-services"); } if (ignoreSigningInformation) { commandArguments.add(" --ignore-signing-information"); } if (compress != null) { commandArguments.add(" --compress"); if (compress < 0 || compress > 2) { throw new MojoFailureException("The given compress parameters " + compress + " is not in the valid value range from 0..2"); } commandArguments.add(" " + compress); } if (noHeaderFiles) { commandArguments.add(" --no-header-files"); } if (noManPages) { commandArguments.add(" --no-man-pages"); } if (jlinkVerbose) { commandArguments.add(" --verbose"); } if (launcher != null && ! launcher.isEmpty()) { commandArguments.add(" --launcher"); String moduleMainClass; if (mainClass.contains("/")) { moduleMainClass = mainClass; } else { moduleMainClass = moduleDescriptor.name() + "/" + mainClass; } commandArguments.add(" " + launcher + "=" + moduleMainClass); } return commandArguments; } private File createZipArchiveFromImage() throws MojoExecutionException { File imageArchive = new File(builddir, jlinkImageName); zipArchiver.addDirectory(imageArchive); File resultArchive = new File(builddir, jlinkZipName + ".zip"); zipArchiver.setDestFile(resultArchive); try { zipArchiver.createArchive(); } catch (ArchiverException | IOException e) { throw new MojoExecutionException(e.getMessage(), e); } return resultArchive; } private boolean isJLinkVersion13orHigher(String jlinkExePath) { CommandLine versionCommandLine = new CommandLine(jlinkExePath) .addArgument("--version"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int resultCode = -1; try { resultCode = executeCommandLine(new DefaultExecutor(), versionCommandLine, null, baos, System.err); } catch (IOException e) { if (getLog().isDebugEnabled()) { getLog().error("Error getting JLink version", e); } } if (resultCode != 0) { getLog().error("Unable to get JLink version"); getLog().error("Result of " + versionCommandLine + " execution is: '" + resultCode + "'"); return false; } String versionStr = new String(baos.toByteArray()); return JLINK_VERSION_PATTERN.matcher(versionStr).lookingAt(); } // for tests }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy