com.gluonhq.NativeBaseMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gluonfx-maven-plugin Show documentation
Show all versions of gluonfx-maven-plugin Show documentation
GluonFX plugin allows to run JavaFX application on the JVM or to create their native images.
/*
* Copyright (c) 2019, 2021, Gluon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.gluonhq;
import com.gluonhq.attach.AttachArtifactResolver;
import com.gluonhq.substrate.Constants;
import com.gluonhq.substrate.ProjectConfiguration;
import com.gluonhq.substrate.SubstrateDispatcher;
import com.gluonhq.substrate.model.Triplet;
import com.gluonhq.substrate.target.WebTargetConfiguration;
import com.gluonhq.utils.MavenArtifactResolver;
import org.apache.commons.exec.ProcessDestroyer;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Repository;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.DependencyFilter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.gluonhq.attach.AttachArtifactResolver.DEPENDENCY_GROUP;
import static com.gluonhq.attach.AttachArtifactResolver.UTIL_ARTIFACT;
public abstract class NativeBaseMojo extends AbstractMojo {
private static final List ALLOWED_DEPENDENCY_TYPES = Collections.singletonList("jar");
Path outputDir;
@Parameter(defaultValue = "${project}", readonly = true)
MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true)
MavenSession session;
@Component
BuildPluginManager pluginManager;
@Parameter(readonly = true, required = true, defaultValue = "${basedir}")
File basedir;
@Parameter(property = "gluonfx.graalvmHome")
String graalvmHome;
@Parameter(property = "gluonfx.javaStaticSdkVersion")
String javaStaticSdkVersion;
@Parameter(property = "gluonfx.javafxStaticSdkVersion")
String javafxStaticSdkVersion;
@Parameter(property = "gluonfx.target", defaultValue = "host")
String target;
@Parameter(property = "gluonfx.bundlesList")
List bundlesList;
@Parameter(property = "gluonfx.resourcesList")
List resourcesList;
@Parameter(property = "gluonfx.reflectionList")
List reflectionList;
@Parameter(property = "gluonfx.jniList")
List jniList;
@Parameter(property = "gluonfx.nativeImageArgs")
List nativeImageArgs;
@Parameter(property = "gluonfx.runtimeArgs")
List runtimeArgs;
@Parameter(property = "gluonfx.mainClass", required = true)
String mainClass;
@Parameter(property = "gluonfx.executable", defaultValue = "java")
String executable;
@Parameter(property = "gluonfx.verbose", defaultValue = "false")
String verbose;
@Parameter(property = "gluonfx.attachList")
List attachList;
@Parameter(property = "gluonfx.enableSWRendering", defaultValue = "false")
String enableSWRendering;
@Parameter(property = "gluonfx.remoteHostName")
String remoteHostName;
@Parameter(property = "gluonfx.remoteDir")
String remoteDir;
@Parameter(property = "gluonfx.releaseConfiguration")
ReleaseConfiguration releaseConfiguration;
private ProcessDestroyer processDestroyer;
public SubstrateDispatcher createSubstrateDispatcher() throws IOException, MojoExecutionException {
if (getGraalvmHome().isEmpty()) {
throw new MojoExecutionException("GraalVM installation directory not found." +
" Either set GRAALVM_HOME as an environment variable or" +
" set graalvmHome in gluonfx-plugin configuration");
}
outputDir = Path.of(project.getBuild().getDirectory(), Constants.GLUONFX_PATH);
ProjectConfiguration substrateConfiguration = createSubstrateConfiguration();
return new SubstrateDispatcher(outputDir, substrateConfiguration);
}
private ProjectConfiguration createSubstrateConfiguration() {
ProjectConfiguration clientConfig = new ProjectConfiguration(mainClass, getProjectClasspath());
clientConfig.setGraalPath(Path.of(getGraalvmHome().get()));
clientConfig.setJavaStaticSdkVersion(javaStaticSdkVersion);
clientConfig.setJavafxStaticSdkVersion(javafxStaticSdkVersion);
Triplet targetTriplet;
switch (target) {
case Constants.PROFILE_HOST:
targetTriplet = Triplet.fromCurrentOS();
break;
case Constants.PROFILE_IOS:
targetTriplet = new Triplet(Constants.Profile.IOS);
break;
case Constants.PROFILE_IOS_SIM:
targetTriplet = new Triplet(Constants.Profile.IOS_SIM);
break;
case Constants.PROFILE_ANDROID:
targetTriplet = new Triplet(Constants.Profile.ANDROID);
break;
case Constants.PROFILE_LINUX_AARCH64:
targetTriplet = new Triplet(Constants.Profile.LINUX_AARCH64);
break;
case Constants.PROFILE_WEB:
targetTriplet = new Triplet(Constants.Profile.WEB);
break;
default:
throw new RuntimeException("No valid target found for " + target);
}
if (releaseConfiguration != null) {
clientConfig.setReleaseConfiguration(releaseConfiguration.toSubstrate());
}
clientConfig.setTarget(targetTriplet);
clientConfig.setBundlesList(bundlesList);
clientConfig.setResourcesList(resourcesList);
clientConfig.setJniList(jniList);
clientConfig.setCompilerArgs(nativeImageArgs);
clientConfig.setRuntimeArgs(runtimeArgs);
clientConfig.setReflectionList(reflectionList);
clientConfig.setAppId(project.getGroupId() + "." + project.getArtifactId());
clientConfig.setAppName(project.getName());
clientConfig.setVerbose("true".equals(verbose));
clientConfig.setUsePrismSW("true".equals(enableSWRendering));
clientConfig.setRemoteHostName(remoteHostName);
clientConfig.setRemoteDir(remoteDir);
return clientConfig;
}
ProcessDestroyer getProcessDestroyer() {
if (processDestroyer == null) {
processDestroyer = new ShutdownHookProcessDestroyer();
}
return processDestroyer;
}
private String getProjectClasspath() {
List classPath = getClasspathElements(project);
getLog().debug("classPath = " + classPath);
return classPath.stream()
.map(File::getAbsolutePath)
.collect(Collectors.joining(File.pathSeparator));
}
private List getClasspathElements(MavenProject project) {
List repositories = project.getRepositories();
Repository gluonRepository = new Repository();
gluonRepository.setId("Gluon");
gluonRepository.setUrl("https://nexus.gluonhq.com/nexus/content/repositories/releases");
repositories.add(gluonRepository);
MavenArtifactResolver.initRepositories(repositories);
List attachDependencies = getAttachDependencies();
List list = Stream.concat(project.getArtifacts().stream(), attachDependencies.stream())
.filter(d -> ALLOWED_DEPENDENCY_TYPES.stream().anyMatch(t -> t.equals(d.getType())))
.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()));
// include runtime dependencies
getRuntimeDependencies().stream()
.filter(d -> !list.contains(d))
.forEach(list::add);
// remove provided dependencies
getProvidedDependencies().stream()
.filter(list::contains)
.forEach(list::remove);
// WEB
if (Constants.PROFILE_WEB.equals(target)) {
project.getArtifacts().stream()
.filter(a -> "org.openjfx".equals(a.getGroupId()) && a.getClassifier() != null)
.map(a -> new DefaultArtifact(a.getGroupId(), a.getArtifactId(),
Constants.WEB_AOT_CLASSIFIER, "jar", Constants.DEFAULT_JAVAFX_JS_SDK_VERSION))
.flatMap(a -> {
DependencyFilter exclusions = (node, parents) ->
!node.getArtifact().getClassifier().equals(Constants.WEB_AOT_CLASSIFIER);
Set resolve = MavenArtifactResolver.getInstance().resolve(a, exclusions);
if (resolve == null) {
return Stream.empty();
}
return resolve.stream();
})
.distinct()
.map(Artifact::getFile)
.forEach(list::add);
WebTargetConfiguration.WEB_AOT_DEPENDENCIES.stream()
.map(s -> s.split(":"))
.map(a -> new DefaultArtifact(a[0], a[1],
a.length == 4 ? a[3] : null, "jar", a[2]))
.flatMap(a -> {
Set resolve = MavenArtifactResolver.getInstance().resolve(a);
if (resolve == null) {
return Stream.empty();
}
return resolve.stream();
})
.distinct()
.map(Artifact::getFile)
.forEach(list::add);
}
return list;
}
List getAttachDependencies() {
List dependencies = project.getDependencies();
// include dependencies from project artifacts (transitive dependencies)
project.getArtifacts().stream()
.filter(a -> DEPENDENCY_GROUP.equals(a.getGroupId()))
.map(a -> {
Dependency d = new Dependency();
d.setGroupId(a.getGroupId());
d.setArtifactId(a.getArtifactId());
d.setVersion(a.getVersion());
return d;
})
.forEach(dependencies::add);
Map attachMap = AttachArtifactResolver.findArtifactsForTarget(dependencies, target);
if (attachList != null) {
return Stream.concat(attachList.stream(), Stream.of(UTIL_ARTIFACT))
.distinct()
.map(attachMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
return new ArrayList<>();
}
private List getRuntimeDependencies() {
return getDependencies("runtime");
}
private List getProvidedDependencies() {
return getDependencies("provided");
}
private List getDependencies(String scope) {
if (scope == null || scope.isEmpty()) {
return new ArrayList<>();
}
return project.getDependencies().stream()
.filter(d -> ALLOWED_DEPENDENCY_TYPES.stream().anyMatch(t -> t.equals(d.getType())))
.filter(d -> scope.equals(d.getScope()))
.map(d -> new DefaultArtifact(d.getGroupId(), d.getArtifactId(),
d.getClassifier(), d.getType(), d.getVersion()))
.flatMap(a -> {
Set resolve = MavenArtifactResolver.getInstance().resolve(a);
if (resolve == null) {
return Stream.empty();
}
return resolve.stream();
})
.distinct()
.map(Artifact::getFile)
.collect(Collectors.toList());
}
Optional getGraalvmHome() {
return Optional.ofNullable(graalvmHome)
.or(() -> Optional.ofNullable(System.getenv("GRAALVM_HOME")));
}
}