added jlink plugin

This commit is contained in:
2023-10-06 15:02:15 +08:00
parent dcdeb2871a
commit b1757a85be
10 changed files with 275 additions and 31 deletions

20
graalvm/build.gradle Normal file
View File

@@ -0,0 +1,20 @@
plugins {
id 'java-gradle-plugin'
}
gradlePlugin {
plugins {
create("NativeImagePlugin") {
id = 'net.woggioni.gradle.graalvm.native-image'
implementationClass = "net.woggioni.gradle.graalvm.NativeImagePlugin"
}
}
plugins {
create("JlinkPlugin") {
id = 'net.woggioni.gradle.graalvm.jlink'
implementationClass = "net.woggioni.gradle.graalvm.JlinkPlugin"
}
}
}

View File

@@ -0,0 +1,5 @@
package net.woggioni.gradle.graalvm;
public class Constants {
public static final String GRAALVM_TASK_GROUP = "graalvm";
}

View File

@@ -0,0 +1,57 @@
package net.woggioni.gradle.graalvm;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.api.plugins.JavaLibraryPlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.bundling.Zip;
import org.gradle.jvm.tasks.Jar;
import java.util.Optional;
public class JlinkPlugin implements Plugin<Project> {
public static final String JLINK_TASK_NAME = "jlink";
public static final String JLINK_DIST_TASK_NAME = "jlinkDist";
@Override
public void apply(Project project) {
project.getPluginManager().apply(JavaLibraryPlugin.class);
ExtensionContainer extensionContainer = project.getExtensions();
BasePluginExtension basePluginExtension = extensionContainer.getByType(BasePluginExtension.class);
JavaApplication javaApplicationExtension =
Optional.ofNullable(extensionContainer.findByType(JavaApplication.class))
.orElseGet(() -> extensionContainer.create("application", JavaApplication.class));
TaskContainer tasks = project.getTasks();
Provider<JlinkTask> jlinTaskProvider = tasks.register(JLINK_TASK_NAME, JlinkTask.class, jlinkTask -> {
ConfigurationContainer configurations = project.getConfigurations();
FileCollection classpath = project.files(tasks.named(JavaPlugin.JAR_TASK_NAME),
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
jlinkTask.getClasspath().set(classpath);
});
Provider<Zip> jlinkZipTaskProvider = tasks.register(JLINK_DIST_TASK_NAME, Zip.class, zip -> {
zip.getArchiveBaseName().set(project.getName());
if(project.getVersion() != null) {
zip.getArchiveVersion().set(project.getVersion().toString());
}
zip.getDestinationDirectory().set(basePluginExtension.getDistsDirectory());
zip.from(jlinTaskProvider);
});
tasks.named(BasePlugin.ASSEMBLE_TASK_NAME, Task.class, t -> {
t.getInputs().files(jlinkZipTaskProvider);
});
}
}

View File

@@ -0,0 +1,158 @@
package net.woggioni.gradle.graalvm;
import lombok.SneakyThrows;
import org.gradle.api.Project;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.Exec;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.OutputFile;
import org.gradle.internal.jvm.JavaModuleDetector;
import org.gradle.jvm.toolchain.JavaInstallationMetadata;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.process.CommandLineArgumentProvider;
import javax.inject.Inject;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import static java.util.Optional.ofNullable;
import static net.woggioni.gradle.graalvm.Constants.GRAALVM_TASK_GROUP;
public abstract class JlinkTask extends Exec {
@Classpath
public abstract Property<FileCollection> getClasspath();
@InputDirectory
public abstract DirectoryProperty getGraalVmHome();
@Input
@Optional
public abstract Property<String> getMainClass();
@Input
@Optional
public abstract Property<String> getMainModule();
@Input
public abstract ListProperty<String> getAdditionalModules();
@Inject
protected abstract JavaModuleDetector getJavaModuleDetector();
@OutputDirectory
public abstract DirectoryProperty getOutputDir();
private final Logger logger;
public JlinkTask() {
Project project = getProject();
logger = project.getLogger();
setGroup(GRAALVM_TASK_GROUP);
setDescription(
"Generates a custom Java runtime image that contains only the platform modules" +
" that are required for a given application");
ExtensionContainer ext = project.getExtensions();
JavaApplication javaApplication = ext.findByType(JavaApplication.class);
if(!Objects.isNull(javaApplication)) {
getMainClass().convention(javaApplication.getMainClass());
getMainModule().convention(javaApplication.getMainModule());
}
getClasspath().convention(project.files());
ProjectLayout layout = project.getLayout();
JavaToolchainService javaToolchainService = ext.findByType(JavaToolchainService.class);
JavaPluginExtension javaPluginExtension = ext.findByType(JavaPluginExtension.class);
Provider<Directory> graalHomeDirectoryProvider = ofNullable(javaPluginExtension.getToolchain()).map(javaToolchainSpec ->
javaToolchainService.launcherFor(javaToolchainSpec)
).map(javaLauncher ->
javaLauncher.map(JavaLauncher::getMetadata).map(JavaInstallationMetadata::getInstallationPath)
).orElseGet(() -> layout.dir(project.provider(() ->project.file(System.getProperty("java.home")))));
getGraalVmHome().convention(graalHomeDirectoryProvider);
getAdditionalModules().convention(new ArrayList<>());
BasePluginExtension basePluginExtension =
ext.getByType(BasePluginExtension.class);
getOutputDir().convention(
basePluginExtension.getLibsDirectory()
.dir(project.getName() +
ofNullable(project.getVersion()).map(it -> "-" + it).orElse(""))
);
Object executableProvider = new Object() {
@Override
public String toString() {
return getGraalVmHome().get() + "/bin/jlink";
}
};
executable(executableProvider);
CommandLineArgumentProvider argumentProvider = new CommandLineArgumentProvider() {
@Override
@SneakyThrows
public Iterable<String> asArguments() {
List<String> result = new ArrayList<>();
result.add("--compress=2");
JavaModuleDetector javaModuleDetector = getJavaModuleDetector();
FileCollection classpath = getClasspath().get();
FileCollection mp = javaModuleDetector.inferModulePath(true, classpath);
if(!mp.isEmpty()) {
result.add("-p");
result.add(mp.getAsPath());
}
if(getMainModule().isPresent()) {
result.add("--launcher");
String launcherArg = project.getName() + '=' +
getMainModule().get() +
ofNullable(getMainClass().getOrElse(null)).map(it -> '/' + it).orElse("");
result.add(launcherArg);
}
result.add("--output");
result.add(getOutputDir().get().getAsFile().toString());
List<String> additionalModules = getAdditionalModules().get();
if(getMainModule().isPresent() || !additionalModules.isEmpty()) {
result.add("--add-modules");
ofNullable(getMainModule().getOrElse(null)).ifPresent(result::add);
additionalModules.forEach(result::add);
}
return Collections.unmodifiableList(result);
}
};
getArgumentProviders().add(argumentProvider);
}
@Override
@SneakyThrows
protected void exec() {
Files.walk(getOutputDir().get().getAsFile().toPath())
.sorted(Comparator.reverseOrder())
.forEach(new Consumer<Path>() {
@Override
@SneakyThrows
public void accept(Path path) {
Files.delete(path);
}
});
super.exec();
}
}

View File

@@ -1,14 +1,12 @@
package net.woggioni.gradle.nativeimage;
package net.woggioni.gradle.graalvm;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskContainer;
@@ -19,10 +17,9 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.StreamSupport;
import static net.woggioni.gradle.nativeimage.NativeImagePlugin.NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME;
import static net.woggioni.gradle.nativeimage.NativeImagePlugin.NATIVE_IMAGE_TASK_GROUP;
import static net.woggioni.gradle.graalvm.Constants.GRAALVM_TASK_GROUP;
import static net.woggioni.gradle.graalvm.NativeImagePlugin.NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME;
public abstract class NativeImageConfigurationTask extends JavaExec {
@@ -30,7 +27,7 @@ public abstract class NativeImageConfigurationTask extends JavaExec {
public abstract DirectoryProperty getConfigurationDir();
public NativeImageConfigurationTask() {
setGroup(NATIVE_IMAGE_TASK_GROUP);
setGroup(GRAALVM_TASK_GROUP);
setDescription("Run the application with the native-image-agent " +
"to create a configuration for native image creation");
ProjectLayout layout = getProject().getLayout();

View File

@@ -1,10 +1,12 @@
package net.woggioni.gradle.nativeimage;
package net.woggioni.gradle.graalvm;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.api.plugins.JavaLibraryPlugin;
@@ -21,8 +23,6 @@ public class NativeImagePlugin implements Plugin<Project> {
public static final String CONFIGURE_NATIVE_IMAGE_TASK_NAME = "configureNativeImage";
public static final String NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME = "native-image";
public static final String NATIVE_IMAGE_TASK_GROUP = "native image";
@Override
public void apply(Project project) {
project.getPluginManager().apply(JavaLibraryPlugin.class);
@@ -33,6 +33,7 @@ public class NativeImagePlugin implements Plugin<Project> {
.orElseGet(() -> extensionContainer.create("application", JavaApplication.class));
TaskContainer tasks = project.getTasks();
Provider<Jar> jarTaskProvider = tasks.named(JavaPlugin.JAR_TASK_NAME, Jar.class, jar -> {
jar.from(layout.getProjectDirectory().dir(NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME), copySpec -> {
copySpec.into(
@@ -43,12 +44,20 @@ public class NativeImagePlugin implements Plugin<Project> {
);
});
});
tasks.register(CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask.class);
tasks.register(NATIVE_IMAGE_TASK_NAME, NativeImageTask.class, nativeImageTask -> {
Provider<NativeImageConfigurationTask> nativeImageConfigurationTaskProvider =
tasks.register(CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask.class);
Provider<NativeImageTask> nativeImageTaskProvider = tasks.register(NATIVE_IMAGE_TASK_NAME, NativeImageTask.class, nativeImageTask -> {
nativeImageTask.getInputs().files(nativeImageConfigurationTaskProvider);
ConfigurationContainer configurations = project.getConfigurations();
FileCollection classpath = project.files(jarTaskProvider,
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
nativeImageTask.getClasspath().set(classpath);
});
tasks.named(BasePlugin.ASSEMBLE_TASK_NAME, Task.class, t -> {
t.getInputs().files(nativeImageTaskProvider);
});
}
}

View File

@@ -1,4 +1,4 @@
package net.woggioni.gradle.nativeimage;
package net.woggioni.gradle.graalvm;
import org.gradle.api.Project;
import org.gradle.api.file.Directory;
@@ -32,7 +32,7 @@ import java.util.List;
import java.util.Objects;
import static java.util.Optional.ofNullable;
import static net.woggioni.gradle.nativeimage.NativeImagePlugin.NATIVE_IMAGE_TASK_GROUP;
import static net.woggioni.gradle.graalvm.Constants.GRAALVM_TASK_GROUP;
public abstract class NativeImageTask extends Exec {
@@ -45,6 +45,10 @@ public abstract class NativeImageTask extends Exec {
@Input
public abstract Property<Boolean> getUseJpms();
@Input
public abstract Property<Boolean> getUseMusl();
@Input
public abstract Property<Boolean> getBuildStaticImage();
@Input
public abstract Property<Boolean> getEnableFallback();
@Input
@@ -63,9 +67,11 @@ public abstract class NativeImageTask extends Exec {
public NativeImageTask() {
Project project = getProject();
logger = project.getLogger();
setGroup(NATIVE_IMAGE_TASK_GROUP);
setGroup(GRAALVM_TASK_GROUP);
setDescription("Create a native image of the application using GraalVM");
getUseJpms().convention(false);
getUseMusl().convention(false);
getBuildStaticImage().convention(false);
getEnableFallback().convention(false);
ExtensionContainer ext = project.getExtensions();
JavaApplication javaApplication = ext.findByType(JavaApplication.class);
@@ -102,6 +108,12 @@ public abstract class NativeImageTask extends Exec {
if(!getEnableFallback().get()) {
result.add("--no-fallback");
}
if(getBuildStaticImage().get()) {
result.add("--static");
}
if(getUseMusl().get()) {
result.add("--libc=musl");
}
JavaModuleDetector javaModuleDetector = getJavaModuleDetector();
boolean useJpms = getUseJpms().get();
FileCollection classpath = getClasspath().get();
@@ -118,7 +130,6 @@ public abstract class NativeImageTask extends Exec {
result.add("-o");
result.add(getOutputFile().get().getAsFile().toString());
result.add(getMainClass().get());
logger.info("Native image arguments: " + String.join(" ", result));
return Collections.unmodifiableList(result);
}
};

View File

@@ -2,7 +2,7 @@ woggioniMavenRepositoryUrl=https://mvn.woggioni.net/
lys.catalog.version=2023.10.01
version.myGradlePlugins=2023.10.05
version.myGradlePlugins=2023.10.06
version.gradle=7.6
version.felix.config.admin=1.9.26
version.felix=7.0.5

View File

@@ -1,13 +0,0 @@
plugins {
id 'java-gradle-plugin'
}
gradlePlugin {
plugins {
create("NativeImagePlugin") {
id = "net.woggioni.gradle.native-image"
implementationClass = "net.woggioni.gradle.nativeimage.NativeImagePlugin"
}
}
}

View File

@@ -26,4 +26,4 @@ include 'osgi-app:osgi-simple-bootstrapper-api'
include 'osgi-app:osgi-simple-bootstrapper-application'
include 'wildfly'
include 'sambal'
include 'native-image'
include 'graalvm'