Skip to content

Commit

Permalink
Enhanced agp 7.4 instrumentation (#1537)
Browse files Browse the repository at this point in the history
* Splitting resources from classes for local projects

* Using custom dependency rule to merge class dirs and resources from local and remote deps

* Using the last api's ALL scope

* Fixing android descriptor

* Removed project references in ByteBuddyLocalClassesEnhancerTask due to https://docs.gradle.org/8.3/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution

* Update working regarding ByteBuddyLocalClassesEnhancerTask's scope

* Update working regarding ByteBuddyLocalClassesEnhancerTask's scope

* Update wordings and renaming methods in ByteBuddyAndroidPlugin

* Renaming bytebuddy 7.4 transform tasks
  • Loading branch information
LikeTheSalad authored Oct 3, 2023
1 parent cf79ad1 commit 2c7d58d
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 157 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.concurrent.ConcurrentMap;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import net.bytebuddy.utility.nullability.MaybeNull;
import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
Expand Down Expand Up @@ -72,7 +73,8 @@ public class ByteBuddyAndroidPlugin implements Plugin<Project> {
/**
* The name of the Byte Buddy jar type.
*/
private static final String BYTE_BUDDY_JAR_TYPE = "bytebuddy-jar";
public static final String BYTE_BUDDY_CLASSES_TYPE = "bytebuddy-classes";
public static final String BYTE_BUDDY_RESOURCES_TYPE = "bytebuddy-resources";

/*
* Resolves the dispatcher.
Expand All @@ -84,7 +86,7 @@ public class ByteBuddyAndroidPlugin implements Plugin<Project> {
Class<?> scopedArtifacts = Class.forName("com.android.build.api.variant.ScopedArtifacts");
Class<?> scopedArtifact = Class.forName("com.android.build.api.artifact.ScopedArtifact");
@SuppressWarnings("unchecked")
Object project = Enum.valueOf((Class) scope, "PROJECT");
Object project = Enum.valueOf((Class) scope, "ALL");
dispatcher = new TransformationDispatcher.ForApk74CompatibleAndroid(
Artifacts.class.getMethod("forScope", scope),
scopedArtifacts.getMethod("use", TaskProvider.class),
Expand All @@ -110,7 +112,6 @@ public void apply(Project project) {
if (currentAgpVersion.compareTo(new AndroidPluginVersion(7, 2)) < 0) {
throw new IllegalStateException("Byte Buddy requires at least Gradle Plugin version 7.2+, but found " + currentAgpVersion);
}
project.getDependencies().registerTransform(AarGradleTransformAction.class, new AarGradleTransformAction.ConfigurationAction());
project.getDependencies().getAttributesSchema().attribute(ARTIFACT_TYPE_ATTRIBUTE, new AttributeMatchingStrategyConfigurationAction());
extension.onVariants(extension.selector().all(), new VariantAction(project, project.getConfigurations().create("byteBuddy", new ConfigurationConfigurationAction())));
}
Expand Down Expand Up @@ -151,9 +152,26 @@ protected VariantAction(Project project, Configuration configuration) {
* {@inheritDoc}
*/
public void execute(Variant variant) {
Configuration configuration = getByteBuddyConfiguration(variant);

if (TRANSFORMATION_DISPATCHER instanceof TransformationDispatcher.ForApk74CompatibleAndroid) {
TRANSFORMATION_DISPATCHER.accept(project, variant, configuration, null);
return;
}

// Legacy api usage.
Provider<ByteBuddyAndroidService> byteBuddyAndroidServiceProvider = project.getGradle().getSharedServices().registerIfAbsent(variant.getName() + "ByteBuddyAndroidService",
ByteBuddyAndroidService.class,
new ByteBuddyAndroidService.ConfigurationAction(project.getExtensions().getByType(BaseExtension.class)));
FileCollection classPath = RuntimeClassPathResolver.INSTANCE.apply(variant);
variant.getInstrumentation().transformClassesWith(ByteBuddyAsmClassVisitorFactory.class, InstrumentationScope.ALL, new ByteBuddyTransformationConfiguration(project,
configuration,
byteBuddyAndroidServiceProvider,
classPath));
TRANSFORMATION_DISPATCHER.accept(project, variant, configuration, classPath);
}

private Configuration getByteBuddyConfiguration(Variant variant) {
if (variant.getBuildType() == null) {
throw new GradleException("Build type for " + variant + " was null");
}
Expand All @@ -167,12 +185,7 @@ public void execute(Variant variant) {
configuration = previous;
}
}
FileCollection classPath = RuntimeClassPathResolver.INSTANCE.apply(variant);
variant.getInstrumentation().transformClassesWith(ByteBuddyAsmClassVisitorFactory.class, InstrumentationScope.ALL, new ByteBuddyTransformationConfiguration(project,
configuration,
byteBuddyAndroidServiceProvider,
classPath));
TRANSFORMATION_DISPATCHER.accept(project, variant, configuration, classPath);
return configuration;
}
}

Expand Down Expand Up @@ -247,9 +260,9 @@ protected OfModernAgp(Method getRuntimeConfiguration) {
protected FileCollection apply(Variant variant) {
try {
return ((Configuration) getRuntimeConfiguration.invoke(variant)).getIncoming()
.artifactView(this)
.getArtifacts()
.getArtifactFiles();
.artifactView(this)
.getArtifacts()
.getArtifactFiles();
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Failed to access runtime configuration", exception);
} catch (InvocationTargetException exception) {
Expand Down Expand Up @@ -314,7 +327,7 @@ protected ByteBuddyTransformationConfiguration(Project project,
* {@inheritDoc}
*/
public Unit invoke(ByteBuddyInstrumentationParameters parameters) {
parameters.getByteBuddyClasspath().from(configuration);
parameters.getByteBuddyClasspath().from(getByteBuddyClasspath(project, configuration));
parameters.getAndroidBootClasspath().from(project.getExtensions().getByType(BaseExtension.class).getBootClasspath());
parameters.getRuntimeClasspath().from(classPath);
parameters.getByteBuddyService().set(byteBuddyAndroidServiceProvider);
Expand Down Expand Up @@ -396,11 +409,9 @@ protected AttributeContainerConfigurationAction(Project project, String buildTyp
* {@inheritDoc}
*/
public void execute(AttributeContainer attributes) {
attributes.attribute(ARTIFACT_TYPE_ATTRIBUTE, BYTE_BUDDY_JAR_TYPE);
attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, Category.LIBRARY));
attributes.attribute(BuildTypeAttr.ATTRIBUTE, project.getObjects().named(BuildTypeAttr.class, buildType));
attributes.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME));

}
}

Expand All @@ -413,7 +424,7 @@ protected static class AttributeMatchingStrategyConfigurationAction implements A
* {@inheritDoc}
*/
public void execute(AttributeMatchingStrategy<String> stringAttributeMatchingStrategy) {
stringAttributeMatchingStrategy.getCompatibilityRules().add(ByteBuddyJarRule.class);
stringAttributeMatchingStrategy.getCompatibilityRules().add(ByteBuddyDependencyRule.class);
}
}

Expand All @@ -434,14 +445,22 @@ public void execute(Configuration configuration) {
/**
* A rule to check for jar compatibility.
*/
public abstract static class ByteBuddyJarRule implements AttributeCompatibilityRule<String> {
public abstract static class ByteBuddyDependencyRule implements AttributeCompatibilityRule<String> {

/**
* {@inheritDoc}
*/
public void execute(CompatibilityCheckDetails<String> details) {
if (BYTE_BUDDY_JAR_TYPE.equals(details.getConsumerValue()) && "jar".equals(details.getProducerValue())) {
details.compatible();
if (BYTE_BUDDY_CLASSES_TYPE.equals(details.getConsumerValue())) {
String producerValue = details.getProducerValue();
if ("java-classes-directory".equals(producerValue) || "android-classes-directory".equals(producerValue)) {
details.compatible();
}
} else if (BYTE_BUDDY_RESOURCES_TYPE.equals(details.getConsumerValue())) {
String producerValue = details.getProducerValue();
if ("java-resources-directory".equals(producerValue) || "android-java-res".equals(producerValue)) {
details.compatible();
}
}
}
}
Expand All @@ -467,7 +486,7 @@ enum ForLegacyAndroid implements TransformationDispatcher {
public void accept(Project project, Variant variant, Configuration configuration, FileCollection classPath) {
TaskProvider<LegacyByteBuddyLocalClassesEnhancerTask> provider = project.getTasks().register(variant.getName() + "BytebuddyLocalTransform",
LegacyByteBuddyLocalClassesEnhancerTask.class,
new LegacyByteBuddyLocalClassesEnhancerTask.ConfigurationAction(configuration, project.getExtensions().getByType(BaseExtension.class), classPath));
new LegacyByteBuddyLocalClassesEnhancerTask.ConfigurationAction(getByteBuddyClasspath(project, configuration), project.getExtensions().getByType(BaseExtension.class), classPath));
variant.getArtifacts()
.use(provider)
.wiredWith(GetLocalClassesFunction.INSTANCE, GetOutputDirFunction.INSTANCE)
Expand Down Expand Up @@ -562,13 +581,13 @@ protected ForApk74CompatibleAndroid(Method forScope, Method use, Method toTransf
* {@inheritDoc}
*/
public void accept(Project project, Variant variant, Configuration configuration, FileCollection classPath) {
TaskProvider<ByteBuddyLocalClassesEnhancerTask> provider = project.getTasks().register(variant.getName() + "BytebuddyLocalTransform",
TaskProvider<ByteBuddyLocalClassesEnhancerTask> provider = project.getTasks().register(variant.getName() + "BytebuddyTransform",
ByteBuddyLocalClassesEnhancerTask.class,
new ByteBuddyLocalClassesEnhancerTask.ConfigurationAction(configuration, project.getExtensions().getByType(BaseExtension.class), classPath));
new ByteBuddyLocalClassesEnhancerTask.ConfigurationAction(getByteBuddyClasspath(project, configuration), project.getExtensions().getByType(BaseExtension.class)));
try {
toTransform.invoke(use.invoke(forScope.invoke(variant.getArtifacts(), scope), provider),
artifact,
GetLocalJarsFunction.INSTANCE,
GetProjectJarsFunction.INSTANCE,
GetLocalClassesDirsFunction.INSTANCE,
GetOutputFileFunction.INSTANCE);
} catch (IllegalAccessException exception) {
Expand All @@ -579,9 +598,9 @@ public void accept(Project project, Variant variant, Configuration configuration
}

/**
* A function representation of resolving local jars.
* A function representation of resolving local and dependencies jars.
*/
protected enum GetLocalJarsFunction implements Function1<ByteBuddyLocalClassesEnhancerTask, ListProperty<RegularFile>> {
protected enum GetProjectJarsFunction implements Function1<ByteBuddyLocalClassesEnhancerTask, ListProperty<RegularFile>> {

/**
* The singleton instance.
Expand All @@ -592,7 +611,7 @@ protected enum GetLocalJarsFunction implements Function1<ByteBuddyLocalClassesEn
* {@inheritDoc}
*/
public ListProperty<RegularFile> invoke(ByteBuddyLocalClassesEnhancerTask task) {
return task.getLocalJars();
return task.getInputJars();
}
}

Expand Down Expand Up @@ -641,6 +660,23 @@ public RegularFileProperty invoke(ByteBuddyLocalClassesEnhancerTask task) {
* @param configuration The configuration to use.
* @param classPath The class path to use.
*/
void accept(Project project, Variant variant, Configuration configuration, FileCollection classPath);
void accept(Project project, Variant variant, Configuration configuration, @MaybeNull FileCollection classPath);
}

/**
* For external dependencies, it provides their JAR files. For local project's dependencies, it provides their local
* build dirs for both classes and resources. The latter allows for faster and more reliable (up-to-date) compilation processes
* when using local plugins.
*/
private static FileCollection getByteBuddyClasspath(Project project, Configuration byteBuddyConfiguration) {
FileCollection resources = byteBuddyConfiguration.getIncoming().artifactView(viewConfiguration -> {
viewConfiguration.lenient(false);
viewConfiguration.getAttributes().attribute(ARTIFACT_TYPE_ATTRIBUTE, BYTE_BUDDY_RESOURCES_TYPE);
}).getFiles();
FileCollection classes = byteBuddyConfiguration.getIncoming().artifactView(viewConfiguration -> {
viewConfiguration.lenient(false);
viewConfiguration.getAttributes().attribute(ARTIFACT_TYPE_ATTRIBUTE, BYTE_BUDDY_CLASSES_TYPE);
}).getFiles();
return project.files(classes, resources);
}
}
Loading

0 comments on commit 2c7d58d

Please sign in to comment.