diff --git a/byte-buddy-gradle-plugin/android-plugin/src/main/java/net/bytebuddy/build/gradle/android/ByteBuddyLocalClassesEnhancerTask.java b/byte-buddy-gradle-plugin/android-plugin/src/main/java/net/bytebuddy/build/gradle/android/ByteBuddyLocalClassesEnhancerTask.java index c46a4ebe3f..39cb3b273d 100644 --- a/byte-buddy-gradle-plugin/android-plugin/src/main/java/net/bytebuddy/build/gradle/android/ByteBuddyLocalClassesEnhancerTask.java +++ b/byte-buddy-gradle-plugin/android-plugin/src/main/java/net/bytebuddy/build/gradle/android/ByteBuddyLocalClassesEnhancerTask.java @@ -19,12 +19,10 @@ import net.bytebuddy.ByteBuddy; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.build.AndroidDescriptor; -import net.bytebuddy.build.BuildLogger; import net.bytebuddy.build.EntryPoint; import net.bytebuddy.build.Plugin; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; -import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer; import net.bytebuddy.utility.QueueFactory; import net.bytebuddy.utility.nullability.MaybeNull; import org.gradle.api.Action; @@ -38,6 +36,7 @@ import org.gradle.api.tasks.*; import java.io.*; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -205,107 +204,64 @@ public void execute() throws IOException { for (RegularFile jarFile : getInputJars().get()) { sources.add(new Plugin.Engine.Source.ForJarFile(jarFile.getAsFile())); } - Plugin.Engine.Summary summary; + ClassFileVersion classFileVersion = ClassFileVersion.ofJavaVersionString(getJavaTargetCompatibilityVersion().get().toString()); + AndroidDescriptor androidDescriptor = DefaultAndroidDescriptor.ofClassPath(localClasspath); ClassLoader classLoader = new URLClassLoader( toUrls(getByteBuddyClasspath().getFiles()), new URLClassLoader(toUrls(getAndroidBootClasspath().getFiles()), ByteBuddy.class.getClassLoader())); try { - if (getDiscovery().get().isDiscover(transformations)) { - Set undiscoverable = new HashSet(); - if (getDiscovery().get().isRecordConfiguration()) { - for (Transformation transformation : transformations) { - undiscoverable.add(transformation.toPluginName()); - } - } - for (String name : Plugin.Engine.Default.scan(classLoader)) { - if (undiscoverable.add(name)) { - try { - @SuppressWarnings("unchecked") - Class plugin = (Class) Class.forName(name, false, classLoader); - Transformation transformation = new Transformation(); - transformation.setPlugin(plugin); - transformations.add(transformation); - } catch (ClassNotFoundException exception) { - throw new IllegalStateException("Discovered plugin is not available: " + name, exception); - } - getLogger().debug("Registered discovered plugin: {}", name); - } else { - getLogger().info("Skipping discovered plugin {} which was previously discovered or registered", name); - } - } - } - if (transformations.isEmpty()) { - getLogger().warn("No transformations are specified or discovered. Application will be non-operational."); + Class.forName("net.bytebuddy.build.gradle.AbstractByteBuddyTask").getMethod("apply", + Logger.class, + ClassLoader.class, + List.class, + Class.forName("net.bytebuddy.build.gradle.Discovery"), + ClassFileLocator.class, + Iterable.class, + Iterable.class, + EntryPoint.class, + ClassFileVersion.class, + Plugin.Factory.UsingReflection.ArgumentResolver.class, + String.class, + int.class, + boolean.class, + boolean.class, + boolean.class, + boolean.class, + Plugin.Engine.Source.class, + Plugin.Engine.Target.class).invoke(null, + getLogger(), + classLoader, + transformations, + getDiscovery().get(), + ClassFileLocator.ForClassLoader.of(ByteBuddy.class.getClassLoader()), + getAndroidBootClasspath().plus(getByteBuddyClasspath()).getFiles(), + Collections.emptyList(), // TODO + getEntryPoint().get(), + classFileVersion, + Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(AndroidDescriptor.class, androidDescriptor), + getSuffix().get(), + getThreads().get(), + getExtendedParsing().get(), + getFailFast().get(), + getFailOnLiveInitializer().get(), + getWarnOnEmptyTypeSet().get(), + new Plugin.Engine.Source.Compound(sources), + new TargetForAndroidAppJarFile(getOutputFile().get().getAsFile())); + } catch (InvocationTargetException exception) { + Throwable cause = exception.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } else if (cause instanceof RuntimeException){ + throw (RuntimeException) cause; } else { - getLogger().debug("{} plugins are being applied via configuration and discovery", transformations.size()); - } - List factories = new ArrayList(transformations.size()); - BuildLogger buildLogger; - try { - buildLogger = (BuildLogger) Class.forName("net.bytebuddy.build.gradle.GradleBuildLogger") - .getConstructor(Logger.class) - .newInstance(getLogger()); - } catch (Exception exception) { - throw new GradleException("Failed to resolve Gradle build logger", exception); - } - AndroidDescriptor androidDescriptor = DefaultAndroidDescriptor.ofClassPath(localClasspath); - for (Transformation transformation : transformations) { - try { - factories.add(new Plugin.Factory.UsingReflection(transformation.toPlugin(classLoader)) - .with(transformation.makeArgumentResolvers()) - .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(AndroidDescriptor.class, androidDescriptor)) - .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(Logger.class, getLogger())) - .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(org.slf4j.Logger.class, getLogger())) - .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(BuildLogger.class, buildLogger))); - getLogger().info("Resolved plugin: {}", transformation.toPluginName()); - } catch (Throwable throwable) { - throw new IllegalStateException("Cannot resolve plugin: " + transformation.toPluginName(), throwable); - } - } - ClassFileVersion classFileVersion = ClassFileVersion.ofJavaVersionString(getJavaTargetCompatibilityVersion().get().toString()); - List classFileLocators = new ArrayList(); - for (File file : getAndroidBootClasspath().plus(getByteBuddyClasspath()).getFiles()) { - classFileLocators.add(file.isFile() - ? ClassFileLocator.ForJarFile.of(file) - : new ClassFileLocator.ForFolder(file)); - } - classFileLocators.add(ClassFileLocator.ForClassLoader.of(ByteBuddy.class.getClassLoader())); - ClassFileLocator classFileLocator = new ClassFileLocator.Compound(classFileLocators); - try { - summary = Plugin.Engine.Default.of(getEntryPoint().get(), classFileVersion, getSuffix().get().length() == 0 - ? MethodNameTransformer.Suffixing.withRandomSuffix() - : new MethodNameTransformer.Suffixing(getSuffix().get())) - .with(getExtendedParsing().get() - ? Plugin.Engine.PoolStrategy.Default.EXTENDED - : Plugin.Engine.PoolStrategy.Default.FAST) - .with(classFileLocator) - .with(new TransformationLogger(getLogger())) - .withErrorHandlers(Plugin.Engine.ErrorHandler.Enforcing.ALL_TYPES_RESOLVED, getFailOnLiveInitializer().get() - ? Plugin.Engine.ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS - : Plugin.Engine.Listener.NoOp.INSTANCE, getFailFast().get() - ? Plugin.Engine.ErrorHandler.Failing.FAIL_FAST - : Plugin.Engine.ErrorHandler.Failing.FAIL_LAST) - .with(getThreads().get() == 0 - ? Plugin.Engine.Dispatcher.ForSerialTransformation.Factory.INSTANCE - : new Plugin.Engine.Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(getThreads().get())) - .apply(new Plugin.Engine.Source.Compound(sources), new TargetForAndroidAppJarFile(getOutputFile().get().getAsFile()), factories); - } finally { - classFileLocator.close(); + throw new GradleException("Unexpected transformation error", exception); } + } catch (Throwable throwable) { + throw new GradleException("Unexpected transformation error", throwable); } finally { if (classLoader instanceof Closeable) { ((Closeable) classLoader).close(); } - if (classLoader.getParent() instanceof Closeable) { - ((Closeable) classLoader.getParent()).close(); - } - } - if (!summary.getFailed().isEmpty()) { - throw new IllegalStateException(summary.getFailed() + " type transformation(s) have failed"); - } else if (getWarnOnEmptyTypeSet().get() && summary.getTransformed().isEmpty()) { - getLogger().warn("No types were transformed during plugin execution"); - } else { - getLogger().info("Transformed {} type(s)", summary.getTransformed().size()); } } @@ -490,49 +446,4 @@ public void retain(Plugin.Engine.Source.Element element) throws IOException { } } } - - /** - * A {@link net.bytebuddy.build.Plugin.Engine.Listener} that logs several relevant events during the build. - */ - protected static class TransformationLogger extends Plugin.Engine.Listener.Adapter { - - /** - * The logger to delegate to. - */ - private final Logger logger; - - /** - * Creates a new transformation logger. - * - * @param logger The logger to delegate to. - */ - protected TransformationLogger(Logger logger) { - this.logger = logger; - } - - @Override - public void onTransformation(TypeDescription typeDescription, List plugins) { - logger.debug("Transformed {} using {}", typeDescription, plugins); - } - - @Override - public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) { - logger.warn("Failed to transform {} using {}", typeDescription, plugin, throwable); - } - - @Override - public void onError(Map> throwables) { - logger.warn("Failed to transform {} types", throwables.size()); - } - - @Override - public void onError(Plugin plugin, Throwable throwable) { - logger.error("Failed to close {}", plugin, throwable); - } - - @Override - public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) { - logger.debug("Discovered live initializer for {} as a result of transforming {}", definingType, typeDescription); - } - } } diff --git a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractByteBuddyTask.java b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractByteBuddyTask.java index af767811ea..b28a988657 100644 --- a/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractByteBuddyTask.java +++ b/byte-buddy-gradle-plugin/src/main/java/net/bytebuddy/build/gradle/AbstractByteBuddyTask.java @@ -39,14 +39,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; +import java.util.*; /** * An abstract Byte Buddy task implementation. @@ -268,7 +261,6 @@ public void setExtendedParsing(boolean extendedParsing) { this.extendedParsing = extendedParsing; } - /** * Determines the discovery for finding plugins on the class path. * @@ -370,8 +362,74 @@ protected void doApply(Plugin.Engine.Source source, Plugin.Engine.Target target) if (source().equals(target())) { throw new IllegalStateException("Source and target cannot be equal: " + source()); } - List transformations = new ArrayList(getTransformations()); - ClassLoader classLoader = ByteBuddySkippingUrlClassLoader.of(getClass().getClassLoader(), discoverySet()); + ClassFileVersion classFileVersion; + if (this.classFileVersion == null) { + classFileVersion = ClassFileVersion.ofThisVm(); + getLogger().warn("Could not locate Java target version, build is JDK dependant: {}", classFileVersion.getJavaVersion()); + } else { + classFileVersion = this.classFileVersion; + getLogger().debug("Java version was configured: {}", classFileVersion.getJavaVersion()); + } + apply(getLogger(), + getClass().getClassLoader(), + new ArrayList(getTransformations()), + getDiscovery(), + ClassFileLocator.ForClassLoader.ofPlatformLoader(), + classPath(), + discoverySet(), + getEntryPoint(), + classFileVersion, + Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(File.class, source()), + getSuffix(), + getThreads(), + isExtendedParsing(), + isFailFast(), + isFailOnLiveInitializer(), + isWarnOnEmptyTypeSet(), + source, + target); + } + + /** + * Dispatches a Byte Buddy instrumentation Gradle task. + * + * @param logger The logger to use. + * @param transformations The transformations to apply. + * @param discovery The discovery for plugins to use. + * @param rootLocator The root class file locator. + * @param artifacts The artifacts to include + * @param entryPoint The entry point to use. + * @param classFileVersion The class file version to use. + * @param rootLocationResolver A argument resolver for the root location of this build. + * @param suffix The suffix to use for rebased methods or an empty string for using a random suffix. + * @param threads The number of threads to use while instrumenting. + * @param extendedParsing {@code true} if extended parsing should be used. + * @param failFast {@code true} if the build should fail fast. + * @param failOnLiveInitializer {@code true} if the build should fail upon discovering a live initializer. + * @param warnOnEmptyTypeSet {@code true} if a warning should be logged if no types are instrumented. + * @param source The source to use for instrumenting. + * @param target The target to use for instrumenting. + * @throws IOException If an I/O error occurs. + */ + public static void apply(Logger logger, + ClassLoader rootLoader, + List transformations, + Discovery discovery, + ClassFileLocator rootLocator, + Iterable artifacts, + Iterable discoveries, + EntryPoint entryPoint, + ClassFileVersion classFileVersion, + Plugin.Factory.UsingReflection.ArgumentResolver rootLocationResolver, + String suffix, + int threads, + boolean extendedParsing, + boolean failFast, + boolean failOnLiveInitializer, + boolean warnOnEmptyTypeSet, + Plugin.Engine.Source source, + Plugin.Engine.Target target) throws IOException { + ClassLoader classLoader = ByteBuddySkippingUrlClassLoader.of(rootLoader, discoveries); Plugin.Engine.Summary summary; try { if (discovery.isDiscover(transformations)) { @@ -392,76 +450,57 @@ protected void doApply(Plugin.Engine.Source source, Plugin.Engine.Target target) } catch (ClassNotFoundException exception) { throw new IllegalStateException("Discovered plugin is not available: " + name, exception); } - getLogger().debug("Registered discovered plugin: {}", name); + logger.debug("Registered discovered plugin: {}", name); } else { - getLogger().info("Skipping discovered plugin {} which was previously discovered or registered", name); + logger.info("Skipping discovered plugin {} which was previously discovered or registered", name); } } } if (transformations.isEmpty()) { - getLogger().warn("No transformations are specified or discovered. Application will be non-operational."); + logger.warn("No transformations are specified or discovered. Application will be non-operational."); } else { - getLogger().debug("{} plugins are being applied via configuration and discovery", transformations.size()); + logger.debug("{} plugins are being applied via configuration and discovery", transformations.size()); } List factories = new ArrayList(transformations.size()); for (Transformation transformation : transformations) { try { factories.add(new Plugin.Factory.UsingReflection(transformation.toPlugin(classLoader)) .with(transformation.makeArgumentResolvers()) - .with(Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(File.class, source()), - Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(Logger.class, getLogger()), - Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(org.slf4j.Logger.class, getLogger()), - Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(BuildLogger.class, new GradleBuildLogger(getLogger())))); - getLogger().info("Resolved plugin: {}", transformation.toPluginName()); + .with(rootLocationResolver, + Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(Logger.class, logger), + Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(org.slf4j.Logger.class, logger), + Plugin.Factory.UsingReflection.ArgumentResolver.ForType.of(BuildLogger.class, new GradleBuildLogger(logger)))); + logger.info("Resolved plugin: {}", transformation.toPluginName()); } catch (Throwable throwable) { throw new IllegalStateException("Cannot resolve plugin: " + transformation.toPluginName(), throwable); } } List classFileLocators = new ArrayList(); - classFileLocators.add(ClassFileLocator.ForClassLoader.ofPlatformLoader()); - for (File artifact : classPath()) { + classFileLocators.add(rootLocator); + for (File artifact : artifacts) { classFileLocators.add(artifact.isFile() ? ClassFileLocator.ForJarFile.of(artifact) : new ClassFileLocator.ForFolder(artifact)); } ClassFileLocator classFileLocator = new ClassFileLocator.Compound(classFileLocators); try { - getLogger().info("Processing class files located in in: {}", source()); - Plugin.Engine pluginEngine; - try { - ClassFileVersion classFileVersion; - if (this.classFileVersion == null) { - classFileVersion = ClassFileVersion.ofThisVm(); - getLogger().warn("Could not locate Java target version, build is JDK dependant: {}", classFileVersion.getJavaVersion()); - } else { - classFileVersion = this.classFileVersion; - getLogger().debug("Java version was configured: {}", classFileVersion.getJavaVersion()); - } - pluginEngine = Plugin.Engine.Default.of(getEntryPoint(), classFileVersion, getSuffix().length() == 0 - ? MethodNameTransformer.Suffixing.withRandomSuffix() - : new MethodNameTransformer.Suffixing(getSuffix())); - } catch (Throwable throwable) { - throw new IllegalStateException("Cannot create plugin engine", throwable); - } - try { - summary = pluginEngine - .with(isExtendedParsing() - ? Plugin.Engine.PoolStrategy.Default.EXTENDED - : Plugin.Engine.PoolStrategy.Default.FAST) - .with(classFileLocator) - .with(new TransformationLogger(getLogger())) - .withErrorHandlers(Plugin.Engine.ErrorHandler.Enforcing.ALL_TYPES_RESOLVED, isFailOnLiveInitializer() - ? Plugin.Engine.ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS - : Plugin.Engine.Listener.NoOp.INSTANCE, isFailFast() - ? Plugin.Engine.ErrorHandler.Failing.FAIL_FAST - : Plugin.Engine.ErrorHandler.Failing.FAIL_LAST) - .with(getThreads() == 0 - ? Plugin.Engine.Dispatcher.ForSerialTransformation.Factory.INSTANCE - : new Plugin.Engine.Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(getThreads())) - .apply(source, target, factories); - } catch (Throwable throwable) { - throw new IllegalStateException("Failed to transform class files in " + source(), throwable); - } + summary = Plugin.Engine.Default.of(entryPoint, classFileVersion, suffix.length() == 0 + ? MethodNameTransformer.Suffixing.withRandomSuffix() + : new MethodNameTransformer.Suffixing(suffix)) + .with(extendedParsing + ? Plugin.Engine.PoolStrategy.Default.EXTENDED + : Plugin.Engine.PoolStrategy.Default.FAST) + .with(classFileLocator) + .with(new TransformationLogger(logger)) + .withErrorHandlers(Plugin.Engine.ErrorHandler.Enforcing.ALL_TYPES_RESOLVED, failOnLiveInitializer + ? Plugin.Engine.ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS + : Plugin.Engine.Listener.NoOp.INSTANCE, failFast + ? Plugin.Engine.ErrorHandler.Failing.FAIL_FAST + : Plugin.Engine.ErrorHandler.Failing.FAIL_LAST) + .with(threads == 0 + ? Plugin.Engine.Dispatcher.ForSerialTransformation.Factory.INSTANCE + : new Plugin.Engine.Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(threads)) + .apply(source, target, factories); } finally { classFileLocator.close(); } @@ -472,10 +511,10 @@ protected void doApply(Plugin.Engine.Source source, Plugin.Engine.Target target) } if (!summary.getFailed().isEmpty()) { throw new IllegalStateException(summary.getFailed() + " type transformation(s) have failed"); - } else if (isWarnOnEmptyTypeSet() && summary.getTransformed().isEmpty()) { - getLogger().warn("No types were transformed during plugin execution"); + } else if (warnOnEmptyTypeSet && summary.getTransformed().isEmpty()) { + logger.warn("No types were transformed during plugin execution"); } else { - getLogger().info("Transformed {} type(s)", summary.getTransformed().size()); + logger.info("Transformed {} type(s)", summary.getTransformed().size()); } }