added sambal plugin and native image plugin
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
woggioniMavenRepositoryUrl=https://mvn.woggioni.net/
|
woggioniMavenRepositoryUrl=https://mvn.woggioni.net/
|
||||||
|
|
||||||
lys.catalog.version=2023.07.16
|
lys.catalog.version=2023.10.01
|
||||||
|
|
||||||
version.myGradlePlugins=2023.07.16
|
version.myGradlePlugins=2023.10.05
|
||||||
version.gradle=7.6
|
version.gradle=7.6
|
||||||
version.felix.config.admin=1.9.26
|
version.felix.config.admin=1.9.26
|
||||||
version.felix=7.0.5
|
version.felix=7.0.5
|
||||||
|
13
native-image/build.gradle
Normal file
13
native-image/build.gradle
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java-gradle-plugin'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
plugins {
|
||||||
|
create("NativeImagePlugin") {
|
||||||
|
id = "net.woggioni.gradle.native-image"
|
||||||
|
implementationClass = "net.woggioni.gradle.nativeimage.NativeImagePlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
package net.woggioni.gradle.nativeimage;
|
||||||
|
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
|
import org.gradle.api.file.Directory;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.file.ProjectLayout;
|
||||||
|
import org.gradle.api.plugins.JavaApplication;
|
||||||
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
|
import org.gradle.api.provider.Provider;
|
||||||
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.JavaExec;
|
||||||
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
|
import org.gradle.api.tasks.TaskContainer;
|
||||||
|
import org.gradle.api.tasks.bundling.Jar;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static net.woggioni.gradle.nativeimage.NativeImagePlugin.NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME;
|
||||||
|
import static net.woggioni.gradle.nativeimage.NativeImagePlugin.NATIVE_IMAGE_TASK_GROUP;
|
||||||
|
|
||||||
|
public abstract class NativeImageConfigurationTask extends JavaExec {
|
||||||
|
|
||||||
|
@OutputDirectory
|
||||||
|
public abstract DirectoryProperty getConfigurationDir();
|
||||||
|
|
||||||
|
public NativeImageConfigurationTask() {
|
||||||
|
setGroup(NATIVE_IMAGE_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);
|
||||||
|
if(!Objects.isNull(javaApplication)) {
|
||||||
|
getMainClass().convention(javaApplication.getMainClass());
|
||||||
|
getMainModule().convention(javaApplication.getMainModule());
|
||||||
|
}
|
||||||
|
getConfigurationDir().convention(layout.getProjectDirectory()
|
||||||
|
.dir(NATIVE_IMAGE_CONFIGURATION_FOLDER_NAME));
|
||||||
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
List<String> jvmArgs = new ArrayList<>();
|
||||||
|
jvmArgs.add("-agentlib:native-image-agent=config-output-dir=" + getConfigurationDir().get());
|
||||||
|
for(String jvmArg : Optional.ofNullable(javaApplication.getApplicationDefaultJvmArgs()).orElse(Collections.emptyList())) {
|
||||||
|
jvmArgs.add(jvmArg);
|
||||||
|
}
|
||||||
|
jvmArgs(jvmArgs);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
package net.woggioni.gradle.nativeimage;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin;
|
||||||
|
import org.gradle.api.Project;
|
||||||
|
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||||
|
import org.gradle.api.file.FileCollection;
|
||||||
|
import org.gradle.api.file.ProjectLayout;
|
||||||
|
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";
|
||||||
|
|
||||||
|
public static final String NATIVE_IMAGE_TASK_GROUP = "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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tasks.register(CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask.class);
|
||||||
|
tasks.register(NATIVE_IMAGE_TASK_NAME, NativeImageTask.class, nativeImageTask -> {
|
||||||
|
ConfigurationContainer configurations = project.getConfigurations();
|
||||||
|
FileCollection classpath = project.files(jarTaskProvider,
|
||||||
|
configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME));
|
||||||
|
nativeImageTask.getClasspath().set(classpath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,127 @@
|
|||||||
|
package net.woggioni.gradle.nativeimage;
|
||||||
|
|
||||||
|
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.nativeimage.NativeImagePlugin.NATIVE_IMAGE_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> 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(NATIVE_IMAGE_TASK_GROUP);
|
||||||
|
setDescription("Create a native image of the application using GraalVM");
|
||||||
|
getUseJpms().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");
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
logger.info("Native image arguments: " + String.join(" ", result));
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getArgumentProviders().add(argumentProvider);
|
||||||
|
}
|
||||||
|
}
|
16
sambal/build.gradle
Normal file
16
sambal/build.gradle
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
package net.woggioni.gradle.sambal;
|
||||||
|
|
||||||
|
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().isBlank()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,3 +25,5 @@ include 'osgi-app:osgi-simple-bootstrapper'
|
|||||||
include 'osgi-app:osgi-simple-bootstrapper-api'
|
include 'osgi-app:osgi-simple-bootstrapper-api'
|
||||||
include 'osgi-app:osgi-simple-bootstrapper-application'
|
include 'osgi-app:osgi-simple-bootstrapper-application'
|
||||||
include 'wildfly'
|
include 'wildfly'
|
||||||
|
include 'sambal'
|
||||||
|
include 'native-image'
|
||||||
|
Reference in New Issue
Block a user