Skip to content

Commit

Permalink
Use reflection and translation to piggy back Android task on top of r…
Browse files Browse the repository at this point in the history
…egular Gadle task.
  • Loading branch information
raphw committed Aug 21, 2024
1 parent c5507cc commit 7e87eca
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 218 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ private static URL[] toUrls(Collection<File> files) {
*/
@TaskAction
public void execute() throws IOException {
List<Transformation> transformations = new ArrayList<Transformation>(getTransformations().get());
List<Object> transformations = new ArrayList<Object>(getTransformations().get().size());
for (Transformation transformation : getTransformations().get()) {
transformations.add(transformation.resolve());
}
Set<Plugin.Engine.Source> sources = new LinkedHashSet<Plugin.Engine.Source>();
Set<File> localClasspath = new HashSet<>();
for (Directory directory : getLocalClassesDirs().get()) {
Expand All @@ -210,11 +213,12 @@ public void execute() throws IOException {
toUrls(getByteBuddyClasspath().getFiles()),
new URLClassLoader(toUrls(getAndroidBootClasspath().getFiles()), ByteBuddy.class.getClassLoader()));
try {
Class.forName("net.bytebuddy.build.gradle.AbstractByteBuddyTask").getMethod("apply",
Class<?> discovery = Class.forName("net.bytebuddy.build.gradle.Discovery");
Class.forName("net.bytebuddy.build.gradle.AbstractByteBuddyTask").getMethod("apply",
Logger.class,
ClassLoader.class,
List.class,
Class.forName("net.bytebuddy.build.gradle.Discovery"),
discovery,
ClassFileLocator.class,
Iterable.class,
Iterable.class,
Expand All @@ -232,7 +236,7 @@ public void execute() throws IOException {
getLogger(),
classLoader,
transformations,
getDiscovery().get(),
discovery.getMethod("valueOf", String.class).invoke(null, getDiscovery().get().name()),
ClassFileLocator.ForClassLoader.of(ByteBuddy.class.getClassLoader()),
getAndroidBootClasspath().plus(getByteBuddyClasspath()).getFiles(),
Collections.emptyList(), // TODO
Expand All @@ -254,7 +258,7 @@ public void execute() throws IOException {
} else if (cause instanceof RuntimeException){
throw (RuntimeException) cause;
} else {
throw new GradleException("Unexpected transformation error", exception);
throw new GradleException("Unexpected transformation error", cause);
}
} catch (Throwable throwable) {
throw new GradleException("Unexpected transformation error", throwable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package net.bytebuddy.build.gradle.android;

import java.util.List;

/**
* Determines how and if plugins are discovered.
*/
Expand All @@ -25,71 +23,20 @@ public enum Discovery {
/**
* Attempts discovery of plugins even if they are explicitly registered.
*/
ALL(false) {
@Override
protected boolean isDiscover(List<Transformation> transformations) {
return true;
}
},
ALL,

/**
* Attempts discovery of plugins but does not discover plugins that are explicitly registered.
*/
UNIQUE(true) {
@Override
protected boolean isDiscover(List<Transformation> transformations) {
return true;
}
},
UNIQUE,

/**
* Only discovers plugins if no plugin was explicitly registered. This is the default configuration.
*/
EMPTY(true) {
@Override
protected boolean isDiscover(List<Transformation> transformations) {
return transformations.isEmpty();
}
},
EMPTY,

/**
* Does never discover plugins.
*/
NONE(true) {
@Override
protected boolean isDiscover(List<Transformation> transformations) {
return false;
}
};

/**
* {@code true} if explicit configurations should be recorded.
*/
private final boolean recordConfiguration;

/**
* Creates a new discovery configuration.
*
* @param recordConfiguration {@code true} if explicit configurations should be recorded.
*/
Discovery(boolean recordConfiguration) {
this.recordConfiguration = recordConfiguration;
}

/**
* Returns {@code true} if explicit configurations should be recorded.
*
* @return {@code true} if explicit configurations should be recorded.
*/
protected boolean isRecordConfiguration() {
return recordConfiguration;
}

/**
* Determines if plugins should be discovered from the class path.
*
* @param transformations The configured transformers.
* @return {@code true} if plugins should be discovered from the class path.
*/
protected abstract boolean isDiscover(List<Transformation> transformations);
NONE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import net.bytebuddy.build.Plugin;
import net.bytebuddy.utility.nullability.MaybeNull;
import org.gradle.api.GradleException;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;

Expand Down Expand Up @@ -106,12 +107,12 @@ public void setValue(@MaybeNull Object value) {
this.value = value;
}

/**
* Resolves this plugin argument to an argument resolver.
*
* @return An argument resolver that represents this plugin argument.
*/
protected Plugin.Factory.UsingReflection.ArgumentResolver toArgumentResolver() {
return new Plugin.Factory.UsingReflection.ArgumentResolver.ForIndex(index, value);
protected Object resolve() {
try {
Class<?> type = Class.forName("net.bytebuddy.build.gradle.PluginArgument");
return type.getConstructor(int.class, Object.class).newInstance(index, value);
} catch (Exception exception) {
throw new GradleException("Could not resolve plugin argument", exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@
import org.gradle.api.tasks.Optional;

import javax.inject.Inject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -111,19 +107,6 @@ public void argument(Action<PluginArgument> action) {
getArguments().add(argument);
}

/**
* Creates the argument resolvers for the plugin's constructor by transforming the plugin arguments.
*
* @return A list of argument resolvers.
*/
protected List<Plugin.Factory.UsingReflection.ArgumentResolver> makeArgumentResolvers() {
List<Plugin.Factory.UsingReflection.ArgumentResolver> argumentResolvers = new ArrayList<Plugin.Factory.UsingReflection.ArgumentResolver>();
for (PluginArgument argument : getArguments()) {
argumentResolvers.add(argument.toArgumentResolver());
}
return argumentResolvers;
}

/**
* Returns the plugin type to apply.
*
Expand Down Expand Up @@ -166,140 +149,20 @@ public void setPluginName(@MaybeNull String pluginName) {
this.pluginName = pluginName;
}

/**
* Resolves the plugin type.
*
* @param classLoader The class loader to load a plugin from, if appropriate.
* @return The loaded plugin type.
*/
protected Class<? extends Plugin> toPlugin(@MaybeNull ClassLoader classLoader) {
if (plugin != null) {
if (pluginName != null && !plugin.getName().equals(pluginName)) {
throw new GradleException("Defined both plugin (" + plugin + ") and plugin name (" + pluginName + ") but they are not equal");
}
if (Plugin.class.isAssignableFrom(plugin)) {
return plugin;
}
protected Object resolve() {
try {
Class<?> type = Class.forName("net.bytebuddy.build.gradle.Transformation");
Object instance = type.getConstructor(Project.class).newInstance(project);
@SuppressWarnings("unchecked")
Class<? extends Plugin> type = (Class<? extends Plugin>) PluginResolvingClassLoader.wrap(getClass().getClassLoader(), plugin);
if (!Plugin.class.isAssignableFrom(type)) {
throw new GradleException(type.getName() + " does not implement " + Plugin.class.getName());
}
return type;
} else if (pluginName != null) {
try {
@SuppressWarnings("unchecked")
Class<? extends Plugin> type = (Class<? extends Plugin>) Class.forName(pluginName, false, classLoader);
if (!Plugin.class.isAssignableFrom(type)) {
throw new GradleException(type.getName() + " does not implement " + Plugin.class.getName());
}
return type;
} catch (ClassNotFoundException e) {
throw new GradleException("Cannot locate plugin class " + pluginName + " by its name", e);
}
} else {
throw new GradleException("No plugin or plugin name defined for transformation");
}
}

/**
* Resolves the name of the plugin.
*
* @return The name of the plugin.
*/
protected String toPluginName() {
if (plugin != null) {
if (pluginName != null && !plugin.getName().equals(pluginName)) {
throw new GradleException("Defined both plugin (" + plugin + ") and plugin name (" + pluginName + ") but they are not equal");
}
return plugin.getName();
} else if (pluginName != null) {
return pluginName;
} else {
throw new GradleException("No plugin or plugin name defined for transformation");
}
}

/**
* A class loader that resolves the plugin against a class loader that is a parent of the Byte Buddy plugin's
* class loader.
*/
protected static class PluginResolvingClassLoader extends ClassLoader {

/**
* The class loader of the user supplied plugin.
*/
private final ClassLoader classLoader;

/**
* The protection domain to resolve.
*/
private final ProtectionDomain protectionDomain;

/**
* Creates a new plugin resolving class loader.
*
* @param parent The parent class loader.
* @param classLoader The class loader of the user supplied plugin.
* @param protectionDomain The protection domain to resolve.
*/
protected PluginResolvingClassLoader(ClassLoader parent, ClassLoader classLoader, ProtectionDomain protectionDomain) {
super(parent);
this.classLoader = classLoader;
this.protectionDomain = protectionDomain;
}

/**
* Wraps a plugin type with a class loader that resolves Byte Buddy via the Byte Buddy plugin's class loader.
*
* @param parent The parent class loader.
* @param type The supplied type.
* @return The wrapped type.
*/
public static Class<?> wrap(ClassLoader parent, Class<?> type) {
try {
Class<?> t = Class.forName(type.getName(),
false,
new PluginResolvingClassLoader(parent, type.getClassLoader(), type.getProtectionDomain()));
System.out.println("XXXXX");
System.out.println(t.getClassLoader());
System.out.println(Plugin.class.getClassLoader());
System.out.println(parent);
return t;
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Failed to wrap plugin type " + type.getName(), e);
}
}

/**
* {@inheritDoc}
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (!name.startsWith("net.bytebuddy.")) {
InputStream inputStream = classLoader.getResourceAsStream(name.replace('.', '/') + ".class");
if (inputStream == null) {
return super.findClass(name);
}
byte[] binaryRepresentation;
try {
ByteArrayOutputStream outputStream;
try {
outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
} finally {
inputStream.close();
}
binaryRepresentation = outputStream.toByteArray();
} catch (IOException exception) {
throw new IllegalStateException(exception);
}
return defineClass(name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain);
List<Object> arguments = (List<Object>) type.getMethod("getArguments").invoke(instance);
for (PluginArgument argument : this.arguments) {
arguments.add(argument.resolve());
}
return super.findClass(name);
type.getMethod("setPlugin").invoke(instance, plugin);
type.getMethod("setPluginName", String.class).invoke(instance, pluginName);
return instance;
} catch (Exception exception) {
throw new GradleException("Could not resolve plugin argument", exception);
}
}
}

0 comments on commit 7e87eca

Please sign in to comment.