-
-
Notifications
You must be signed in to change notification settings - Fork 43
Description
The setup for native dependencies in a Gradle based projects is still relatively complex even after #221 and might be simplified even more by providing a Gradle plugin that takes care of the setup automatically.
In Gradle-based project that could be used simply as:
plugins {
id("com.aayushatharva.brotli4j") version "x.y.z"
}
dependencies {
runtimeOnly("com.aayushatharva.brotli4j:natives:x.y.z")
// optionally together with Gradle Shadow plugin
brotli4jShadow("com.aayushatharva.brotli4j:natives:x.y.z")
}I was able to produce such plugin in my local project based on #221 and @solonovamax's #219. This could be probably published to Maven Central / Gradle Plugin Portal and then it would be available without any additional setup. I have no idea if that could be achieved from the current Maven-based project structure or if that would need an additional separate Gradle-based project just for the plugin & its publishing.
PoC code:
buildSrc/build.gradle.kts
plugins {
`java-gradle-plugin`
}
gradlePlugin {
plugins {
create("brotli4j-plugin") {
id = "com.aayushatharva.brotli4j"
implementationClass = "com.aayushatharva.brotli4j.gradle.Brotli4jPlugin"
}
}
}buildSrc/src/main/java/com/aayushatharva/brotli4j/gradle/Brotli4jPlugin.java
package com.aayushatharva.brotli4j.gradle;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.Usage;
import org.gradle.api.file.FileCollection;
import org.gradle.api.model.ObjectFactory;
import org.gradle.nativeplatform.MachineArchitecture;
import org.gradle.nativeplatform.OperatingSystemFamily;
import org.gradle.nativeplatform.internal.DefaultTargetMachineFactory;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
class Brotli4jPlugin implements Plugin<Project> {
static final String BROTLI4J_GROUP_ID = "com.aayushatharva.brotli4j";
static final String BROTLI4J_NATIVES_MODULE = "natives";
static final String BROTLI4J_NATIVE_MODULE = "native";
static final String BROTLI4J_SHADOW_CONFIGURATION = "brotli4jShadow";
private final ObjectFactory objects;
@Inject
public Brotli4jPlugin(ObjectFactory objects) {
this.objects = objects;
}
@Override
public void apply(Project project) {
var configurations = project.getConfigurations();
project.getDependencies().getComponents().withModule(
BROTLI4J_GROUP_ID + ":" + BROTLI4J_NATIVES_MODULE,
Brotli4jComponentMetadataRule.class
);
var host = new DefaultTargetMachineFactory(project.getObjects()).host();
configurations.configureEach(configuration -> {
if (!configuration.getName().endsWith("Classpath")) {
return;
}
configuration.attributes(attributes -> {
if (!attributes.keySet().contains(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE)) {
attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, host.getOperatingSystemFamily());
}
if (!attributes.keySet().contains(MachineArchitecture.ARCHITECTURE_ATTRIBUTE)) {
attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, host.getArchitecture());
}
});
});
project.getPluginManager().withPlugin("com.gradleup.shadow", shadowPlugin -> {
var runtimeClasspath = configurations.getByName("runtimeClasspath");
var brotliShadow = configurations.register(BROTLI4J_SHADOW_CONFIGURATION, configuration -> {
configuration.extendsFrom(runtimeClasspath);
configuration.setCanBeConsumed(true);
configuration.setCanBeResolved(true);
configuration.setCanBeDeclared(false);
configuration.attributes(attributes -> {
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.JAVA_RUNTIME));
attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.LIBRARY));
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.class, LibraryElements.JAR));
attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named(OperatingSystemFamily.class, "all"));
attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(MachineArchitecture.class, "all"));
});
});
project.getTasks().matching(task -> task.getName().equals("shadowJar"))
.configureEach(task -> {
@SuppressWarnings("unchecked")
var shadowJarConfigurations = (List<FileCollection>) task.property("configurations");
if (shadowJarConfigurations == null) {
shadowJarConfigurations = new ArrayList<>();
}
shadowJarConfigurations.add(brotliShadow.get());
task.setProperty("configurations", shadowJarConfigurations);
});
});
}
}buildSrc/src/main/java/com/aayushatharva/brotli4j/gradle/Brotli4jComponentMetadataRule.java
package com.aayushatharva.brotli4j.gradle;
import org.gradle.api.artifacts.CacheableRule;
import org.gradle.api.artifacts.ComponentMetadataContext;
import org.gradle.api.artifacts.ComponentMetadataRule;
import org.gradle.api.model.ObjectFactory;
import org.gradle.nativeplatform.MachineArchitecture;
import org.gradle.nativeplatform.OperatingSystemFamily;
import org.gradle.nativeplatform.platform.internal.Architectures;
import javax.inject.Inject;
import java.util.List;
import static com.aayushatharva.brotli4j.gradle.Brotli4jPlugin.BROTLI4J_GROUP_ID;
import static com.aayushatharva.brotli4j.gradle.Brotli4jPlugin.BROTLI4J_NATIVE_MODULE;
@CacheableRule
abstract class Brotli4jComponentMetadataRule implements ComponentMetadataRule {
private static final List<NativeVariant> NATIVE_VARIANTS = List.of(
new NativeVariant(OperatingSystemFamily.WINDOWS, Architectures.AARCH64.getCanonicalName(), "windows-aarch64"),
new NativeVariant(OperatingSystemFamily.WINDOWS, Architectures.X86_64.getCanonicalName(), "windows-x86_64"),
new NativeVariant(OperatingSystemFamily.MACOS, Architectures.X86_64.getCanonicalName(), "osx-x86_64"),
new NativeVariant(OperatingSystemFamily.MACOS, Architectures.AARCH64.getCanonicalName(), "osx-aarch64"),
new NativeVariant(OperatingSystemFamily.LINUX, Architectures.X86_64.getCanonicalName(), "linux-x86_64"),
new NativeVariant(OperatingSystemFamily.LINUX, Architectures.AARCH64.getCanonicalName(), "linux-aarch64"),
new NativeVariant(OperatingSystemFamily.LINUX, Architectures.ARM_V7.getCanonicalName(), "linux-armv7"),
new NativeVariant(OperatingSystemFamily.LINUX, "s390x", "linux-s390x"),
new NativeVariant(OperatingSystemFamily.LINUX, "riscv64", "linux-riscv64"),
new NativeVariant(OperatingSystemFamily.LINUX, "ppc64le", "linux-ppc64le")
);
private final ObjectFactory objects;
@Inject
public Brotli4jComponentMetadataRule(ObjectFactory objects) {
this.objects = objects;
}
@Override
public void execute(ComponentMetadataContext context) {
List.of("compile", "runtime")
.forEach(base -> addVariant(context, base));
}
private void addVariant(ComponentMetadataContext context, String base) {
var version = context.getDetails().getId().getVersion();
NATIVE_VARIANTS.forEach(nativeVariant -> {
context.getDetails().addVariant("%s-%s".formatted(nativeVariant.classifier(), base), base, variant -> {
variant.attributes(attributes -> {
attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named(OperatingSystemFamily.class, nativeVariant.os()));
attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(MachineArchitecture.class, nativeVariant.arch()));
});
variant.withDependencies(dependencies ->
dependencies.add(nativeVariant.dependency(version))
);
});
});
context.getDetails().addVariant("all-%s".formatted(base), base, variant -> {
variant.attributes(attributes -> {
attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named(OperatingSystemFamily.class, "all"));
attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(MachineArchitecture.class, "all"));
});
variant.withDependencies(dependencies ->
NATIVE_VARIANTS.forEach(nativeVariant ->
dependencies.add(nativeVariant.dependency(version))
)
);
});
}
private record NativeVariant(
String os,
String arch,
String classifier
) {
String dependency(String version) {
return "%s:%s-%s:%s".formatted(BROTLI4J_GROUP_ID, BROTLI4J_NATIVE_MODULE, classifier(), version);
}
}
}