From 3f6627465c07c3ac258bfa5f951bcac79ed0f8d6 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Tue, 15 Apr 2025 22:04:50 +0800 Subject: [PATCH] added jdeps plugin --- graalvm/{REAMDE.md => README.md} | 0 .../woggioni/gradle/graalvm/JlinkPlugin.java | 3 - .../woggioni/gradle/graalvm/JlinkTask.java | 74 ++++++- gradle.properties | 2 +- jdeps/build.gradle | 13 ++ .../net/woggioni/gradle/jdeps/Constants.java | 5 + .../woggioni/gradle/jdeps/JdepsPlugin.java | 26 +++ .../net/woggioni/gradle/jdeps/JdepsTask.java | 209 ++++++++++++++++++ settings.gradle | 1 + 9 files changed, 321 insertions(+), 12 deletions(-) rename graalvm/{REAMDE.md => README.md} (100%) create mode 100644 jdeps/build.gradle create mode 100644 jdeps/src/main/java/net/woggioni/gradle/jdeps/Constants.java create mode 100644 jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsPlugin.java create mode 100644 jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsTask.java diff --git a/graalvm/REAMDE.md b/graalvm/README.md similarity index 100% rename from graalvm/REAMDE.md rename to graalvm/README.md diff --git a/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkPlugin.java b/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkPlugin.java index 4a40d6f..7f212a4 100644 --- a/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkPlugin.java +++ b/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkPlugin.java @@ -27,9 +27,6 @@ public class JlinkPlugin implements Plugin { 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 jlinTaskProvider = tasks.register(JLINK_TASK_NAME, JlinkTask.class, jlinkTask -> { diff --git a/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkTask.java b/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkTask.java index e5ad0f2..ff7dbd1 100644 --- a/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkTask.java +++ b/graalvm/src/main/java/net/woggioni/gradle/graalvm/JlinkTask.java @@ -1,6 +1,7 @@ package net.woggioni.gradle.graalvm; import lombok.SneakyThrows; +import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; @@ -8,6 +9,7 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.file.ProjectLayout; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.BasePluginExtension; import org.gradle.api.plugins.ExtensionContainer; import org.gradle.api.plugins.JavaApplication; @@ -25,6 +27,8 @@ 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.jvm.toolchain.JavaToolchainSpec; +import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec; import org.gradle.process.CommandLineArgumentProvider; import javax.inject.Inject; @@ -42,6 +46,13 @@ import static net.woggioni.gradle.graalvm.Constants.GRAALVM_TASK_GROUP; public abstract class JlinkTask extends Exec { + private final JavaToolchainSpec toolchain; + + public JavaToolchainSpec toolchain(Action action) { + action.execute(toolchain); + return toolchain; + } + @Classpath public abstract Property getClasspath(); @@ -59,9 +70,24 @@ public abstract class JlinkTask extends Exec { @Input public abstract ListProperty getAdditionalModules(); + @Input + public abstract ListProperty getLimitModules(); + @Input public abstract Property getBindServices(); + @Input + public abstract Property getIncludeHeaderFiles(); + + @Input + public abstract Property getIncludeManPages(); + + @Input + public abstract Property getStripDebug(); + + @Input + public abstract Property getGenerateCdsArchive(); + @Input @Optional public abstract Property getCompressionLevel(); @@ -74,7 +100,8 @@ public abstract class JlinkTask extends Exec { private static final Logger log = Logging.getLogger(JlinkTask.class); - public JlinkTask() { + @Inject + public JlinkTask(ObjectFactory objects) { Project project = getProject(); setGroup(GRAALVM_TASK_GROUP); setDescription( @@ -86,17 +113,26 @@ public abstract class JlinkTask extends Exec { getMainClass().convention(javaApplication.getMainClass()); getMainModule().convention(javaApplication.getMainModule()); } + getIncludeManPages().convention(false); + getIncludeHeaderFiles().convention(false); + getGenerateCdsArchive().convention(true); + getStripDebug().convention(true); getClasspath().convention(project.files()); ProjectLayout layout = project.getLayout(); + toolchain = getObjectFactory().newInstance(DefaultToolchainSpec.class); JavaToolchainService javaToolchainService = ext.findByType(JavaToolchainService.class); - JavaPluginExtension javaPluginExtension = ext.findByType(JavaPluginExtension.class); - Provider 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"))))); + Provider graalHomeDirectoryProvider = javaToolchainService.launcherFor(it -> { + it.getLanguageVersion().set(toolchain.getLanguageVersion()); + it.getVendor().set(toolchain.getVendor()); + it.getImplementation().set(toolchain.getImplementation()); + }).map(javaLauncher -> + javaLauncher.getMetadata().getInstallationPath() + ).orElse(layout.dir(project.provider(() -> project.file(System.getProperty("java.home"))))); + getGraalVmHome().convention(graalHomeDirectoryProvider); + getGraalVmHome().convention(graalHomeDirectoryProvider); getAdditionalModules().convention(new ArrayList<>()); + getLimitModules().convention(new ArrayList<>()); getBindServices().convention(false); BasePluginExtension basePluginExtension = @@ -118,7 +154,7 @@ public abstract class JlinkTask extends Exec { @SneakyThrows public Iterable asArguments() { List result = new ArrayList<>(); - final Property compressionLevelProperty= getCompressionLevel(); + final Property compressionLevelProperty = getCompressionLevel(); if(compressionLevelProperty.isPresent()) { result.add(String.format("--compress=zip-%d", compressionLevelProperty.get())); } @@ -152,6 +188,28 @@ public abstract class JlinkTask extends Exec { result.add(String.join(",", modules2BeAdded)); } } + List limitModules = getLimitModules().get(); + if(!limitModules.isEmpty()) { + result.add("--limit-modules"); + final List modules2BeAdded = new ArrayList<>(); + modules2BeAdded.addAll(limitModules); + if(!modules2BeAdded.isEmpty()) { + result.add(String.join(",", modules2BeAdded)); + } + } + + if(getStripDebug().getOrElse(false)) { + result.add("--strip-debug"); + } + if(getGenerateCdsArchive().getOrElse(false)) { + result.add("--generate-cds-archive"); + } + if(!getIncludeHeaderFiles().getOrElse(true)) { + result.add("--no-header-files"); + } + if(!getIncludeManPages().getOrElse(true)) { + result.add("--no-man-pages"); + } return Collections.unmodifiableList(result); } }; diff --git a/gradle.properties b/gradle.properties index 0970586..053426b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ lys.catalog.version=2025.02.05 -version.myGradlePlugins=2025.04.15 +version.myGradlePlugins=2025.04.16 version.gradle=8.12 gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven diff --git a/jdeps/build.gradle b/jdeps/build.gradle new file mode 100644 index 0000000..f9402ca --- /dev/null +++ b/jdeps/build.gradle @@ -0,0 +1,13 @@ +plugins { + id 'java-gradle-plugin' +} + + +gradlePlugin { + plugins { + create("JdepsPlugin") { + id = 'net.woggioni.gradle.jdeps' + implementationClass = "net.woggioni.gradle.jdeps.JdepsPlugin" + } + } +} diff --git a/jdeps/src/main/java/net/woggioni/gradle/jdeps/Constants.java b/jdeps/src/main/java/net/woggioni/gradle/jdeps/Constants.java new file mode 100644 index 0000000..6c20b00 --- /dev/null +++ b/jdeps/src/main/java/net/woggioni/gradle/jdeps/Constants.java @@ -0,0 +1,5 @@ +package net.woggioni.gradle.jdeps; + +public class Constants { + public static final String JDEPS_TASK_GROUP = "jdeps"; +} diff --git a/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsPlugin.java b/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsPlugin.java new file mode 100644 index 0000000..da8a437 --- /dev/null +++ b/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsPlugin.java @@ -0,0 +1,26 @@ +package net.woggioni.gradle.jdeps; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.JavaLibraryPlugin; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskContainer; + +public class JdepsPlugin implements Plugin { + public static final String JDEPS_TASK_NAME = "jdeps"; + + @Override + public void apply(Project project) { + project.getPluginManager().apply(JavaLibraryPlugin.class); + TaskContainer tasks = project.getTasks(); + Provider jdepsTaskProvider = tasks.register(JDEPS_TASK_NAME, JdepsTask.class, jdepsTask -> { + ConfigurationContainer configurations = project.getConfigurations(); + FileCollection classpath = project.files(configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)); + jdepsTask.getClasspath().set(classpath); + jdepsTask.getArchives().set(project.files(tasks.named(JavaPlugin.JAR_TASK_NAME))); + }); + } +} diff --git a/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsTask.java b/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsTask.java new file mode 100644 index 0000000..6ede0ce --- /dev/null +++ b/jdeps/src/main/java/net/woggioni/gradle/jdeps/JdepsTask.java @@ -0,0 +1,209 @@ +package net.woggioni.gradle.jdeps; + +import lombok.SneakyThrows; +import org.gradle.api.Action; +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.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.ExtensionContainer; +import org.gradle.api.plugins.JavaApplication; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.reporting.ReportingExtension; +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.internal.jvm.JavaModuleDetector; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.jvm.toolchain.internal.DefaultToolchainSpec; +import org.gradle.process.CommandLineArgumentProvider; + +import javax.inject.Inject; +import java.io.File; +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.jdeps.Constants.JDEPS_TASK_GROUP; + +public abstract class JdepsTask extends Exec { + + private final JavaToolchainSpec toolchain; + + public JavaToolchainSpec toolchain(Action action) { + action.execute(toolchain); + return toolchain; + } + + @Classpath + public abstract Property getClasspath(); + + @Classpath + public abstract Property getArchives(); + + @InputDirectory + public abstract DirectoryProperty getJavaHome(); + + @Input + @Optional + public abstract Property getMainClass(); + + @Input + @Optional + public abstract Property getMainModule(); + + @Input + public abstract ListProperty getAdditionalModules(); + + @Inject + protected abstract JavaModuleDetector getJavaModuleDetector(); + + @OutputDirectory + public abstract DirectoryProperty getOutputDir(); + + @Optional + @OutputDirectory + public abstract DirectoryProperty getDotOutput(); + + @Input + @Optional + public abstract Property getJavaRelease(); + + @Input + public abstract Property getRecursive(); + + private static final Logger log = Logging.getLogger(JdepsTask.class); + + public JdepsTask() { + Project project = getProject(); + setGroup(JDEPS_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()); + getRecursive().convention(true); + ProjectLayout layout = project.getLayout(); + toolchain = getObjectFactory().newInstance(DefaultToolchainSpec.class); + JavaToolchainService javaToolchainService = ext.findByType(JavaToolchainService.class); + Provider graalHomeDirectoryProvider = javaToolchainService.launcherFor(it -> { + it.getLanguageVersion().set(toolchain.getLanguageVersion()); + it.getVendor().set(toolchain.getVendor()); + it.getImplementation().set(toolchain.getImplementation()); + }).map(javaLauncher -> + javaLauncher.getMetadata().getInstallationPath() + ).orElse(layout.dir(project.provider(() -> project.file(System.getProperty("java.home"))))); + getJavaHome().convention(graalHomeDirectoryProvider); + + getJavaHome().convention(graalHomeDirectoryProvider); + getAdditionalModules().convention(new ArrayList<>()); + + ReportingExtension reporting = ext.getByType(ReportingExtension.class); + + getOutputDir().convention( + reporting.getBaseDirectory() + .dir(project.getName() + + ofNullable(project.getVersion()).map(it -> "-" + it).orElse("")) + ); + getDotOutput().convention(getOutputDir().dir("graphviz")); + Object executableProvider = new Object() { + @Override + public String toString() { + return getJavaHome().get() + "/bin/jdeps"; + } + }; + executable(executableProvider); + CommandLineArgumentProvider argumentProvider = new CommandLineArgumentProvider() { + @Override + @SneakyThrows + public Iterable asArguments() { + List result = new ArrayList<>(); + JavaModuleDetector javaModuleDetector = getJavaModuleDetector(); + FileCollection classpath = getClasspath().get(); + FileCollection mp = javaModuleDetector.inferModulePath(true, classpath); + if (!mp.isEmpty()) { + result.add("--module-path"); + result.add(mp.getAsPath()); + } + + FileCollection cp = classpath.minus(mp); + if(!cp.isEmpty()) { + result.add("-cp"); + result.add(cp.getAsPath()); + } + + List additionalModules = getAdditionalModules().get(); + if (!additionalModules.isEmpty()) { + result.add("--add-modules"); + final List modules2BeAdded = new ArrayList<>(); + modules2BeAdded.addAll(additionalModules); + if (!modules2BeAdded.isEmpty()) { + result.add(String.join(",", modules2BeAdded)); + } + } + if (getDotOutput().isPresent()) { + result.add("-dotoutput"); + result.add(getDotOutput().get().getAsFile().toString()); + } + + if(getRecursive().get()) { + result.add("--recursive"); + } else { + result.add("--no-recursive"); + } + + if (getMainModule().isPresent()) { + result.add("-m"); + result.add(getMainModule().get()); + } + if (getJavaRelease().isPresent()) { + result.add("--multi-release"); + result.add(getJavaRelease().get().toString()); + } + + for (File archive : getArchives().get()) { + result.add(archive.toString()); + } + return Collections.unmodifiableList(result); + } + }; + getArgumentProviders().add(argumentProvider); + + } + + @Override + @SneakyThrows + protected void exec() { + Files.walk(getOutputDir().get().getAsFile().toPath()) + .sorted(Comparator.reverseOrder()) + .forEach(new Consumer() { + @Override + @SneakyThrows + public void accept(Path path) { + Files.delete(path); + } + }); + super.exec(); + } +} diff --git a/settings.gradle b/settings.gradle index d60dd3b..2a5275d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,3 +27,4 @@ include 'osgi-app:osgi-simple-bootstrapper-application' include 'wildfly' include 'sambal' include 'graalvm' +include 'jdeps'