Compare commits

...

10 Commits

25 changed files with 1489 additions and 335 deletions

View File

@@ -5,12 +5,12 @@ subprojects { subproject ->
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
languageVersion = JavaLanguageVersion.of(17)
}
}
int javaVersion
if(subproject.path == ':osgi-app') {
if(subproject.path == ':osgi-app' || subproject.path == ':multi-release-jar') {
javaVersion = 11
} else {
javaVersion = 8

132
graalvm/REAMDE.md Normal file
View File

@@ -0,0 +1,132 @@
## Overview
This project contains 2 Gradle plugin:
- NativeImage allows you to create a native executable file from a Gradle project using GraalVM's `native-image` tool
- Jlink allows you to create a native distribution of a Gradle project using GraalVM `jlink` tool
### Native Image plugin
Declare the plugin in your build's `settings.gradle` like this
```groovy
pluginManagement {
repositories {
maven {
url = 'https://woggioni.net/mvn/'
}
}
plugins {
id "net.woggioni.gradle.graalvm.native-image" version "2023.10.23"
}
}
```
Then add it to a project's `build.gradle`
```groovy
plugins {
id 'net.woggioni.gradle.graalvm.native-image'
}
configureNativeImage {
args = [ 'your', 'command', 'line', 'arguments']
}
application {
mainClass = 'your.main.Class'
}
```
Mind that if your project also uses the built-in Gradle `application` plugin, that must be applied before the `net.woggioni.gradle.graalvm.native-image`.
The plugin adds 2 tasks to your project:
- `configureNativeImage` of type `net.woggioni.gradle.graalvm.NativeImageConfigurationTask` which launches
your application with the [native-image-agent](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/)
to generate the native image configuration files in `${project.projectDir}/native-image`
- `nativeImage` of type `net.woggioni.gradle.graalvm.NativeImageTask` that creates the native executable the project's
libraries folder (`${project.buildDir}/libs`)
#### Configuration for `NativeImageConfigurationTask`
###### mergeConfiguration
This boolean property decides whether to create a new set of configuration files or simply append to the existing ones
#### Configuration for `NativeImageTask`
###### mainClass
The name of the main class the native executable will launch, defaults to the value set in
```groovy
application {
mainClass = 'my.main.class'
}
```
###### mainModule
The name of the main JPMS module the native executable will launch, defaults to the value set in
```groovy
application {
mainModule = 'my.main.module'
}
```
Only applicable when `useJpms` is true
###### useJpms
Whether or not enable JPMS in the generated executable
(dependencies that support JPMS will be forwarded to `native-image` using the `-p` instead of the `-cp` option)
###### useMusl
This boolean property allows to link the generated executable to musl libc,
note that in order to do that a valid (linked to musl-libc) compiler toolchain must be available
on the `PATH`, for example if building x86_64 executables on Linux, GraalVM will look for
`x86_64-linux-musl-gcc`
###### buildStaticImage
This boolean property allows to create a statically linked executable for maximum portability
(a static executable only depends on the kernel and can be moved freely to
another different machine with the same operating system and CPU architecture).
Beware that this requires the libc to support static linking
(most notably, the glibc does not support static linking, the only way to build static executables
on linux is to link to a different libc implementation like musl libc).
###### enableFallback
Whether or not to allow the creation of the [fallback image](https://www.graalvm.org/22.0/reference-manual/native-image/Limitations/)
when native-image configuration is missing.
###### graalVmHome
This points to the installation folder of GraalVM, defaults to the installation directory
of the selected Gradle toolchain (if any) or the installation directory
of the JVM Gradle is running on otherwise.
#### Customize native-image command line
A [native-image.properties](https://www.graalvm.org/22.0/reference-manual/native-image/BuildConfiguration/)
file can be added to `${project.projectDir}/native-image` to add custom parameters:
```bash
-H:Optimize=3 --initialize-at-run-time=your.main.class --gc=G1
```
#### Limitations
GraalVM with the [native-image](https://www.graalvm.org/22.0/reference-manual/native-image/) plugin is required in order
for these plugins to work, this can be achieved either running Gradle under GraalVM directly or using Gradle toolchains
support to request GraalVM at the project level
```groovy
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.GRAAL_VM
}
}
```

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,53 @@
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);
});
}
}

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

@@ -0,0 +1,84 @@
package net.woggioni.gradle.graalvm;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaApplication;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
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;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.process.CommandLineArgumentProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
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 {
@Input
public abstract Property<Boolean> getMergeConfiguration();
@OutputDirectory
public abstract DirectoryProperty getConfigurationDir();
public NativeImageConfigurationTask() {
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();
TaskContainer taskContainer = getProject().getTasks();
JavaApplication javaApplication = getProject().getExtensions().findByType(JavaApplication.class);
JavaPluginExtension javaExtension = getProject().getExtensions().getByType(JavaPluginExtension.class);
ExtensionContainer ext = getProject().getExtensions();
Property<JavaLauncher> javaLauncherProperty = getJavaLauncher();
Optional.ofNullable(ext.findByType(JavaToolchainService.class))
.flatMap(ts -> Optional.ofNullable(javaExtension.getToolchain()).map(ts::launcherFor))
.ifPresent(javaLauncherProperty::set);
if(!Objects.isNull(javaApplication)) {
getMainClass().convention(javaApplication.getMainClass());
getMainModule().convention(javaApplication.getMainModule());
}
getConfigurationDir().convention(layout.getProjectDirectory()
.dir(NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME));
getMergeConfiguration().convention(true);
ConfigurationContainer cc = getProject().getConfigurations();
Configuration runtimeClasspath = cc.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
setClasspath(runtimeClasspath);
Provider<Jar> jarTaskProvider = taskContainer.named(JavaPlugin.JAR_TASK_NAME, Jar.class);
setClasspath(
getProject().files(
jarTaskProvider,
runtimeClasspath
)
);
getJvmArgumentProviders().add(new CommandLineArgumentProvider() {
@Override
public Iterable<String> asArguments() {
List<String> jvmArgs = new ArrayList<>();
if(getMergeConfiguration().get()) {
jvmArgs.add("-agentlib:native-image-agent=config-merge-dir=" + getConfigurationDir().get());
} else {
jvmArgs.add("-agentlib:native-image-agent=config-output-dir=" + getConfigurationDir().get());
}
for(String jvmArg : Optional.ofNullable(javaApplication.getApplicationDefaultJvmArgs()).orElse(Collections.emptyList())) {
jvmArgs.add(jvmArg);
}
return jvmArgs;
}
});
}
}

View File

@@ -0,0 +1,59 @@
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;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.jvm.tasks.Jar;
import java.util.Optional;
public class NativeImagePlugin implements Plugin<Project> {
public static final String NATIVE_IMAGE_TASK_NAME = "nativeImage";
public static final String CONFIGURE_NATIVE_IMAGE_TASK_NAME = "configureNativeImage";
public static final String NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME = "native-image";
@Override
public void apply(Project project) {
project.getPluginManager().apply(JavaLibraryPlugin.class);
ProjectLayout layout = project.getLayout();
ExtensionContainer extensionContainer = project.getExtensions();
JavaApplication javaApplicationExtension =
Optional.ofNullable(extensionContainer.findByType(JavaApplication.class))
.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(
String.format("META-INF/native-image/%s/%s/",
project.getName(),
project.getGroup()
)
);
});
});
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);
});
}
}

View File

@@ -0,0 +1,138 @@
package net.woggioni.gradle.graalvm;
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.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.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.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static java.util.Optional.ofNullable;
import static net.woggioni.gradle.graalvm.Constants.GRAALVM_TASK_GROUP;
public abstract class NativeImageTask extends Exec {
@Classpath
public abstract Property<FileCollection> getClasspath();
@InputDirectory
public abstract DirectoryProperty getGraalVmHome();
@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
public abstract Property<String> getMainClass();
@Input
@Optional
public abstract Property<String> getMainModule();
@Inject
protected abstract JavaModuleDetector getJavaModuleDetector();
@OutputFile
protected abstract RegularFileProperty getOutputFile();
private final Logger logger;
public NativeImageTask() {
Project project = getProject();
logger = project.getLogger();
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);
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);
BasePluginExtension basePluginExtension =
ext.getByType(BasePluginExtension.class);
getOutputFile().convention(basePluginExtension.getLibsDirectory().file(project.getName()));
Object executableProvider = new Object() {
@Override
public String toString() {
return getGraalVmHome().get() + "/bin/native-image";
}
};
executable(executableProvider);
CommandLineArgumentProvider argumentProvider = new CommandLineArgumentProvider() {
@Override
public Iterable<String> asArguments() {
List<String> result = new ArrayList<>();
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();
FileCollection cp = javaModuleDetector.inferClasspath(useJpms, classpath);
FileCollection mp = javaModuleDetector.inferModulePath(useJpms, classpath);
if(!cp.isEmpty()) {
result.add("-cp");
result.add(cp.getAsPath());
}
if(!mp.isEmpty()) {
result.add("-p");
result.add(mp.getAsPath());
}
result.add("-o");
result.add(getOutputFile().get().getAsFile().toString());
result.add(getMainClass().get());
return Collections.unmodifiableList(result);
}
};
getArgumentProviders().add(argumentProvider);
}
}

View File

@@ -1,8 +1,8 @@
woggioniMavenRepositoryUrl=https://mvn.woggioni.net/
lys.catalog.version=2023.06.11
lys.catalog.version=2024.02.12
version.myGradlePlugins=2023.06.13
version.myGradlePlugins=2024.03.11
version.gradle=7.6
version.felix.config.admin=1.9.26
version.felix=7.0.5

View File

@@ -1,8 +1,18 @@
package net.woggioni.gradle.lombok;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.JavaExec;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.SourceSet;
import org.gradle.internal.jvm.JavaModuleDetector;
import javax.inject.Inject;
import java.io.File;
@@ -10,12 +20,10 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Delombok extends JavaExec {
@Getter
@OutputDirectory
private final File outputDir;
@RequiredArgsConstructor(onConstructor_ = {@Inject})
public abstract class Delombok extends JavaExec {
private final JavaModuleDetector javaModuleDetector;
private static String buildClasspathString(Iterable<File> classpath) {
StringBuilder sb = new StringBuilder();
Iterator<File> it = classpath.iterator();
@@ -33,19 +41,56 @@ public class Delombok extends JavaExec {
return sb.toString();
}
@Inject
public Delombok(File lombokJar, File outputDir, Iterable<File> sourceDirs, String classpath) {
this.outputDir = outputDir;
classpath(lombokJar);
@InputFiles
public Provider<FileCollection> getSourceClasspath() {
return getSourceSet()
.map(SourceSet::getCompileClasspath);
}
@Internal
abstract public Property<SourceSet> getSourceSet();
@Internal
abstract public Property<Configuration> getLombokJar();
@Internal
abstract public Property<Boolean> getInferModulePath();
@OutputDirectory
abstract public RegularFileProperty getOutputDir();
@InputFiles
public Provider<FileCollection> getInputFiles() {
return getSourceSet()
.map(SourceSet::getAllSource)
.map(SourceDirectorySet::getSourceDirectories)
.zip(getLombokJar(), FileCollection::plus);
}
@Override
public void exec() {
classpath(getLombokJar());
List<String> args = new ArrayList<>();
args.add("delombok");
args.add("-d");
args.add(outputDir.getPath());
args.add("-c");
args.add(classpath);
for(File sourceDir : sourceDirs) args.add(sourceDir.getPath());
args.add(getOutputDir().getAsFile().get().getPath());
SourceSet sourceSet = getSourceSet().get();
Boolean inferModulePath = getInferModulePath().get();
FileCollection classpath = javaModuleDetector.inferClasspath(inferModulePath, sourceSet.getCompileClasspath());
if(!classpath.isEmpty()) {
args.add("-c");
args.add(classpath.getAsPath());
}
if(inferModulePath) {
FileCollection modulepath = javaModuleDetector.inferModulePath(true, sourceSet.getCompileClasspath());
if(!modulepath.isEmpty()) {
args.add("--module-path");
args.add(modulepath.getAsPath());
}
}
for(File sourceDir : sourceSet.getJava().getSrcDirs()) {
args.add(sourceDir.getPath());
}
Object[] argsArray = new String[args.size()];
args.toArray(argsArray);
args(argsArray);
super.exec();
}
}

View File

@@ -1,13 +1,7 @@
package net.woggioni.gradle.lombok;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.gradle.api.provider.Property;
import javax.inject.Inject;
@RequiredArgsConstructor(onConstructor_ = { @Inject })
public class LombokExtension {
@Getter
private final Property<String> version;
abstract public class LombokExtension {
abstract public Property<String> getVersion();
}

View File

@@ -7,6 +7,7 @@ import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
@@ -28,76 +29,71 @@ public class LombokPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPluginManager().apply(JavaPlugin.class);
ObjectFactory objectFactory = project.getObjects();
LombokExtension ext = project.getExtensions()
.create("lombok", LombokExtension.class,
objectFactory.property(String.class)
.convention(project.provider(
() -> (String) project.getExtensions().getExtraProperties().get("version.lombok")))
);
project.afterEvaluate(p -> {
SourceSetContainer sourceSetContainer = project.getExtensions().findByType(JavaPluginExtension.class).getSourceSets();
Provider<Map<String, String>> dependencyNotationProvider = project.provider(() -> {
Map<String, String> m = new HashMap<>();
m.put("group", "org.projectlombok");
m.put("name", "lombok");
m.put("version", ext.getVersion().get());
return Collections.unmodifiableMap(m);
});
Configuration lombokConfiguration = project.getConfigurations().create("lombok");
project.getDependencies().addProvider(
lombokConfiguration.getName(),
LombokExtension ext = project.getExtensions().create("lombok", LombokExtension.class);
ExtraPropertiesExtension epe = project.getExtensions().getExtraProperties();
if(epe.has("version.lombok")) {
ext.getVersion().convention(
project.provider(() -> (String) epe.get("version.lombok"))
);
}
JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class);
SourceSetContainer sourceSetContainer = javaPluginExtension.getSourceSets();
Provider<Map<String, String>> dependencyNotationProvider = ext.getVersion().map((String version) -> {
Map<String, String> m = new HashMap<>();
m.put("group", "org.projectlombok");
m.put("name", "lombok");
m.put("version", version);
return Collections.unmodifiableMap(m);
});
Configuration lombokConfiguration = project.getConfigurations().create("lombok");
project.getDependencies().addProvider(
lombokConfiguration.getName(),
dependencyNotationProvider,
externalModuleDependency -> {
}
);
for (SourceSet ss : sourceSetContainer) {
DependencyHandler dependencyHandler = project.getDependencies();
dependencyHandler.addProvider(
ss.getCompileOnlyConfigurationName(),
dependencyNotationProvider,
externalModuleDependency -> {
}
);
for (SourceSet ss : sourceSetContainer) {
DependencyHandler dependencyHandler = project.getDependencies();
dependencyHandler.addProvider(
ss.getCompileOnlyConfigurationName(),
dependencyNotationProvider,
externalModuleDependency -> {
});
dependencyHandler.addProvider(
ss.getAnnotationProcessorConfigurationName(),
dependencyNotationProvider,
externalModuleDependency -> {
});
TaskContainer tasks = project.getTasks();
String javadocTaskName = ss.getJavadocTaskName();
Task javadocTask = tasks.findByName(javadocTaskName);
if(javadocTask != null) {
String delombokTaskName = "delombok" + ss.getName().substring(0, 1).toUpperCase() + ss.getName().substring(1);
File outputDir = new File(new File(project.getBuildDir(), "delombok"), ss.getName());
Javadoc javadoc = (Javadoc) javadocTask;
TaskProvider<Delombok> delombokTaskProvider = tasks.register(delombokTaskName,
Delombok.class,
lombokConfiguration.getSingleFile(),
outputDir,
ss.getJava().getSrcDirs(),
ss.getCompileClasspath().getAsPath()
);
delombokTaskProvider.configure(delombokTask -> {
delombokTask.getInputs().files(ss.getAllSource().getSourceDirectories());
});
javadoc.setSource(outputDir);
javadoc.getInputs().files(delombokTaskProvider);
dependencyHandler.addProvider(
ss.getAnnotationProcessorConfigurationName(),
dependencyNotationProvider,
externalModuleDependency -> {
});
TaskContainer tasks = project.getTasks();
String javadocTaskName = ss.getJavadocTaskName();
Task javadocTask = tasks.findByName(javadocTaskName);
if(javadocTask != null) {
String delombokTaskName = "delombok" + ss.getName().substring(0, 1).toUpperCase() + ss.getName().substring(1);
File outputDir = new File(new File(project.getBuildDir(), "delombok"), ss.getName());
Javadoc javadoc = (Javadoc) javadocTask;
TaskProvider<Delombok> delombokTaskProvider = tasks.register(delombokTaskName, Delombok.class, (delombok -> {
delombok.getSourceSet().set(ss);
delombok.getOutputDir().set(outputDir);
delombok.getLombokJar().set(lombokConfiguration);
delombok.getInferModulePath().set(javaPluginExtension.getModularity().getInferModulePath());
}));
javadoc.setSource(outputDir);
javadoc.getInputs().files(delombokTaskProvider);
}
}
JavaToolchainSpec toolchain = javaPluginExtension.getToolchain();
if(toolchain.getLanguageVersion().isPresent()) {
project.afterEvaluate((Project pro) -> {
if(toolchain.getLanguageVersion().get().asInt() >= 16) {
pro.getTasks().withType(JavaCompile.class, t -> {
t.getOptions().getForkOptions().getJvmArgs().add("--illegal-access=permit");
});
}
}
JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class);
JavaToolchainSpec toolchain = javaPluginExtension.getToolchain();
if(toolchain.getLanguageVersion().isPresent()) {
project.afterEvaluate((Project pro) -> {
if(toolchain.getLanguageVersion().get().asInt() >= 16) {
pro.getTasks().withType(JavaCompile.class, t -> {
t.getOptions().getForkOptions().getJvmArgs().add("--illegal-access=permit");
});
}
});
} else if(JavaVersion.current().compareTo(JavaVersion.VERSION_16) >= 0) {
project.getTasks().withType(JavaCompile.class, t -> {
t.getOptions().getForkOptions().getJvmArgs().add("--illegal-access=permit");
});
}
});
});
} else if(JavaVersion.current().compareTo(JavaVersion.VERSION_16) >= 0) {
project.getTasks().withType(JavaCompile.class, t -> {
t.getOptions().getForkOptions().getJvmArgs().add("--illegal-access=permit");
});
}
}
}

View File

@@ -1,6 +1,6 @@
plugins {
id 'maven-publish'
id 'groovy-gradle-plugin'
id 'java-gradle-plugin'
}
gradlePlugin {

View File

@@ -1,198 +0,0 @@
package net.woggioni.gradle.multi.release.jar
import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.java.TargetJvmVersion
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.gradle.process.CommandLineArgumentProvider
import java.lang.module.ModuleDescriptor
import java.util.jar.JarFile
import java.util.stream.Collectors
import java.util.zip.ZipFile
import static org.gradle.api.attributes.LibraryElements.JAR
import static org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE
class MultiReleaseJarPlugin implements Plugin<Project> {
private static void jpmsModuleName(File file) {
JarFile jarFile = new JarFile(file).with {
if (it.isMultiRelease()) {
new JarFile(
file,
false,
ZipFile.OPEN_READ,
Runtime.version()
)
} else {
it
}
}
String automaticModuleName = jarFile.manifest?.with {it.mainAttributes.getValue("Automatic-Module-Name") }
def moduleInfoEntry = jarFile.getJarEntry("module-info.class")
moduleInfoEntry
?.with(jarFile.&getInputStream)
?.withCloseable(ModuleDescriptor.&read) ?: automaticModuleName
ModuleDescriptor.read()
}
// @Canonical
static class CompilerArgumentProvider implements CommandLineArgumentProvider {
private final Project project
private final ObjectFactory objects
@Input
final Provider<String> jpmsModuleName
private final Map<String, ListProperty<String>> patchModules
@InputFiles
@CompileClasspath
final FileCollection sourceSetOutput
// @InputFiles
// FileCollection getPatchModules() {
// return project.files(patchModules.entrySet().stream().flatMap {
// it.getValue().get().stream()
// }.toArray(String::new))
// }
CompilerArgumentProvider(
Project project,
ObjectFactory objects,
Provider<String> jpmsModuleName,
Map<String, ListProperty<String>> patchModules,
FileCollection sourceSetOutput) {
this.project = project
this.objects = objects
this.jpmsModuleName = jpmsModuleName
this.patchModules = patchModules
this.sourceSetOutput = sourceSetOutput
}
@Override
Iterable<String> asArguments() {
Map<String, ListProperty<String>> patchModules = new HashMap<>(patchModules)
String name = jpmsModuleName.get()
if(name) {
patchModules.computeIfAbsent(name) {
objects.listProperty(String.class).convention(new ArrayList<String>())
}.addAll(sourceSetOutput.collect { it.toString() })
} else {
throw new GradleException("Missing property 'jpms.module.name'")
}
String sep = System.getProperty('path.separator')
List<String> result = new ArrayList<>()
for(Map.Entry<String, ListProperty<String>> entry : patchModules.entrySet()) {
String arg = entry.getValue().get().stream().collect(Collectors.joining(sep))
result += '--patch-module'
result += "${entry.getKey()}=${arg}"
}
result
}
}
@Override
void apply(Project project) {
project.pluginManager.apply(JavaPlugin)
MultiReleaseJarPluginExtension mrjpe = new MultiReleaseJarPluginExtension(project.objects)
project.extensions.add('multiReleaseJar', mrjpe)
JavaPluginExtension javaPluginExtension = project.extensions.findByType(JavaPluginExtension.class)
JavaVersion binaryVersion = javaPluginExtension.targetCompatibility ?: javaPluginExtension.toolchain?.with {
it.languageVersion.get()
} ?: JavaVersion.current()
if(binaryVersion > JavaVersion.VERSION_1_8) {
Configuration compileClasspathConfiguration = project.configurations.compileClasspath
project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) {
attributes {
attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.class, JAR))
}
}
SourceSet mainSourceSet = (project.sourceSets.main as SourceSet)
JavaCompile compileJavaTask = project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile).get()
compileJavaTask.configure {
options.release.set(JavaVersion.VERSION_1_8.majorVersion.toInteger())
}
Jar jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar).get()
jarTask.configure {
manifest.attributes('Multi-Release': 'true')
}
ArrayList<FileCollection> compileOutputs = new ArrayList<>()
compileOutputs << compileJavaTask.outputs.files
ArrayList<FileCollection> sourcePaths = new ArrayList<>()
sourcePaths << mainSourceSet.java.sourceDirectories
Arrays.stream(JavaVersion.values()).filter {
it > JavaVersion.VERSION_1_8 && it <= binaryVersion
}.forEach {javaVersion ->
SourceDirectorySet sourceDirectorySet =
project.objects.sourceDirectorySet("java${javaVersion.majorVersion}", javaVersion.toString())
sourceDirectorySet.with {
srcDir(new File(project.projectDir, "src/${mainSourceSet.name}/${sourceDirectorySet.name}"))
destinationDirectory.set(new File(project.buildDir, "classes/${mainSourceSet.name}/${sourceDirectorySet.name}"))
sourcePaths << sourceDirectories
}
new DslObject(mainSourceSet).getConvention().getPlugins().put(sourceDirectorySet.name, sourceDirectorySet)
mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.name, sourceDirectorySet)
TaskProvider<JavaCompile> compileTask = project.tasks.register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.majorVersion, JavaCompile, { javaCompileTask ->
javaCompileTask.options.release.set(javaVersion.majorVersion.toInteger())
javaCompileTask.classpath = compileClasspathConfiguration + compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get()
javaCompileTask.options.compilerArgumentProviders.add(
new CompilerArgumentProvider(
project,
project.objects,
project.provider {
project.hasProperty("jpms.module.name") ?
project.property("jpms.module.name") : null
},
mrjpe.patchModules,
mainSourceSet.output
)
)
javaCompileTask.source = sourceDirectorySet
javaCompileTask.destinationDirectory.set(sourceDirectorySet.destinationDirectory)
javaCompileTask.options.annotationProcessorPath = mainSourceSet.annotationProcessorPath
javaCompileTask.modularity.inferModulePath = javaPluginExtension.modularity.inferModulePath
javaCompileTask.options.sourcepath = sourcePaths.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get()
javaCompileTask.javaCompiler = compileJavaTask.javaCompiler
})
compileOutputs << compileTask.get().outputs.files
sourceDirectorySet.compiledBy(compileTask, { it.getDestinationDirectory()})
jarTask.configure {
from(compileTask.get().destinationDirectory) {
into("META-INF/versions/${javaVersion.majorVersion}")
}
}
}
SourceSet testSourceSet = (project.sourceSets.test as SourceSet)
testSourceSet.compileClasspath += compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get()
testSourceSet.runtimeClasspath += compileOutputs.stream().reduce { fc1, fc2 -> fc1 + fc2 }.get()
["apiElements", "runtimeElements"].forEach { String name ->
Configuration conf = project.configurations.getByName(name)
conf.attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, compileJavaTask.options.release.get())
}
}
}
}
}

View File

@@ -0,0 +1,219 @@
package net.woggioni.gradle.multi.release.jar;
import lombok.Getter;
import org.gradle.api.GradleException;
import org.gradle.api.JavaVersion;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.java.TargetJvmVersion;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.internal.plugins.DslObject;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.CompileClasspath;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.jvm.tasks.Jar;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
import org.gradle.jvm.toolchain.JavaToolchainSpec;
import org.gradle.process.CommandLineArgumentProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static org.gradle.api.attributes.LibraryElements.JAR;
import static org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE;
public class MultiReleaseJarPlugin implements Plugin<Project> {
// @SneakyThrows
// private static void jpmsModuleName(File file) {
// JarFile jarFile = new JarFile(file);
// if (jarFile.isMultiRelease()) {
// jarFile = new JarFile(
// file,
// false,
// ZipFile.OPEN_READ,
// Runtime.version()
// );
// }
// String automaticModuleName = Optional.ofNullable(jarFile.getManifest())
// .map(Manifest::getMainAttributes)
// .map(mainAttr -> mainAttr.getValue("Automatic-Module-Name"))
// .orElse(null);
// Optional<JarEntry> moduleInfoEntry = Optional.ofNullable(jarFile.getJarEntry("module-info.class"));
// moduleInfoEntry
// .map(jarFile::getInputStream)
// .map(ModuleDescriptor::read)
// .orElse(automaticModuleName);
// }
static class CompilerArgumentProvider implements CommandLineArgumentProvider {
@Getter(onMethod_ = {@Input})
private final Provider<String> jpmsModuleName;
@Getter(onMethod_ = {@Input})
private final MapProperty<String, List<String>> patchModules;
@Getter(onMethod_ = {@InputFiles, @CompileClasspath})
private final FileCollection sourceSetOutput;
public CompilerArgumentProvider(
Provider<String> jpmsModuleName,
MapProperty<String, List<String>> patchModules,
FileCollection sourceSetOutput) {
this.jpmsModuleName = jpmsModuleName;
this.patchModules = patchModules;
this.sourceSetOutput = sourceSetOutput;
}
@Override
public Iterable<String> asArguments() {
Map<String, List<String>> patchModules = new TreeMap<>(this.patchModules.get());
if (jpmsModuleName.isPresent()) {
String name = jpmsModuleName.get();
patchModules.computeIfAbsent(name, k -> new ArrayList<>())
.addAll(
StreamSupport.stream(sourceSetOutput.spliterator(), false)
.map(File::toString)
.collect(Collectors.toList())
);
} else {
throw new GradleException("Missing property 'jpms.module.name'");
}
String sep = System.getProperty("path.separator");
List<String> result = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : patchModules.entrySet()) {
String arg = String.join(sep, entry.getValue());
result.add("--patch-module");
result.add(entry.getKey() + "=" + arg);
}
return result;
}
}
@Override
public void apply(Project project) {
project.getPluginManager().apply(JavaPlugin.class);
MultiReleaseJarPluginExtension mrjpe = project.getObjects().newInstance(MultiReleaseJarPluginExtension.class);
project.getExtensions().add("multiReleaseJar", mrjpe);
JavaPluginExtension javaPluginExtension = project.getExtensions().findByType(JavaPluginExtension.class);
SourceSetContainer ssc = javaPluginExtension.getSourceSets();
JavaVersion binaryVersion = Optional.ofNullable(javaPluginExtension.getTargetCompatibility())
.or(() -> Optional.ofNullable(javaPluginExtension.getToolchain())
.map(JavaToolchainSpec::getLanguageVersion)
.filter(Property::isPresent)
.map(Property::get)
.map(JavaLanguageVersion::asInt)
.map(JavaVersion::toVersion)
).orElseGet(JavaVersion::current);
if (Comparator.<JavaVersion>naturalOrder().compare(binaryVersion, JavaVersion.VERSION_1_8) > 0) {
Configuration compileClasspathConfiguration = project.getConfigurations()
.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME);
project.getConfigurations().named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, cfg -> {
cfg.attributes(attr -> {
attr.attribute(LIBRARY_ELEMENTS_ATTRIBUTE, project.getObjects().named(LibraryElements.class, JAR));
});
});
SourceSet mainSourceSet = ssc.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
JavaCompile compileJavaTask = project.getTasks()
.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class, (JavaCompile t) -> {
t.getOptions().getRelease().set(Integer.parseInt(JavaVersion.VERSION_1_8.getMajorVersion()));
}).get();
Jar jarTask = project.getTasks().named(JavaPlugin.JAR_TASK_NAME, Jar.class, (Jar t) -> {
Map<String, String> attrs = new HashMap<>();
attrs.put("Multi-Release", "true");
t.getManifest().attributes(attrs);
}).get();
SourceDirectorySet java = mainSourceSet.getJava();
ArrayList<FileCollection> compileOutputs = new ArrayList<>();
compileOutputs.add(compileJavaTask.getOutputs().getFiles());
ArrayList<FileCollection> sourcePaths = new ArrayList<>();
sourcePaths.add(java.getSourceDirectories());
Comparator<JavaVersion> cmp = Comparator.naturalOrder();
Arrays.stream(JavaVersion.values())
.filter((JavaVersion jv) -> cmp.compare(jv, JavaVersion.VERSION_1_8) > 0 && cmp.compare(jv, binaryVersion) <= 0)
.forEach(javaVersion -> {
SourceDirectorySet sourceDirectorySet = project.getObjects()
.sourceDirectorySet("java" + javaVersion.getMajorVersion(), javaVersion.toString());
sourceDirectorySet.srcDir(new File(project.getProjectDir(),
"src/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName()));
sourceDirectorySet.getDestinationDirectory().set(
new File(project.getBuildDir(),
"classes/" + mainSourceSet.getName() + "/" + sourceDirectorySet.getName())
);
sourcePaths.add(sourceDirectorySet.getSourceDirectories());
new DslObject(mainSourceSet).getConvention().getPlugins().put(sourceDirectorySet.getName(), sourceDirectorySet);
mainSourceSet.getExtensions().add(SourceDirectorySet.class, sourceDirectorySet.getName(), sourceDirectorySet);
TaskProvider<JavaCompile> compileTask =
project.getTasks().register(JavaPlugin.COMPILE_JAVA_TASK_NAME + javaVersion.getMajorVersion(), JavaCompile.class,
(JavaCompile javaCompileTask) -> {
javaCompileTask.getOptions().getRelease().set(Integer.parseInt(javaVersion.getMajorVersion()));
javaCompileTask.setClasspath(compileClasspathConfiguration.plus(
compileOutputs.stream().reduce(project.getObjects().fileCollection(), FileCollection::plus)));
javaCompileTask.getOptions().getCompilerArgumentProviders().add(
new CompilerArgumentProvider(
project.provider(() -> Optional.of("jpms.module.name")
.filter(project::hasProperty)
.map(project::property)
.map(Object::toString)
.orElse(null)),
mrjpe.getPatchModules(),
mainSourceSet.getOutput()
)
);
javaCompileTask.source(sourceDirectorySet);
javaCompileTask.getDestinationDirectory().set(sourceDirectorySet.getDestinationDirectory());
javaCompileTask.getOptions().setAnnotationProcessorPath(mainSourceSet.getAnnotationProcessorPath());
javaCompileTask.getModularity().getInferModulePath().set(javaPluginExtension.getModularity().getInferModulePath());
javaCompileTask.getOptions().setSourcepath(sourcePaths.stream().reduce(project.files(), FileCollection::plus));
javaCompileTask.getJavaCompiler().set(compileJavaTask.getJavaCompiler());
});
compileOutputs.add(compileTask.get().getOutputs().getFiles());
sourceDirectorySet.compiledBy(compileTask, AbstractCompile::getDestinationDirectory);
jarTask.from(compileTask.get().getDestinationDirectory(), copySpec -> {
copySpec.into("META-INF/versions/" + javaVersion.getMajorVersion());
});
});
SourceSet testSourceSet = ssc.getByName(SourceSet.TEST_SOURCE_SET_NAME);
testSourceSet.setCompileClasspath(
testSourceSet.getCompileClasspath().plus(compileOutputs.stream().reduce(project.files(), FileCollection::plus))
);
testSourceSet.setRuntimeClasspath(
testSourceSet.getRuntimeClasspath().plus(compileOutputs.stream().reduce(project.files(), FileCollection::plus))
);
Arrays.asList("apiElements", "runtimeElements").forEach((String name) -> {
Configuration conf = project.getConfigurations().getByName(name);
conf.attributes(attrs -> {
attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
compileJavaTask.getOptions().getRelease().get());
});
});
}
}
}

View File

@@ -1,33 +1,41 @@
package net.woggioni.gradle.multi.release.jar;
import org.gradle.api.Project;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Provider;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
public class MultiReleaseJarPluginExtension {
private final ObjectFactory objects;
private final Map<String, ListProperty<String>> patchModules;
private final Provider<List<String>> listCtor;
private final MapProperty<String, List<String>> patchModules;
@Inject
public MultiReleaseJarPluginExtension(ObjectFactory objects) {
this.objects = objects;
patchModules = new HashMap<>();
public MultiReleaseJarPluginExtension(Project project, ObjectFactory objects) {
patchModules = objects.mapProperty(String.class,
(Class<List<String>>) ((List<String>) new ArrayList<String>()).getClass());
listCtor = project.provider(ArrayList::new);
}
private static <T> List<T> listAdd(List<T> l, T el) {
l.add(el);
return l;
}
public void patchModule(String moduleName, Provider<String> path) {
this.patchModules
.computeIfAbsent(moduleName, key -> objects.listProperty(String.class)
.convention(new ArrayList<>()))
.add(path);
Provider<List<String>> listProvider = this.patchModules.getting(moduleName);
if(listProvider.isPresent()) {
patchModules.put(moduleName, listProvider.zip(path, MultiReleaseJarPluginExtension::listAdd));
} else {
patchModules.put(moduleName, listCtor.zip(path, MultiReleaseJarPluginExtension::listAdd));
}
}
public Map<String, ListProperty<String>> getPatchModules() {
public MapProperty<String, List<String>> getPatchModules() {
return patchModules;
}
}

View File

@@ -18,7 +18,9 @@ dependencies {
}
jar {
bnd '''\
Import-Package: !lombok, *
'''
bundle {
bnd '''\
Import-Package: !lombok, *
'''
}
}

16
sambal/build.gradle Normal file
View File

@@ -0,0 +1,16 @@
plugins {
id 'java-gradle-plugin'
}
dependencies {
implementation catalog.jgit
}
gradlePlugin {
plugins {
create("SambalPlugin") {
id = "net.woggioni.gradle.sambal"
implementationClass = "net.woggioni.gradle.sambal.SambalPlugin"
}
}
}

View File

@@ -0,0 +1,232 @@
package net.woggioni.gradle.sambal;
import lombok.SneakyThrows;
import org.codehaus.groovy.runtime.MethodClosure;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.lib.Ref;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.AttributeContainer;
import org.gradle.api.java.archives.Attributes;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.AppliedPlugin;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.PluginManager;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.bundling.Jar;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SambalPlugin implements Plugin<Project> {
private static Pattern tagPattern = Pattern.compile("^refs/tags/v?(\\d+\\.\\d+.*)");
final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
private static final String currentTagCachedKey = "CURRENT_TAG_CACHED";
private static final String currentRevCachedKey = "CURRENT_REV_CACHED";
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private static void copyConfigurationAttributes(Configuration source, Configuration destination) {
destination.attributes(new Action<AttributeContainer>() {
@Override
public void execute(@Nonnull AttributeContainer destinationAttributes) {
AttributeContainer sourceAttributes = source.getAttributes();
for (Attribute attr : sourceAttributes.keySet()) {
destinationAttributes.attribute(attr, Objects.requireNonNull(sourceAttributes.getAttribute(attr)));
}
}
});
}
@SneakyThrows
private static String sha256(File f) {
byte[] buffer = new byte[0x10000];
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (InputStream inputStream = new DigestInputStream(new FileInputStream(f), md)) {
while (true) {
int read = inputStream.read(buffer);
if (read < 0) break;
md.update(buffer, 0, read);
}
}
return bytesToHex(md.digest());
}
private static Optional<Path> which(String command) {
return Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator)))
.map(path -> Paths.get(path, command))
.filter(Files::exists)
.filter(Files::isExecutable)
.findFirst();
}
private static int getVersionInt(Project project) {
int result = 0;
int scale = 100 * 100;
String versionString = project.getVersion().toString();
int dash = versionString.indexOf('-');
if (dash < 0) {
dash = versionString.length();
}
versionString = versionString.substring(0, dash);
for (String part : versionString.split("\\.")) {
if (scale >= 1) {
result += scale * Integer.parseInt(part);
scale /= 100;
}
}
return result;
}
@SneakyThrows
private static String runCommand(String... args) {
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
Process p = pb.start();
p.waitFor();
if (p.exitValue() != 0) throw new GradleException("Error invoking subprocess");
StringWriter sw = new StringWriter();
char[] buffer = new char[0x1024];
try (Reader reader = new InputStreamReader(p.getInputStream())) {
while (true) {
int read = reader.read(buffer);
if (read <= 0) break;
sw.write(buffer, 0, read);
}
}
return sw.toString();
}
@SneakyThrows
private static String getCurrentTag(Git git, Logger logger) {
List<Ref> tags = git.tagList().call();
Ref currentRef = git.getRepository().findRef("HEAD");
List<String> currentTag = tags.stream()
.filter(it -> Objects.equals(it.getObjectId(), currentRef.getObjectId()))
.map(it -> tagPattern.matcher(it.getName())).filter(Matcher::matches)
.map(it -> it.group(1))
.collect(Collectors.toList());
if (currentTag.isEmpty()) return null;
else {
if (currentTag.size() > 1) {
logger.warn("Found more than one tag in correct format for HEAD.");
}
return currentTag.get(0);
}
}
@SneakyThrows
private static String getCurrentTag(Project project) {
ExtraPropertiesExtension ext = project.getRootProject().getExtensions().getExtraProperties();
if (!ext.has(currentTagCachedKey)) {
Git git = Git.open(project.getRootDir());
Status status = git.status().call();
String currentTag;
if (status.isClean() && (currentTag = getCurrentTag(git, project.getLogger())) != null) {
ext.set(currentTagCachedKey, currentTag);
} else {
ext.set(currentTagCachedKey, null);
}
}
return Optional.ofNullable(ext.get(currentTagCachedKey))
.map(Object::toString)
.orElse(null);
}
@SneakyThrows
private static String getGitRevision(Project project) {
ExtraPropertiesExtension ext = project.getRootProject().getExtensions().getExtraProperties();
if (!ext.has(currentRevCachedKey)) {
Git git = Git.open(project.getRootDir());
ext.set(currentRevCachedKey, git.getRepository().findRef("HEAD").getObjectId().name());
}
return Optional.ofNullable(ext.get(currentRevCachedKey))
.map(Object::toString)
.orElse(null);
}
private static String resolveProperty(Project project, String key, String defaultValue) {
if (project.hasProperty(key)) return project.property(key).toString();
else {
String envVarKey = key.replace('.', '_').toUpperCase();
String envVarValue = System.getenv().get(envVarKey);
if (envVarValue != null) return envVarValue;
else return defaultValue;
}
}
private static String resolveProperty(Project project, String key) {
if (project.hasProperty(key)) return project.property(key).toString();
else {
String envVarKey = key.replace('.', '_').toUpperCase();
String envVarValue = System.getenv().get(envVarKey);
if (envVarValue != null) return envVarValue;
else {
String msg = String.format("Impossible to resolve property '%s'," +
" either add it to Gradle's project properties from the command line using:\n" +
"./gradlew -P%s=someValue\n" +
"or set the environmental variable %s", key, key, envVarKey);
throw new GradleException(msg);
}
}
}
@Override
public void apply(Project project) {
ExtraPropertiesExtension ext = project.getRootProject().getExtensions().getExtraProperties();
ext.set("getIntegerVersion", new MethodClosure(this, "getVersionInt").curry(project));
ext.set("currentTag", getCurrentTag(project));
ext.set("resolveProperty", new MethodClosure(this, "resolveProperty").curry(project));
ext.set("copyConfigurationAttributes", new MethodClosure(this, "copyConfigurationAttributes"));
final String gitRevision = getGitRevision(project);
ext.set("gitRevision", gitRevision);
PluginManager pluginManager = project.getPluginManager();
pluginManager.withPlugin("java-library", (AppliedPlugin plugin) -> {
TaskContainer tasks = project.getTasks();
project.afterEvaluate(p -> {
tasks.named(JavaPlugin.JAR_TASK_NAME, Jar.class, jarTask -> {
jarTask.manifest(mf -> {
Attributes attrs = mf.getAttributes();
attrs.put(java.util.jar.Attributes.Name.SPECIFICATION_TITLE.toString(), project.getName());
attrs.put(java.util.jar.Attributes.Name.SPECIFICATION_VERSION.toString(), project.getVersion());
attrs.put(java.util.jar.Attributes.Name.IMPLEMENTATION_VERSION.toString(), gitRevision);
});
});
});
});
}
}

View File

@@ -0,0 +1,86 @@
package net.woggioni.gradle.sambal;
import org.codehaus.groovy.ant.Groovy;
import org.codehaus.groovy.util.StringUtil;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFile;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.bundling.Jar;
import javax.inject.Inject;
import java.io.File;
import java.util.Map;
import java.util.TreeMap;
abstract class SignJarTask extends DefaultTask {
@InputFile
public abstract RegularFileProperty getInputJarFile();
@Input
public abstract MapProperty<String,String> getOptions();
@Input
public abstract DirectoryProperty getArchiveDestination();
@Input
public abstract Property<String> getArchiveBaseName();
@Input
public abstract Property<String> getArchiveVersion();
@Input
public abstract Property<String> getArchiveExtension();
@Input
@Optional
public abstract Property<String> getArchiveClassifier();
@OutputFile
public Provider<RegularFile> getArchiveFile() {
StringBuilder sb = new StringBuilder(getArchiveBaseName().get());
sb.append('-').append(getArchiveVersion().get());
if(getArchiveClassifier().isPresent() && !getArchiveClassifier().get().isEmpty()) {
sb.append('-').append(getArchiveClassifier().get());
}
sb.append('.').append(getArchiveExtension().get());
return getProject().getLayout().file(getArchiveDestination().map(ad ->
new File(ad.getAsFile(), sb.toString())
));
}
@Inject
public SignJarTask() {
BasePluginExtension bpe = getProject()
.getExtensions()
.getByType(BasePluginExtension.class);
getArchiveDestination().convention(bpe.getLibsDirectory());
getArchiveBaseName().convention(getProject().getName());
getArchiveVersion().convention(getProject().getVersion().toString());
getArchiveExtension().convention("jar");
getArchiveClassifier().convention("signed");
getArchiveClassifier().set("signed");
getInputJarFile().convention(
getProject().getTasks()
.named(JavaPlugin.JAR_TASK_NAME, Jar.class)
.flatMap(Jar::getArchiveFile));
}
@TaskAction
public void run() {
Map<String, String> signingOptions = new TreeMap<>(getOptions().get());
signingOptions.put("jar", getInputJarFile().map(RegularFile::getAsFile).get().toString());
signingOptions.put("signedJar", getArchiveFile().get().getAsFile().toString());
getAnt().invokeMethod("signjar", signingOptions);
}
}

View File

@@ -0,0 +1,54 @@
package net.woggioni.gradle.sambal.attribute;
import org.gradle.api.Named;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.AttributeCompatibilityRule;
import org.gradle.api.attributes.AttributeDisambiguationRule;
import org.gradle.api.attributes.CompatibilityCheckDetails;
import org.gradle.api.attributes.MultipleCandidatesDetails;
import org.gradle.api.internal.ReusableAction;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
public interface Sealing extends Named {
Attribute<Sealing> SEALING_ATTRIBUTE = Attribute.of("net.woggioni.gradle.lys.artifact.type", Sealing.class);
String sealed = "sealed";
String open = "open";
class CompatibilityRules implements AttributeCompatibilityRule<Sealing>, ReusableAction {
public void execute(CompatibilityCheckDetails<Sealing> details) {
Sealing consumerValue = details.getConsumerValue();
Sealing producerValue = details.getProducerValue();
if (consumerValue == null) {
details.compatible();
} else if(producerValue == null) {
details.incompatible();
} else if(Objects.equals(consumerValue.getName(), producerValue.getName())) {
details.compatible();
} else {
details.incompatible();
}
}
}
class DisambiguationRules implements AttributeDisambiguationRule<Sealing>, ReusableAction {
private static final List<String> ORDER = Arrays.asList(open, sealed);
private static final Comparator<Sealing> comparator =
Comparator.comparingInt(sealing -> ORDER.indexOf(sealing.getName()));
@Override
public void execute(MultipleCandidatesDetails<Sealing> details) {
if(details.getConsumerValue() == null) {
details.closestMatch(null);
} else {
details.getCandidateValues().stream()
.min(comparator)
.ifPresent(details::closestMatch);
}
}
}
}

View File

@@ -0,0 +1,54 @@
package net.woggioni.gradle.sambal.attribute;
import org.gradle.api.Named;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.attributes.AttributeCompatibilityRule;
import org.gradle.api.attributes.AttributeDisambiguationRule;
import org.gradle.api.attributes.CompatibilityCheckDetails;
import org.gradle.api.attributes.MultipleCandidatesDetails;
import org.gradle.api.internal.ReusableAction;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
public interface Signing extends Named {
Attribute<Signing> SIGNING_ATTRIBUTE = Attribute.of("net.woggioni.gradle.lys.artifact.signing", Signing.class);
String signed = "signed";
String unsigned = "unsigned";
class CompatibilityRules implements AttributeCompatibilityRule<Signing>, ReusableAction {
public void execute(CompatibilityCheckDetails<Signing> details) {
Signing consumerValue = details.getConsumerValue();
Signing producerValue = details.getProducerValue();
if (consumerValue == null) {
details.compatible();
} else if(producerValue == null) {
details.incompatible();
} else if(Objects.equals(consumerValue.getName(), producerValue.getName())) {
details.compatible();
} else {
details.incompatible();
}
}
}
class DisambiguationRules implements AttributeDisambiguationRule<Signing>, ReusableAction {
private static final List<String> ORDER = Arrays.asList(unsigned, signed);
private static final Comparator<Signing> comparator =
Comparator.comparingInt(signing -> ORDER.indexOf(signing.getName()));
@Override
public void execute(MultipleCandidatesDetails<Signing> details) {
if(details.getConsumerValue() == null) {
details.closestMatch(null);
} else {
details.getCandidateValues().stream()
.min(comparator)
.ifPresent(details::closestMatch);
}
}
}
}

View File

@@ -10,7 +10,6 @@ dependencyResolutionManagement {
versionCatalogs {
catalog {
from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.catalog.version')
version("slf4j", "1.7.36")
}
}
}
@@ -26,3 +25,5 @@ include 'osgi-app:osgi-simple-bootstrapper'
include 'osgi-app:osgi-simple-bootstrapper-api'
include 'osgi-app:osgi-simple-bootstrapper-application'
include 'wildfly'
include 'sambal'
include 'graalvm'

View File

@@ -1,19 +1,16 @@
package net.woggioni.gradle.wildfly;
import lombok.Getter;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Exec;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.Arrays;
public class Deploy2WildflyTask extends Exec {
public abstract class Deploy2WildflyTask extends Exec {
private static final String PROPERTY_PREFIX = "net.woggioni.gradle.wildfly.";
private static final String HOST_PROPERTY_KEY = PROPERTY_PREFIX + "rpcHost";
private static final String PORT_PROPERTY_KEY = PROPERTY_PREFIX + "rpcPort";
@@ -21,24 +18,21 @@ public class Deploy2WildflyTask extends Exec {
private static final String PASSWORD_PROPERTY_KEY = PROPERTY_PREFIX + "rpcPassword";
@Input
@Getter
private final Property<String> rpcHost;
public abstract Property<String> getRpcHost();
@Input
@Getter
private final Property<Integer> rpcPort;
public abstract Property<Integer> getRpcPort();
@Input
@Getter
private final Property<String> rpcUsername;
public abstract Property<String> getRpcUsername();
@Input
@Getter
private final Property<String> rpcPassword;
public abstract Property<String> getRpcPassword();
@Input
public abstract Property<String> getDeploymentName();
@Getter
@InputFile
private final RegularFileProperty artifact;
public abstract RegularFileProperty getArtifact();
private String projectProperty(String key, String defaultValue) {
String result = (String) getProject().findProperty(key);
@@ -46,7 +40,7 @@ public class Deploy2WildflyTask extends Exec {
}
@Inject
public Deploy2WildflyTask(@Nonnull ObjectFactory objectFactory) {
public Deploy2WildflyTask() {
setGroup("deploy");
setDescription("Deploy this project artifact to Wildfly application server");
Provider<String> defaultHostProvider = getProject()
@@ -59,19 +53,21 @@ public class Deploy2WildflyTask extends Exec {
.provider(() -> projectProperty(PASSWORD_PROPERTY_KEY, "password"));
executable("/opt/wildfly/bin/jboss-cli.sh");
rpcHost = objectFactory.property(String.class).convention(defaultHostProvider);
rpcPort = objectFactory.property(Integer.class).convention(defaultPortProvider);
rpcUsername = objectFactory.property(String.class).convention(defaultUsernameProvider);
rpcPassword = objectFactory.property(String.class).convention(defaultPasswordProvider);
artifact = objectFactory.fileProperty();
getRpcHost().convention(defaultHostProvider);
getRpcPort().convention(defaultPortProvider);
getRpcUsername().convention(defaultUsernameProvider);
getRpcPassword().convention(defaultPasswordProvider);
getDeploymentName().convention(getArtifact().map(it -> it.getAsFile().getName()));
getArgumentProviders().add(() ->
Arrays.asList(
"--controller=" + rpcHost.get() + ":" + rpcPort.get(),
"--controller=" + getRpcHost().get() + ":" + getRpcPort().get(),
"--connect",
"--user=" + rpcUsername.get(),
"--password=" + rpcPassword.get(),
"--command=deploy " + artifact.getAsFile().get().getPath() + " --force")
"--user=" + getRpcUsername().get(),
"--password=" + getRpcPassword().get(),
"--command=deploy "
+ getArtifact().getAsFile().get().getPath()
+ " --name=" + getDeploymentName().get()
+ " --force")
);
}
}