org.openjfx.JavaFXJLinkMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javafx-maven-plugin Show documentation
Show all versions of javafx-maven-plugin Show documentation
The JavaFX Plugin is used to run JavaFX 11+ projects
/*
* 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