From 79e53ba85ff0b61ba74038927b30856d05c025bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Sun, 3 Nov 2024 23:37:21 +0100 Subject: [PATCH] more options and better tests for the jlink plugin --- gradle-plugin-jacc/README.md | 0 gradle-plugin-jflex/README.md | 0 gradle-plugin-jlink/README.md | 40 +++++ .../xbib/gradle/plugin/jlink/JDepsTask.java | 165 ++++++++++++++++++ .../gradle/plugin/jlink/JLinkExtension.java | 85 ++++++++- .../xbib/gradle/plugin/jlink/JLinkPlugin.java | 64 +++++-- .../xbib/gradle/plugin/jlink/JLinkTask.java | 64 +++---- .../xbib/gradle/plugin/jlink/JModTask.java | 44 +++-- .../plugin/jlink/JLinkGradleBuild.groovy | 4 +- .../xbib/gradle/plugin/jlink/JLinkTest.groovy | 3 +- .../plugin/jpackage/JPackagePlugin.java | 5 + ...uild.groovy => JPackageGradleBuild.groovy} | 6 +- .../plugin/jpackage/JPackageTest.groovy | 12 +- 13 files changed, 404 insertions(+), 88 deletions(-) mode change 100755 => 100644 gradle-plugin-jacc/README.md mode change 100755 => 100644 gradle-plugin-jflex/README.md create mode 100644 gradle-plugin-jlink/README.md create mode 100644 gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JDepsTask.java rename gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/{fixture/GradleBuild.groovy => JPackageGradleBuild.groovy} (95%) diff --git a/gradle-plugin-jacc/README.md b/gradle-plugin-jacc/README.md old mode 100755 new mode 100644 diff --git a/gradle-plugin-jflex/README.md b/gradle-plugin-jflex/README.md old mode 100755 new mode 100644 diff --git a/gradle-plugin-jlink/README.md b/gradle-plugin-jlink/README.md new file mode 100644 index 0000000..cd87beb --- /dev/null +++ b/gradle-plugin-jlink/README.md @@ -0,0 +1,40 @@ +# JLink Gradle plugin + +This project defines a Gradle plugin that enable the use of +[jlink](https://docs.oracle.com/javase/9/tools/jlink.htm) in Gradle builds. +In general, the plugin enables developers to create Java Runtimes with jlink. +It uses the java toolchain to locate the `jlink` executable and creates +a java runtime for your module path based Java application. This +modularization allows small, secure and tailored application distributions. + +## Jlink plugin usage + +This Gradle plugin integrates with the [Gradle Java Library plugin](https://docs.gradle.org/current/userguide/java_library_plugin.html) +to create distributions automatically using a jlink runtime rather than a user provided JDK runtime. + +It applies the `org.xbib.gradle.plugin.jlink` plugin which configures a `jlink` task that executes the jlink tool. + +```groovy +plugins { + id 'org.xbib.gradle.plugin.jlink' version "8.8.0" +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + +jlink { + modules.set(List.of("org.example.app")) + launcher.set("app=org.example.app/org.example.app.Main") + stripDebug.set(true) +} + +``` + +In addition, the plugin configures a `jmod` task to download the dependent artifacts on the +runtime class path and create jmod files for the `jlink` task. Also, a `jdeps` task is automatically +configured to show the dependent modules. + +The plugin is currently only available under Linux. diff --git a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JDepsTask.java b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JDepsTask.java new file mode 100644 index 0000000..a8fc162 --- /dev/null +++ b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JDepsTask.java @@ -0,0 +1,165 @@ +package org.xbib.gradle.plugin.jlink; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; +import javax.inject.Inject; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.artifacts.ResolvedDependency; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.AbstractExecTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; +import org.gradle.jvm.toolchain.JavaCompiler; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.jvm.toolchain.JavaToolchainSpec; + +public class JDepsTask extends AbstractExecTask { + + @Nested + Property javaCompiler; + + @InputDirectory + @Optional + DirectoryProperty modulePath; + + @Input + Property printModuleDeps; + + @Input + Property recursive; + + @Input + Property verbose; + + @Inject + public JDepsTask() { + super(JDepsTask.class); + javaCompiler = getProject().getObjects().property(JavaCompiler.class); + JavaToolchainSpec toolchain = getProject().getExtensions().getByType(JavaPluginExtension.class).getToolchain(); + JavaToolchainService service = getProject().getExtensions().getByType(JavaToolchainService.class); + javaCompiler.convention(service.compilerFor(toolchain)); + Provider modulePathProvider = javaCompiler.map(it -> { + Directory jmods = it.getMetadata().getInstallationPath().dir("jmods"); + if (jmods.getAsFile().exists()) { + getLogger().log(LogLevel.INFO, "using jmods directory found in installation for module path"); + return jmods; + } else { + getLogger().log(LogLevel.WARN, "directory " + jmods + " does not exist! Is jmods package installed?"); + return null; + } + }); + modulePath = getProject().getObjects().directoryProperty(); + modulePath.convention(modulePathProvider); + printModuleDeps = getProject().getObjects().property(Boolean.class); + printModuleDeps.convention(true); + recursive = getProject().getObjects().property(Boolean.class); + recursive.convention(true); + verbose = getProject().getObjects().property(Boolean.class); + verbose.convention(false); + } + + @Override + public void exec() { + setExecutable(javaCompiler.get().getMetadata().getInstallationPath().file("bin/jdeps")); + TaskProvider jarTask = getProject().getTasks().named("jar", Jar.class); + Objects.requireNonNull(jarTask, "jar task must be present"); + Configuration runtimeConfiguration = getProject().getConfigurations().getByName("runtimeClasspath"); + Objects.requireNonNull(runtimeConfiguration, "runtimeClasspath configuration must be present"); + List resolvedArtifacts = collectArtifacts(runtimeConfiguration); + List jarFiles = resolvedArtifacts.stream() + .map(a -> a.getFile().getAbsolutePath()) + .toList(); + List args = new ArrayList<>(); + args.add("--module-path"); + args.add(modulePath.get().getAsFile().getAbsolutePath()); + args.add("--module-path"); + args.add(String.join(";", jarFiles)); // jdeps does not understand jmod files + args.add("--class-path"); + args.add(String.join(";", jarFiles)); + if (printModuleDeps.get()) { + args.add("--print-module-deps"); + } + if (recursive.get()) { + args.add("--recursive"); + } + if (verbose.get()) { + args.add("-v"); // verbose, list deps for each class + } + args.add(jarTask.get().getArchiveFile().get().getAsFile().getAbsolutePath()); + setArgs(args); + getLogger().log(LogLevel.INFO, "executing " + getExecutable() + " with " + args); + super.exec(); + } + + public void setJavaCompiler(JavaCompiler javaCompiler) { + this.javaCompiler.set(javaCompiler); + } + + public Property getJavaCompiler() { + return javaCompiler; + } + + public void setModulePath(Directory modulePath) { + this.modulePath.set(modulePath); + } + + public DirectoryProperty getModulePath() { + return modulePath; + } + + public void setPrintModuleDeps(Boolean printModuleDeps) { + this.printModuleDeps.set(printModuleDeps); + } + + public Property getPrintModuleDeps() { + return printModuleDeps; + } + + public void setRecursive(Boolean recursive) { + this.recursive.set(recursive); + } + + public Property getRecursive() { + return recursive; + } + + public void setVerbose(Boolean verbose) { + this.verbose.set(verbose); + } + + public Property getVerbose() { + return verbose; + } + + private static List collectArtifacts(Configuration configuration) { + return configuration.getResolvedConfiguration() + .getFirstLevelModuleDependencies() + .stream() + .flatMap(JDepsTask::of) + .flatMap(dependency -> dependency.getModuleArtifacts().stream()) + .distinct() + .toList(); + } + + private static Stream of(ResolvedDependency node) { + return of(node, ResolvedDependency::getChildren); + } + + private static Stream of(T node, Function> childrenFn) { + return Stream.concat(Stream.of(node), childrenFn.apply(node).stream().flatMap(n -> of(n, childrenFn))); + } +} diff --git a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkExtension.java b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkExtension.java index d1290b1..4fd265f 100644 --- a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkExtension.java +++ b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkExtension.java @@ -1,15 +1,26 @@ package org.xbib.gradle.plugin.jlink; import org.gradle.api.Project; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import java.util.List; +import org.gradle.jvm.toolchain.JavaCompiler; public class JLinkExtension { + private final Property javaCompiler; + + private final DirectoryProperty modulePath; + + private final DirectoryProperty outputDirectory; + private final ListProperty addModules; + private final ListProperty limitModules; + private final Property bindServices; private final Property launcher; @@ -24,6 +35,10 @@ public class JLinkExtension { private final Property endian; + private final ListProperty includeLocales; + + private final Property moduleVersion; + public enum Endian { LITTLE, BIG, @@ -31,14 +46,44 @@ public class JLinkExtension { } public JLinkExtension(Project project) { - this.addModules = project.getObjects().listProperty(String.class); - this.bindServices = project.getObjects().property(Boolean.class); - this.launcher = project.getObjects().property(String.class); - this.compress = project.getObjects().property(Integer.class); - this.stripDebug = project.getObjects().property(Boolean.class); - this.noHeaderFiles = project.getObjects().property(Boolean.class); - this.noManPages = project.getObjects().property(Boolean.class); - this.endian = project.getObjects().property(Endian.class); + javaCompiler = project.getObjects().property(JavaCompiler.class); + modulePath = project.getObjects().directoryProperty(); + outputDirectory = project.getObjects().directoryProperty(); + addModules = project.getObjects().listProperty(String.class); + limitModules = project.getObjects().listProperty(String.class); + bindServices = project.getObjects().property(Boolean.class); + launcher = project.getObjects().property(String.class); + compress = project.getObjects().property(Integer.class); + stripDebug = project.getObjects().property(Boolean.class); + noHeaderFiles = project.getObjects().property(Boolean.class); + noManPages = project.getObjects().property(Boolean.class); + endian = project.getObjects().property(Endian.class); + includeLocales = project.getObjects().listProperty(String.class); + moduleVersion = project.getObjects().property(String.class); + } + + public void setJavaCompiler(JavaCompiler javaCompiler) { + this.javaCompiler.set(javaCompiler); + } + + public Property getJavaCompiler() { + return javaCompiler; + } + + public void setModulePath(Directory modulePath) { + this.modulePath.set(modulePath); + } + + public DirectoryProperty getModulePath() { + return modulePath; + } + + public void setOutputDirectory(Directory outputDirectory) { + this.outputDirectory.set(outputDirectory); + } + + public DirectoryProperty getOutputDirectory() { + return outputDirectory; } public void setAddModules(List addModules) { @@ -49,6 +94,14 @@ public class JLinkExtension { return addModules; } + public void setLimitModules(List limitModules) { + this.limitModules.set(limitModules); + } + + public ListProperty getLimitModules() { + return limitModules; + } + public void setBindServices(Boolean bindServices) { this.bindServices.set(bindServices); } @@ -104,4 +157,20 @@ public class JLinkExtension { public Property getEndian() { return endian; } + + public void setIncludeLocales(List includeLocales) { + this.includeLocales.set(includeLocales); + } + + public ListProperty getIncludeLocales() { + return includeLocales; + } + + public void setModuleVersion(String moduleVersion) { + this.moduleVersion.set(moduleVersion); + } + + public Property getModuleVersion() { + return moduleVersion; + } } diff --git a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkPlugin.java b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkPlugin.java index 30af010..feb24c3 100644 --- a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkPlugin.java +++ b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkPlugin.java @@ -1,45 +1,87 @@ package org.xbib.gradle.plugin.jlink; +import org.gradle.api.GradleException; +import org.gradle.api.NonNullApi; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.ResolvedDependency; import org.gradle.api.attributes.Usage; -import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.file.Directory; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.plugins.JavaLibraryPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; +import org.gradle.internal.os.OperatingSystem; import org.gradle.jvm.tasks.Jar; -import java.util.Collection; import java.util.List; import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Stream; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +@NonNullApi public abstract class JLinkPlugin implements Plugin { @Override public void apply(Project project) { - project.getPluginManager().apply(JavaPlugin.class); + if (!OperatingSystem.current().isLinux()) { + throw new GradleException("this plugin is only available for Linux"); + } + project.getPluginManager().apply(JavaLibraryPlugin.class); JLinkExtension extension = project.getExtensions().create("jlink", JLinkExtension.class); + JavaToolchainSpec toolchain = project.getExtensions().getByType(JavaPluginExtension.class).getToolchain(); + JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class); + extension.getJavaCompiler().convention(service.compilerFor(toolchain)); + Provider modulePathProvider = extension.getJavaCompiler().map(it -> { + Directory jmods = it.getMetadata().getInstallationPath().dir("jmods"); + if (jmods.getAsFile().exists()) { + project.getLogger().log(LogLevel.INFO, "using jmods directory found in installation for module path"); + return jmods; + } else { + project.getLogger().log(LogLevel.WARN, "directory " + jmods + " does not exist! Is jmods package installed?"); + return null; + } + }); + extension.getModulePath().convention(modulePathProvider); + extension.getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir("jlink")); extension.getAddModules().convention(List.of("java.base")); + extension.getLimitModules().convention(List.of()); extension.getBindServices().convention(false); extension.getLauncher().unsetConvention(); extension.getCompress().convention(6); // zip-6, default - extension.getStripDebug().convention(true); + extension.getStripDebug().convention(false); extension.getNoHeaderFiles().convention(true); extension.getNoManPages().convention(true); extension.getEndian().convention(JLinkExtension.Endian.NATIVE); + extension.getIncludeLocales().unsetConvention(); + extension.getModuleVersion().convention(project.getVersion().toString()); + // get the jar task TaskProvider jarTask = project.getTasks().named("jar", Jar.class); - Objects.requireNonNull(jarTask); + Objects.requireNonNull(jarTask, "jar task required to be present"); TaskProvider jModTask = project.getTasks().register("jmod", JModTask.class); project.getTasks().withType(JModTask.class).forEach(it -> { it.dependsOn(jarTask); + it.javaCompiler.set(extension.getJavaCompiler()); + it.moduleVersion.set(extension.getModuleVersion()); }); - Objects.requireNonNull(jModTask); - TaskProvider jLinkTask = project.getTasks().register("jlink", JLinkTask.class); - project.getTasks().withType(JLinkTask.class).forEach(it -> { + // install the jdeps task + TaskProvider jDepsTask = project.getTasks().register("jdeps", JDepsTask.class); + project.getTasks().withType(JDepsTask.class).forEach(it -> { it.dependsOn(jModTask); + it.modulePath.set(extension.getModulePath()); + }); + Objects.requireNonNull(jModTask, "jmod task required to be present"); + // depend jlink on jdeps and jmod task + TaskProvider jLinkTask = project.getTasks().register("jlink", JLinkTask.class); + Objects.requireNonNull(jLinkTask, "jlink task required to be present"); + project.getTasks().withType(JLinkTask.class).forEach(it -> { + it.dependsOn(jDepsTask); + it.javaCompiler.set(extension.getJavaCompiler()); + it.modulePath.set(extension.getModulePath()); + it.outputDirectory.set(extension.getOutputDirectory()); it.addModules.set(extension.getAddModules()); + it.limitModules.set(extension.getLimitModules()); it.bindServices.set(extension.getBindServices()); it.launcher.set(extension.getLauncher()); it.compress.set(extension.getCompress()); diff --git a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkTask.java b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkTask.java index c36aaf3..8893dce 100644 --- a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkTask.java +++ b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JLinkTask.java @@ -1,12 +1,11 @@ package org.xbib.gradle.plugin.jlink; +import java.util.Objects; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.logging.LogLevel; -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.AbstractExecTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; @@ -15,8 +14,6 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.TaskProvider; import org.gradle.jvm.toolchain.JavaCompiler; -import org.gradle.jvm.toolchain.JavaToolchainService; -import org.gradle.jvm.toolchain.JavaToolchainSpec; import javax.inject.Inject; import java.io.File; @@ -35,6 +32,12 @@ public class JLinkTask extends AbstractExecTask { @Nested Property javaCompiler; + @InputDirectory + DirectoryProperty modulePath; + + @OutputDirectory + DirectoryProperty outputDirectory; + @Input ListProperty addModules; @@ -46,12 +49,6 @@ public class JLinkTask extends AbstractExecTask { @Optional Property bindServices; - @InputDirectory - DirectoryProperty modulePath; - - @OutputDirectory - DirectoryProperty outputDirectory; - @Input @Optional Property launcher; @@ -84,11 +81,11 @@ public class JLinkTask extends AbstractExecTask { public JLinkTask() { super(JLinkTask.class); javaCompiler = getProject().getObjects().property(JavaCompiler.class); + modulePath = getProject().getObjects().directoryProperty(); + outputDirectory = getProject().getObjects().directoryProperty(); addModules = getProject().getObjects().listProperty(String.class); limitModules = getProject().getObjects().listProperty(String.class); bindServices = getProject().getObjects().property(Boolean.class); - modulePath = getProject().getObjects().directoryProperty(); - outputDirectory = getProject().getObjects().directoryProperty(); launcher = getProject().getObjects().property(String.class); compress = getProject().getObjects().property(Integer.class); stripDebug = getProject().getObjects().property(Boolean.class); @@ -96,32 +93,11 @@ public class JLinkTask extends AbstractExecTask { noManPages = getProject().getObjects().property(Boolean.class); endian = getProject().getObjects().property(JLinkExtension.Endian.class); includeLocales = getProject().getObjects().listProperty(String.class); - JavaToolchainSpec toolchain = getProject().getExtensions().getByType(JavaPluginExtension.class).getToolchain(); - JavaToolchainService service = getProject().getExtensions().getByType(JavaToolchainService.class); - javaCompiler.convention(service.compilerFor(toolchain)); - addModules.convention(List.of("java.base")); - limitModules.unsetConvention(); - bindServices.convention(false); - Provider modulePathProvider = javaCompiler.map(it -> { - Directory jmods = it.getMetadata().getInstallationPath().dir("jmods"); - if (jmods.getAsFile().exists()) { - getLogger().log(LogLevel.INFO, "using jmods directory found in installation for module path"); - return jmods; - } else { - getLogger().log(LogLevel.WARN, "directory " + jmods + " does not exist! Is jmods package installed?"); - return null; - } - }); - modulePath.convention(modulePathProvider); - outputDirectory.convention(getProject().getLayout().getBuildDirectory().dir("jlink")); - launcher.unsetConvention(); - includeLocales.unsetConvention(); } @Override public void exec() { setExecutable(javaCompiler.get().getMetadata().getInstallationPath().file("bin/jlink")); - TaskProvider jmodTask = getProject().getTasks().named("jmod", JModTask.class); File jlinkOutput = outputDirectory.get().getAsFile(); try { createDirectory(jlinkOutput.toPath()); @@ -129,13 +105,7 @@ public class JLinkTask extends AbstractExecTask { throw new RuntimeException(e); } List args = new ArrayList<>(); - args.add("--module-path"); - args.add(modulePath.get().getAsFile().getAbsolutePath()); - args.add("--module-path"); - args.add(jmodTask.get().jmodDirectory.getAsFile().get().getAbsolutePath()); - args.add("--add-modules"); - args.add(String.join(",", addModules.get())); - if (limitModules.isPresent()) { + if (limitModules.isPresent() && !limitModules.get().isEmpty()) { args.add("--limit-modules"); args.add(String.join(",", limitModules.get())); } @@ -152,6 +122,9 @@ public class JLinkTask extends AbstractExecTask { } if (stripDebug.get()) { args.add("--strip-debug"); + //args.add("--strip-native-debug-symbols=keep-debuginfo-files"); + args.add("--strip-native-debug-symbols=exclude-debuginfo-files"); + args.add("--strip-java-debug-attributes"); } if (noHeaderFiles.get()) { args.add("--no-header-files"); @@ -163,12 +136,19 @@ public class JLinkTask extends AbstractExecTask { args.add("--endian"); args.add(endian.get().toString().toLowerCase(Locale.ROOT)); } - if (includeLocales.isPresent()) { + if (includeLocales.isPresent() && !includeLocales.get().isEmpty()) { args.add("--include-locales"); args.add(String.join(",", includeLocales.get())); } + args.add("--module-path"); + args.add(modulePath.get().getAsFile().getAbsolutePath()); + args.add("--module-path"); + TaskProvider jmodTask = getProject().getTasks().named("jmod", JModTask.class); + Objects.requireNonNull(jmodTask, "jmod task must be present"); + args.add(jmodTask.get().jModDirectory.getAsFile().get().getAbsolutePath()); + args.add("--add-modules"); + args.add(String.join(",", addModules.get())); setArgs(args); - System.err.println("executing " + getExecutable() + " with " + args); getLogger().log(LogLevel.INFO, "executing " + getExecutable() + " with " + args); super.exec(); } diff --git a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JModTask.java b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JModTask.java index 9ac6821..bc72235 100644 --- a/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JModTask.java +++ b/gradle-plugin-jlink/src/main/java/org/xbib/gradle/plugin/jlink/JModTask.java @@ -11,6 +11,7 @@ import org.gradle.api.logging.LogLevel; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Property; import org.gradle.api.tasks.AbstractExecTask; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.OutputDirectory; @@ -42,7 +43,10 @@ public class JModTask extends AbstractExecTask { RegularFileProperty jarFile; @OutputDirectory - DirectoryProperty jmodDirectory; + DirectoryProperty jModDirectory; + + @Input + Property moduleVersion; @Inject public JModTask() { @@ -55,16 +59,18 @@ public class JModTask extends AbstractExecTask { TaskProvider jarTask = getProject().getTasks().named("jar", Jar.class); Objects.requireNonNull(jarTask); jarFile.convention(jarTask.get().getArchiveFile()); - jmodDirectory = getProject().getObjects().directoryProperty(); - jmodDirectory.convention(getProject().getLayout().getBuildDirectory().dir("jmods")); + jModDirectory = getProject().getObjects().directoryProperty(); + jModDirectory.convention(getProject().getLayout().getBuildDirectory().dir("jmods")); + moduleVersion = getProject().getObjects().property(String.class); + moduleVersion.convention(getProject().getVersion().toString()); } @Override public void exec() { setExecutable(javaCompiler.get().getMetadata().getInstallationPath().file("bin/jmod")); try { - System.err.println("creating directory " + jmodDirectory.getAsFile().get().toPath()); - createDirectory(jmodDirectory.getAsFile().get().toPath()); + getLogger().log(LogLevel.INFO, "creating directory " + jModDirectory.getAsFile().get().toPath()); + createDirectory(jModDirectory.getAsFile().get().toPath()); } catch (IOException e) { getLogger().log(LogLevel.ERROR, e.getMessage(), e); throw new RuntimeException(e); @@ -73,21 +79,21 @@ public class JModTask extends AbstractExecTask { "create", "--class-path", jarFile.get().getAsFile().getAbsolutePath(), - jmodDirectory.get().file(getProject().getName() + ".jmod").getAsFile().getAbsolutePath() + jModDirectory.get().file(getProject().getName() + ".jmod").getAsFile().getAbsolutePath() ); setArgs(args); - System.err.println("executing " + getExecutable() + " with " + args); getLogger().log(LogLevel.INFO, "executing " + getExecutable() + " with " + args); super.exec(); - for (ResolvedArtifact artifact : collectArtifacts(getProject().getConfigurations().getByName("runtimeClasspath"))) { + Configuration runtimeConfiguration = getProject().getConfigurations().getByName("runtimeClasspath"); + Objects.requireNonNull(runtimeConfiguration, "runtimeClasspath configuration must be present"); + for (ResolvedArtifact artifact : collectArtifacts(runtimeConfiguration)) { args = List.of( "create", "--class-path", artifact.getFile().getAbsolutePath(), - jmodDirectory.get().file(artifact.getName() + ".jmod").getAsFile().getAbsolutePath() + jModDirectory.get().file(artifact.getName() + ".jmod").getAsFile().getAbsolutePath() ); setArgs(args); - System.err.println("executing " + getExecutable() + " with " + args); getLogger().log(LogLevel.INFO, "executing " + getExecutable() + " with " + args); super.exec(); } @@ -109,12 +115,20 @@ public class JModTask extends AbstractExecTask { return jarFile; } - public void setJmodDirectory(Directory jmodDirectory) { - this.jmodDirectory.set(jmodDirectory); + public void setjModDirectory(Directory jModDirectory) { + this.jModDirectory.set(jModDirectory); } - public DirectoryProperty getJmodDirectory() { - return jmodDirectory; + public DirectoryProperty getjModDirectory() { + return jModDirectory; + } + + public void setModuleVersion(String moduleVersion) { + this.moduleVersion.set(moduleVersion); + } + + public Property getModuleVersion() { + return moduleVersion; } private static void createDirectory(Path path) throws IOException { @@ -134,7 +148,7 @@ public class JModTask extends AbstractExecTask { }); Files.deleteIfExists(path); } - Files.createDirectory(path); + Files.createDirectories(path); } private static List collectArtifacts(Configuration configuration) { diff --git a/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkGradleBuild.groovy b/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkGradleBuild.groovy index 456bcf4..1916e1f 100644 --- a/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkGradleBuild.groovy +++ b/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkGradleBuild.groovy @@ -29,9 +29,9 @@ class JLinkGradleBuild { ''' appBuildFile << ''' plugins { - id("org.xbib.gradle.plugin.jlink") + id "org.xbib.gradle.plugin.jlink" } - group = "org.example" + group = "org.example" ''' tapFile("app/src/main/java/org/example/app/Main.java") << ''' package org.example.app; diff --git a/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkTest.groovy b/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkTest.groovy index badb447..ce3ea0e 100644 --- a/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkTest.groovy +++ b/gradle-plugin-jlink/src/test/groovy/org/xbib/gradle/plugin/jlink/JLinkTest.groovy @@ -16,11 +16,12 @@ class JLinkTest extends Specification { def taskToRun = ":app:jlink" appBuildFile << """ dependencies { - runtimeOnly "org.junit.jupiter:junit-jupiter-engine:5.10.2" + api "org.junit.jupiter:junit-jupiter-engine:5.10.2" } jlink { addModules.set(List.of("org.example.app")) launcher.set("app=org.example.app/org.example.app.Main") + stripDebug.set(true) } """ appModuleInfoFile << """ diff --git a/gradle-plugin-jpackage/src/main/java/org/xbib/gradle/plugin/jpackage/JPackagePlugin.java b/gradle-plugin-jpackage/src/main/java/org/xbib/gradle/plugin/jpackage/JPackagePlugin.java index b8f155d..f9d44bb 100644 --- a/gradle-plugin-jpackage/src/main/java/org/xbib/gradle/plugin/jpackage/JPackagePlugin.java +++ b/gradle-plugin-jpackage/src/main/java/org/xbib/gradle/plugin/jpackage/JPackagePlugin.java @@ -1,11 +1,13 @@ package org.xbib.gradle.plugin.jpackage; +import org.gradle.api.GradleException; import org.gradle.api.NonNullApi; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.internal.os.OperatingSystem; @SuppressWarnings("unused") @NonNullApi @@ -13,6 +15,9 @@ public abstract class JPackagePlugin implements Plugin { @Override public void apply(Project project) { + if (!OperatingSystem.current().isLinux()) { + throw new GradleException("this plugin is only available for Linux"); + } project.getPlugins().apply(JavaPlugin.class); SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); SourceDirectorySet mainResources = sourceSets.getByName("main").getResources(); diff --git a/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/fixture/GradleBuild.groovy b/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageGradleBuild.groovy similarity index 95% rename from gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/fixture/GradleBuild.groovy rename to gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageGradleBuild.groovy index f5c533c..e6d2594 100644 --- a/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/fixture/GradleBuild.groovy +++ b/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageGradleBuild.groovy @@ -1,4 +1,4 @@ -package org.xbib.gradle.plugin.jpackage.fixture +package org.xbib.gradle.plugin.jpackage import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -6,7 +6,7 @@ import org.gradle.testkit.runner.GradleRunner import java.lang.management.ManagementFactory import java.nio.file.Files -class GradleBuild { +class JPackageGradleBuild { final File projectDir final File settingsFile @@ -17,7 +17,7 @@ class GradleBuild { final String gradleVersionUnderTest = System.getProperty('gradleVersionUnderTest') - GradleBuild(File projectDir = Files.createTempDirectory('gradle-build').toFile()) { + JPackageGradleBuild(File projectDir = Files.createTempDirectory('gradle-build').toFile()) { this.projectDir = projectDir this.settingsFile = file('settings.gradle.kts') this.appBuildFile = file('app/build.gradle.kts') diff --git a/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageTest.groovy b/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageTest.groovy index 89f67aa..c8c8656 100644 --- a/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageTest.groovy +++ b/gradle-plugin-jpackage/src/test/groovy/org/xbib/gradle/plugin/jpackage/JPackageTest.groovy @@ -1,19 +1,19 @@ package org.xbib.gradle.plugin.jpackage -import org.xbib.gradle.plugin.jpackage.fixture.GradleBuild + import spock.lang.Specification import static org.gradle.testkit.runner.TaskOutcome.FAILED import static org.gradle.testkit.runner.TaskOutcome.SUCCESS -import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.hostOs -import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnLinux -import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnMacos -import static org.xbib.gradle.plugin.jpackage.fixture.GradleBuild.runsOnWindows +import static JPackageGradleBuild.hostOs +import static JPackageGradleBuild.runsOnLinux +import static JPackageGradleBuild.runsOnMacos +import static JPackageGradleBuild.runsOnWindows class JPackageTest extends Specification { @Delegate - GradleBuild build = new GradleBuild() + JPackageGradleBuild build = new JPackageGradleBuild() def "can use plugin on #os with success=#success"() { given: