create jmods of runtime dependencies and add more jlink options
This commit is contained in:
parent
e91f776db2
commit
942e765c78
6 changed files with 224 additions and 82 deletions
|
@ -8,7 +8,11 @@ import java.util.List;
|
|||
|
||||
public class JLinkExtension {
|
||||
|
||||
private final ListProperty<String> modules;
|
||||
private final ListProperty<String> addModules;
|
||||
|
||||
private final Property<Boolean> bindServices;
|
||||
|
||||
private final Property<String> launcher;
|
||||
|
||||
private final Property<Integer> compress;
|
||||
|
||||
|
@ -27,7 +31,9 @@ public class JLinkExtension {
|
|||
}
|
||||
|
||||
public JLinkExtension(Project project) {
|
||||
this.modules = project.getObjects().listProperty(String.class);
|
||||
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);
|
||||
|
@ -35,12 +41,28 @@ public class JLinkExtension {
|
|||
this.endian = project.getObjects().property(Endian.class);
|
||||
}
|
||||
|
||||
public void setModules(List<String> modules) {
|
||||
this.modules.set(modules);
|
||||
public void setAddModules(List<String> addModules) {
|
||||
this.addModules.set(addModules);
|
||||
}
|
||||
|
||||
public ListProperty<String> getModules() {
|
||||
return modules;
|
||||
public ListProperty<String> getAddModules() {
|
||||
return addModules;
|
||||
}
|
||||
|
||||
public void setBindServices(Boolean bindServices) {
|
||||
this.bindServices.set(bindServices);
|
||||
}
|
||||
|
||||
public Property<Boolean> getBindServices() {
|
||||
return bindServices;
|
||||
}
|
||||
|
||||
public void setLauncher(String launcher) {
|
||||
this.launcher.set(launcher);
|
||||
}
|
||||
|
||||
public Property<String> getLauncher() {
|
||||
return launcher;
|
||||
}
|
||||
|
||||
public void setCompress(Integer compress) {
|
||||
|
|
|
@ -3,13 +3,17 @@ package org.xbib.gradle.plugin.jlink;
|
|||
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.tasks.TaskProvider;
|
||||
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;
|
||||
|
||||
public abstract class JLinkPlugin implements Plugin<Project> {
|
||||
|
||||
|
@ -17,7 +21,9 @@ public abstract class JLinkPlugin implements Plugin<Project> {
|
|||
public void apply(Project project) {
|
||||
project.getPluginManager().apply(JavaPlugin.class);
|
||||
JLinkExtension extension = project.getExtensions().create("jlink", JLinkExtension.class);
|
||||
extension.getModules().convention(List.of("java.base"));
|
||||
extension.getAddModules().convention(List.of("java.base"));
|
||||
extension.getBindServices().convention(false);
|
||||
extension.getLauncher().unsetConvention();
|
||||
extension.getCompress().convention(6); // zip-6, default
|
||||
extension.getStripDebug().convention(true);
|
||||
extension.getNoHeaderFiles().convention(true);
|
||||
|
@ -33,7 +39,9 @@ public abstract class JLinkPlugin implements Plugin<Project> {
|
|||
TaskProvider<JLinkTask> jLinkTask = project.getTasks().register("jlink", JLinkTask.class);
|
||||
project.getTasks().withType(JLinkTask.class).forEach(it -> {
|
||||
it.dependsOn(jModTask);
|
||||
it.modules.set(extension.getModules());
|
||||
it.addModules.set(extension.getAddModules());
|
||||
it.bindServices.set(extension.getBindServices());
|
||||
it.launcher.set(extension.getLauncher());
|
||||
it.compress.set(extension.getCompress());
|
||||
it.stripDebug.set(extension.getStripDebug());
|
||||
it.noHeaderFiles.set(extension.getNoHeaderFiles());
|
||||
|
|
|
@ -35,9 +35,26 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
@Nested
|
||||
Property<JavaCompiler> javaCompiler;
|
||||
|
||||
@Input
|
||||
ListProperty<String> addModules;
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
ListProperty<String> modules;
|
||||
ListProperty<String> limitModules;
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
Property<Boolean> bindServices;
|
||||
|
||||
@InputDirectory
|
||||
DirectoryProperty modulePath;
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty outputDirectory;
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> launcher;
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
|
@ -59,31 +76,36 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
@Optional
|
||||
Property<JLinkExtension.Endian> endian;
|
||||
|
||||
@InputDirectory
|
||||
DirectoryProperty modulePath;
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty outputDirectory;
|
||||
@Input
|
||||
@Optional
|
||||
ListProperty<String> includeLocales;
|
||||
|
||||
@Inject
|
||||
public JLinkTask() {
|
||||
super(JLinkTask.class);
|
||||
this.javaCompiler = getProject().getObjects().property(JavaCompiler.class);
|
||||
this.modules = getProject().getObjects().listProperty(String.class);
|
||||
this.compress = getProject().getObjects().property(Integer.class);
|
||||
this.stripDebug = getProject().getObjects().property(Boolean.class);
|
||||
this.noHeaderFiles = getProject().getObjects().property(Boolean.class);
|
||||
this.noManPages = getProject().getObjects().property(Boolean.class);
|
||||
this.endian = getProject().getObjects().property(JLinkExtension.Endian.class);
|
||||
this.modulePath = getProject().getObjects().directoryProperty();
|
||||
this.outputDirectory = getProject().getObjects().directoryProperty();
|
||||
javaCompiler = getProject().getObjects().property(JavaCompiler.class);
|
||||
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);
|
||||
noHeaderFiles = getProject().getObjects().property(Boolean.class);
|
||||
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));
|
||||
modules.convention(List.of("java.base"));
|
||||
addModules.convention(List.of("java.base"));
|
||||
limitModules.unsetConvention();
|
||||
bindServices.convention(false);
|
||||
Provider<Directory> 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?");
|
||||
|
@ -92,30 +114,42 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
});
|
||||
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> jmodTask = getProject().getTasks().named("jmod", JModTask.class);
|
||||
File jlinkOutput = outputDirectory.dir("jlink-output").get().getAsFile();
|
||||
File jlinkOutput = outputDirectory.get().getAsFile();
|
||||
try {
|
||||
createDirectory(jlinkOutput.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
List<String> args = new ArrayList<>(List.of(
|
||||
"--module-path",
|
||||
modulePath.get().getAsFile().getAbsolutePath(),
|
||||
"--module-path",
|
||||
jmodTask.get().jmodFile.getAsFile().get().getAbsolutePath(),
|
||||
"--add-modules",
|
||||
String.join(",", modules.get()),
|
||||
"--compress",
|
||||
"zip-" + compress.get(),
|
||||
"--output",
|
||||
jlinkOutput.getAbsolutePath()
|
||||
));
|
||||
List<String> 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()) {
|
||||
args.add("--limit-modules");
|
||||
args.add(String.join(",", limitModules.get()));
|
||||
}
|
||||
args.add("--compress");
|
||||
args.add("zip-" + compress.get());
|
||||
args.add("--output");
|
||||
args.add(jlinkOutput.getAbsolutePath());
|
||||
if (bindServices.get()) {
|
||||
args.add("--bind-services");
|
||||
}
|
||||
if (launcher.isPresent()) {
|
||||
args.add("--launcher");
|
||||
args.add(launcher.get());
|
||||
}
|
||||
if (stripDebug.get()) {
|
||||
args.add("--strip-debug");
|
||||
}
|
||||
|
@ -129,8 +163,13 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
args.add("--endian");
|
||||
args.add(endian.get().toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
if (includeLocales.isPresent()) {
|
||||
args.add("--include-locales");
|
||||
args.add(String.join(",", includeLocales.get()));
|
||||
}
|
||||
setArgs(args);
|
||||
System.err.println("executing " + getExecutable() + " with " + args);
|
||||
getLogger().log(LogLevel.INFO, "executing " + getExecutable() + " with " + args);
|
||||
super.exec();
|
||||
}
|
||||
|
||||
|
@ -142,12 +181,52 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
return javaCompiler;
|
||||
}
|
||||
|
||||
public void setModules(List<String> modules) {
|
||||
this.modules.set(modules);
|
||||
public void setModulePath(Directory modulePath) {
|
||||
this.modulePath.set(modulePath);
|
||||
}
|
||||
|
||||
public ListProperty<String> getModules() {
|
||||
return modules;
|
||||
public DirectoryProperty getModulePath() {
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
public void setOutputDirectory(Directory outputDirectory) {
|
||||
this.outputDirectory.set(outputDirectory);
|
||||
}
|
||||
|
||||
public DirectoryProperty getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public void setAddModules(List<String> addModules) {
|
||||
this.addModules.set(addModules);
|
||||
}
|
||||
|
||||
public ListProperty<String> getAddModules() {
|
||||
return addModules;
|
||||
}
|
||||
|
||||
public void setLimitModules(List<String> limitModules) {
|
||||
this.limitModules.set(limitModules);
|
||||
}
|
||||
|
||||
public ListProperty<String> getLimitModules() {
|
||||
return limitModules;
|
||||
}
|
||||
|
||||
public void setBindServices(Boolean bindServices) {
|
||||
this.bindServices.set(bindServices);
|
||||
}
|
||||
|
||||
public Property<Boolean> getBindServices() {
|
||||
return bindServices;
|
||||
}
|
||||
|
||||
public void setLauncher(String launcher) {
|
||||
this.launcher.set(launcher);
|
||||
}
|
||||
|
||||
public Property<String> getLauncher() {
|
||||
return launcher;
|
||||
}
|
||||
|
||||
public void setCompress(Integer compress) {
|
||||
|
@ -190,20 +269,12 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
return endian;
|
||||
}
|
||||
|
||||
public void setModulePath(Directory modulePath) {
|
||||
this.modulePath.set(modulePath);
|
||||
public void setIncludeLocales(List<String> includeLocales) {
|
||||
this.includeLocales.set(includeLocales);
|
||||
}
|
||||
|
||||
public DirectoryProperty getModulePath() {
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
public void setOutputDirectory(Directory outputDirectory) {
|
||||
this.outputDirectory.set(outputDirectory);
|
||||
}
|
||||
|
||||
public DirectoryProperty getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
public ListProperty<String> getIncludeLocales() {
|
||||
return includeLocales;
|
||||
}
|
||||
|
||||
private static void createDirectory(Path path) throws IOException {
|
||||
|
@ -221,9 +292,9 @@ public class JLinkTask extends AbstractExecTask<JLinkTask> {
|
|||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
Files.delete(path);
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
// create only parent, jlink aborts if output directory exists
|
||||
// jlink bails out if directory is created, so we create the parent
|
||||
Files.createDirectories(path.getParent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package org.xbib.gradle.plugin.jlink;
|
||||
|
||||
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.file.RegularFile;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
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.InputFile;
|
||||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.jvm.tasks.Jar;
|
||||
import org.gradle.jvm.toolchain.JavaCompiler;
|
||||
|
@ -21,9 +27,11 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
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;
|
||||
|
||||
public class JModTask extends AbstractExecTask<JModTask> {
|
||||
|
||||
|
@ -33,8 +41,8 @@ public class JModTask extends AbstractExecTask<JModTask> {
|
|||
@InputFile
|
||||
RegularFileProperty jarFile;
|
||||
|
||||
@OutputFile
|
||||
RegularFileProperty jmodFile;
|
||||
@OutputDirectory
|
||||
DirectoryProperty jmodDirectory;
|
||||
|
||||
@Inject
|
||||
public JModTask() {
|
||||
|
@ -47,28 +55,42 @@ public class JModTask extends AbstractExecTask<JModTask> {
|
|||
TaskProvider<Jar> jarTask = getProject().getTasks().named("jar", Jar.class);
|
||||
Objects.requireNonNull(jarTask);
|
||||
jarFile.convention(jarTask.get().getArchiveFile());
|
||||
jmodFile = getProject().getObjects().fileProperty();
|
||||
jmodFile.convention(getProject().getLayout().getBuildDirectory()
|
||||
.dir("jmod").get().file(getProject().getName() + ".jmod"));
|
||||
jmodDirectory = getProject().getObjects().directoryProperty();
|
||||
jmodDirectory.convention(getProject().getLayout().getBuildDirectory().dir("jmods"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exec() {
|
||||
setExecutable(javaCompiler.get().getMetadata().getInstallationPath().file("bin/jmod"));
|
||||
try {
|
||||
createDirectory(jmodFile.getAsFile().get().toPath());
|
||||
System.err.println("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);
|
||||
}
|
||||
List<String> args = new ArrayList<>(List.of(
|
||||
List<String> args = List.of(
|
||||
"create",
|
||||
"--class-path",
|
||||
jarFile.get().getAsFile().getAbsolutePath(),
|
||||
jmodFile.get().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"))) {
|
||||
args = List.of(
|
||||
"create",
|
||||
"--class-path",
|
||||
artifact.getFile().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();
|
||||
}
|
||||
}
|
||||
|
||||
public void setJavaCompiler(JavaCompiler javaCompiler) {
|
||||
|
@ -87,12 +109,12 @@ public class JModTask extends AbstractExecTask<JModTask> {
|
|||
return jarFile;
|
||||
}
|
||||
|
||||
public void setJmodFile(RegularFile jmodFile) {
|
||||
this.jmodFile.set(jmodFile);
|
||||
public void setJmodDirectory(Directory jmodDirectory) {
|
||||
this.jmodDirectory.set(jmodDirectory);
|
||||
}
|
||||
|
||||
public RegularFileProperty getJmodFile() {
|
||||
return jmodFile;
|
||||
public DirectoryProperty getJmodDirectory() {
|
||||
return jmodDirectory;
|
||||
}
|
||||
|
||||
private static void createDirectory(Path path) throws IOException {
|
||||
|
@ -110,9 +132,26 @@ public class JModTask extends AbstractExecTask<JModTask> {
|
|||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
Files.delete(path);
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
// create only parent
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.createDirectory(path);
|
||||
}
|
||||
|
||||
private static List<ResolvedArtifact> collectArtifacts(Configuration configuration) {
|
||||
return configuration.getResolvedConfiguration()
|
||||
.getFirstLevelModuleDependencies()
|
||||
.stream()
|
||||
.flatMap(JModTask::of)
|
||||
.flatMap(dependency -> dependency.getModuleArtifacts().stream())
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static Stream<ResolvedDependency> of(ResolvedDependency node) {
|
||||
return of(node, ResolvedDependency::getChildren);
|
||||
}
|
||||
|
||||
private static <T> Stream<T> of(T node, Function<T, Collection<T>> childrenFn) {
|
||||
return Stream.concat(Stream.of(node), childrenFn.apply(node).stream().flatMap(n -> of(n, childrenFn)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,11 +32,6 @@ class JLinkGradleBuild {
|
|||
id("org.xbib.gradle.plugin.jlink")
|
||||
}
|
||||
group = "org.example"
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
'''
|
||||
tapFile("app/src/main/java/org/example/app/Main.java") << '''
|
||||
package org.example.app;
|
||||
|
@ -98,7 +93,10 @@ class JLinkGradleBuild {
|
|||
}
|
||||
|
||||
GradleRunner runner(String... args) {
|
||||
def err = new PrintWriter(System.err)
|
||||
GradleRunner.create()
|
||||
.forwardStdError(err)
|
||||
.forwardStdOutput(err)
|
||||
.forwardOutput()
|
||||
.withPluginClasspath()
|
||||
.withProjectDir(projectDir)
|
||||
|
|
|
@ -11,12 +11,16 @@ class JLinkTest extends Specification {
|
|||
@Delegate
|
||||
JLinkGradleBuild build = new JLinkGradleBuild()
|
||||
|
||||
def "can use jlink plugin with success=#success"() {
|
||||
def "can use jlink plugin"() {
|
||||
given:
|
||||
def taskToRun = ":app:jlink"
|
||||
appBuildFile << """
|
||||
dependencies {
|
||||
runtimeOnly "org.junit.jupiter:junit-jupiter-engine:5.10.2"
|
||||
}
|
||||
jlink {
|
||||
modules.set(List.of("org.example.app"))
|
||||
addModules.set(List.of("org.example.app"))
|
||||
launcher.set("app=org.example.app/org.example.app.Main")
|
||||
}
|
||||
"""
|
||||
appModuleInfoFile << """
|
||||
|
|
Loading…
Reference in a new issue