groovy / groovy-android-gradle-plugin

A Gradle plugin to support the Groovy language for building Android apps
Apache License 2.0
853 stars 116 forks source link

Dagger Annotation Processor Crashes When Building With Groovy #32

Closed Sarev0k closed 9 years ago

Sarev0k commented 9 years ago

I've created the a sample Gradle project to demonstrate the problem

  1. Download the zip
  2. Extract
  3. Modify the local.properties to point at your Android SDK
  4. ./gradlew build --stacktrace
    :preBuild
    :compileDebugNdk
    :preDebugBuild
    :checkDebugManifest
    :prepareDebugDependencies
    :compileDebugAidl
    :compileDebugRenderscript
    :generateDebugBuildConfig
    :generateDebugAssets UP-TO-DATE
    :mergeDebugAssets
    :generateDebugResValues UP-TO-DATE
    :generateDebugResources
    :mergeDebugResources
    :processDebugManifest
    :processDebugResources
    :generateDebugSources
    :compileDebugJava
    :compileDebugGroovy
    warning: [options] bootstrap class path not set in conjunction with -source 1.6
    :compileDebugGroovy FAILED

    FAILURE: Build failed with an exception.

    * What went wrong:
    Execution failed for task ':compileDebugGroovy'.
    > java.lang.NoSuchMethodError: com.google.common.collect.Queues.newArrayDeque()Ljava/util/ArrayDeque;

    * Try:
    Run with --info or --debug option to get more log output.

    * Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':compileDebugGroovy'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:305)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:80)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:51)
    at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:171)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
    at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
    at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
    at org.gradle.launcher.Main.doAction(Main.java:33)
    at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
    at org.gradle.launcher.GradleMain.main(GradleMain.java:23)
    at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:30)
    at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:127)
    at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:56)
    Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodError: com.google.common.collect.Queues.newArrayDeque()Ljava/util/ArrayDeque;
    at com.sun.tools.javac.main.Main.compile(Main.java:469)
    at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:132)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:42)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:35)
    at org.gradle.api.internal.tasks.compile.ApiGroovyCompiler$2$1.compile(ApiGroovyCompiler.java:112)
    at org.gradle.api.internal.tasks.compile.ApiGroovyCompiler.execute(ApiGroovyCompiler.java:122)
    at org.gradle.api.internal.tasks.compile.ApiGroovyCompiler.execute(ApiGroovyCompiler.java:47)
    at org.gradle.api.internal.tasks.compile.daemon.CompilerDaemonServer.execute(CompilerDaemonServer.java:53)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
    Caused by: java.lang.NoSuchMethodError: com.google.common.collect.Queues.newArrayDeque()Ljava/util/ArrayDeque;
    at dagger.internal.codegen.InjectBindingRegistry.<init>(InjectBindingRegistry.java:68)
    at dagger.internal.codegen.ComponentProcessor.init(ComponentProcessor.java:99)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:517)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator.next(JavacProcessingEnvironment.java:614)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:707)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1700(JavacProcessingEnvironment.java:97)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1029)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1163)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1108)
    at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:824)
    at com.sun.tools.javac.main.Main.compile(Main.java:439)
    ... 11 more

I've uploaded logs at the --debug log level for your references.

Oddly enough, if you were to move the AndroidModule.java file form src/main/groovy to src/main/java this project compiles with no issues. I've uploaded logs at the --debug log level for this case as well.

Do you have any ideas for what might be causing this issue?

Thanks for your help.

melix commented 9 years ago

Hi!

This is not an issue of the Gradle plugin. From what I read, the code generated by Dagger 2 uses a method which is not provided by the Guava dependency it includes. Maybe @cgruber has an idea.

Sarev0k commented 9 years ago

If it wasn't a problem with the Gradle plugin, why does Dagger 2 work when the file I mention is in the src/main/java folder?

If my understanding is correct, the classpaths are exactly the same for both JavaCompile and GroovyCompile. The JavaCompile classpath satisfies the Guava dependency just fine.

Am I missing something here?

melix commented 9 years ago

The Gradle plugin is only responsible for making it possible to compile Groovy files in Android. Nothing more. When you move the file from src/main/groovy to src/main/java, then you disable joint compilation basically, so no stubs are generated and Dagger has nothing to work on. When joint compilation is on, stubs are generated, Dagger will do more work than it has to do when it just finds a Java file. And I am assuming that the stubs trigger a condition where a call to a non existing method is done. Basically I would suspect a binary incompatibility between two dependencies in Dagger, but I am not sure, hence why I asked @cgruber.

Sarev0k commented 9 years ago

I've actually checked on the method that the Gradle output says isn't available. As far as I can tell, based on the jars that the Gradle build is pulling in, that this method is there. I've both decompiled the jar that is getting pulled down from maven, and looked at the source for that tagged version of Guava.

Is it possible that in the Groovy joint compilation mode is doing something unnatural to the Dagger annotation processor, that would cause it to think that, that method isn't there?

Also I'm curious about what you mean when you say stubs are generated in joint compilation mode? Is there a wiki or something that describes what's going on here? [Edit: Nevermind, just found the documentation, if my understanding is correct, the Groovy Compiler generates stub java files for the java compiler to compile it's sources against. In this particular sample I've linked to, I don't think this applies given that there is no groovy file in this sample). This is a pretty simple sample project, that only has Groovy compiling a java file, so I'm not sure what Groovy is doing differently here, than going straight through JavaCompile wouldn't.

Thanks for your help.

melix commented 9 years ago

It is actually pretty simple : in order for Java to be able to "see" and "use" Java classes and the opposite, we have a problem, because one needs to be compiled before the other, and then you face a cyclic dependency (class A, written in Java, depends on class B, written in Groovy, depending itself of class C written in Java). So the solution that Groovy has put in place so far is to use joint compilation with stubs: for each Groovy class, a Java stub is generated, so that the Java compiler can do its work and recognize Java classes. Then Groovy classes are compiled normally.

The stub generation is interesting for Dagger 2 because the stubs include the annotations, so if a Groovy class is annotated with Dagger annotations, the Java stubs will include those, and Dagger will be able to process them.

The underlying error is:

java.lang.NoSuchMethodError: com.google.common.collect.Queues.newArrayDeque()Ljava/util/ArrayDeque;
    at dagger.internal.codegen.InjectBindingRegistry.<init>(InjectBindingRegistry.java:68)
    at dagger.internal.codegen.ComponentProcessor.init(ComponentProcessor.java:99)

Which means that during Dagger work, it is trying to use a method which doesn't exist in Guava. I can only see two reasons for this:

  1. a binary incompatibility, that is to say a dependency of Dagger was compiled with a different version of Guava than the one included now
  2. two versions of Guava on classpath

For 2., this could be hidden by Gradle which would select one version automatically, but the dependency report seems to say the opposite:

$ gw dependencyInsight --configuration provided --dependency guava:dependencyInsight
com.google.guava:guava:18.0
+--- com.google.auto:auto-common:1.0-SNAPSHOT
|    \--- com.google.dagger:dagger-compiler:2.0-SNAPSHOT
|         \--- provided
\--- com.google.dagger:dagger-compiler:2.0-SNAPSHOT (*)

(*) - dependencies omitted (listed previously)

I think it's not a missing jar because it would have failed with a class not found instead.

melix commented 9 years ago

One last option would be that the compile classpath for Groovy doesn't include Guava, which would in turn be provided from Gradle itself. It sounds unlikely and I would expect that to be a Gradle bug, but I need to check this too.

edit: doesn't seem to be the case.

Sarev0k commented 9 years ago

I just reproduced the issue with a pure groovy sample in a Gradle build, so I could rule out the Android build as a contributor to this failure. Sorry for presuming it was something wrong with your plugin.

I found some interesting things in the debug logs though. Here are the arguments being passed to the groovy compiler. As you can see the classpath looks in order.

13:05:49.734 [DEBUG] [org.gradle.api.internal.tasks.compile.NormalizingGroovyCompiler] Java compiler arguments: -d /Users/werickson/AndroidStudioProjects/PureGroovyDaggerGuavaFailureDemo/build/classes/main -g -classpath /Users/werickson/.gradle/caches/modules-2/files-2.1/javax.annotation/jsr250-api/1.0/5025422767732a1ab45d93abfea846513d742dcf/jsr250-api-1.0.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger-compiler/2.0-SNAPSHOT/d021463ddfd1b5c04a4118293391418843277a78/dagger-compiler-2.0-SNAPSHOT.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger/2.0-SNAPSHOT/cadd885d40b816fd98ec11c4421786024da7ca76/dagger-2.0-SNAPSHOT.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.4.0-rc-1/95865a25ae70317679c0c5739baaa884a7016422/groovy-2.4.0-rc-1.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.auto/auto-common/1.0-SNAPSHOT/42f4d366b24b3fdd56950e6cfaf0ecd141d101bf/auto-common-1.0-SNAPSHOT.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.squareup/javawriter/2.5.0/81241ff7078ef14f42ea2a8995fa09c096256e6b/javawriter-2.5.0.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar:/Users/werickson/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar:/Users/werickson/AndroidStudioProjects/PureGroovyDaggerGuavaFailureDemo/build/classes/main /Users/werickson/AndroidStudioProjects/PureGroovyDaggerGuavaFailureDemo/src/main/groovy/com/example/myapplication/AppModule.java

However, if you look at this log line, for the compiler daemon Gradle is setting up, you'll see that it's got its own eversion of guava on the classpath:

13:05:50.262 [INFO] [org.gradle.process.internal.DefaultExecHandle] Starting process 'Gradle Compiler Daemon 1'. Working directory: /Users/werickson/AndroidStudioProjects/PureGroovyDaggerGuavaFailureDemo Command: /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/bin/java -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -cp /Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-base-services-2.2.1.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-core-2.2.1.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-cli-2.2.1.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-native-2.2.1.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-messaging-2.2.1.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/slf4j-api-1.7.7.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/logback-classic-1.0.13.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/logback-core-1.0.13.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/jul-to-slf4j-1.7.7.jar:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/guava-jdk5-17.0.jar org.gradle.process.internal.launcher.GradleWorkerMain 'Gradle Compiler Daemon 1'

I then pulled out the Queue class from this version of guava, and ran javap on it to look at it's public interface:

$ javap -public ./com/google/common/collect/Queues.class
        Compiled from "Queues.java"
public final class com.google.common.collect.Queues {
public static <E extends java/lang/Object> java.util.concurrent.ArrayBlockingQueue<E> newArrayBlockingQueue(int);
public static <E extends java/lang/Object> java.util.concurrent.ConcurrentLinkedQueue<E> newConcurrentLinkedQueue();
public static <E extends java/lang/Object> java.util.concurrent.ConcurrentLinkedQueue<E> newConcurrentLinkedQueue(java.lang.Iterable<? extends E>);
public static <E extends java/lang/Object> java.util.concurrent.LinkedBlockingQueue<E> newLinkedBlockingQueue();
public static <E extends java/lang/Object> java.util.concurrent.LinkedBlockingQueue<E> newLinkedBlockingQueue(int);
public static <E extends java/lang/Object> java.util.concurrent.LinkedBlockingQueue<E> newLinkedBlockingQueue(java.lang.Iterable<? extends E>);
public static <E extends java/lang/Comparable> java.util.concurrent.PriorityBlockingQueue<E> newPriorityBlockingQueue();
public static <E extends java/lang/Comparable> java.util.concurrent.PriorityBlockingQueue<E> newPriorityBlockingQueue(java.lang.Iterable<? extends E>);
public static <E extends java/lang/Comparable> java.util.PriorityQueue<E> newPriorityQueue();
public static <E extends java/lang/Comparable> java.util.PriorityQueue<E> newPriorityQueue(java.lang.Iterable<? extends E>);
public static <E extends java/lang/Object> java.util.concurrent.SynchronousQueue<E> newSynchronousQueue();
public static <E extends java/lang/Object> int drain(java.util.concurrent.BlockingQueue<E>, java.util.Collection<? super E>, int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
public static <E extends java/lang/Object> int drainUninterruptibly(java.util.concurrent.BlockingQueue<E>, java.util.Collection<? super E>, int, long, java.util.concurrent.TimeUnit);
public static <E extends java/lang/Object> java.util.Queue<E> synchronizedQueue(java.util.Queue<E>);
        }

As you can see newArrayDeque is nowhere to be seen.

My thought is if that compiler daemon isn't using an isolated classloader when invoking the groovy compilation, it's version of guava might be what the Dagger annotation processor is seeing.

Do you know of a way to conclusively prove or disprove that this is the problem?

Thanks!

Sarev0k commented 9 years ago

I think I just found the smoking gun.

I just put together another sample project with a custom annotation processor that outputs all the jars in the annotation processor's classpath.

==Classes Loaded From My Annotation Processor's Classloader==
file:/Users/werickson/AndroidStudioProjects/PureGroovyGuavaFailureDemoWithCustomAnnotationProcessor/MyProcessor/build/libs/MyProcessor.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.4.0-rc-1/95865a25ae70317679c0c5739baaa884a7016422/groovy-2.4.0-rc-1.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger/2.0-SNAPSHOT/cadd885d40b816fd98ec11c4421786024da7ca76/dagger-2.0-SNAPSHOT.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar
file:/Users/werickson/AndroidStudioProjects/PureGroovyGuavaFailureDemoWithCustomAnnotationProcessor/build/classes/main/

Queue is from: jar:file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/guava-jdk5-17.0.jar!/com/google/common/collect/Queues.class

==Classes Loaded From Queues ClassLoader==
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-base-services-2.2.1.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-core-2.2.1.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-cli-2.2.1.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-native-2.2.1.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/gradle-messaging-2.2.1.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/slf4j-api-1.7.7.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/logback-classic-1.0.13.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/logback-core-1.0.13.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/jul-to-slf4j-1.7.7.jar
file:/Users/werickson/.gradle/wrapper/dists/gradle-2.2.1-bin/88n1whbyjvxg3s40jzz5ur27/gradle-2.2.1/lib/guava-jdk5-17.0.jar

==Queue Methods==
public static java.util.concurrent.ArrayBlockingQueue com.google.common.collect.Queues.newArrayBlockingQueue(int)
public static java.util.concurrent.ConcurrentLinkedQueue com.google.common.collect.Queues.newConcurrentLinkedQueue(java.lang.Iterable)
public static java.util.concurrent.ConcurrentLinkedQueue com.google.common.collect.Queues.newConcurrentLinkedQueue()
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue(java.lang.Iterable)
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue(int)
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue()
public static java.util.concurrent.PriorityBlockingQueue com.google.common.collect.Queues.newPriorityBlockingQueue(java.lang.Iterable)
public static java.util.concurrent.PriorityBlockingQueue com.google.common.collect.Queues.newPriorityBlockingQueue()
public static java.util.PriorityQueue com.google.common.collect.Queues.newPriorityQueue(java.lang.Iterable)
public static java.util.PriorityQueue com.google.common.collect.Queues.newPriorityQueue()
public static java.util.concurrent.SynchronousQueue com.google.common.collect.Queues.newSynchronousQueue()
public static int com.google.common.collect.Queues.drainUninterruptibly(java.util.concurrent.BlockingQueue,java.util.Collection,int,long,java.util.concurrent.TimeUnit)
public static java.util.Queue com.google.common.collect.Queues.synchronizedQueue(java.util.Queue)
public static int com.google.common.collect.Queues.drain(java.util.concurrent.BlockingQueue,java.util.Collection,int,long,java.util.concurrent.TimeUnit) throws java.lang.InterruptedException

As you can see from this output, it looks like the Gradle jars are leaking into the class space of the Annotation Processor.

For your reference, here's what it looks like if I place AppModule in src/main/java:

==Classes Loaded From My Annotation Processor's Classloader==
file:/Users/werickson/AndroidStudioProjects/PureGroovyGuavaFailureDemoWithCustomAnnotationProcessor/MyProcessor/build/libs/MyProcessor.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.4.0-rc-1/95865a25ae70317679c0c5739baaa884a7016422/groovy-2.4.0-rc-1.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger/2.0-SNAPSHOT/cadd885d40b816fd98ec11c4421786024da7ca76/dagger-2.0-SNAPSHOT.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar

Queue is from: jar:file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar!/com/google/common/collect/Queues.class

==Classes Loaded From Queues ClassLoader==
file:/Users/werickson/AndroidStudioProjects/PureGroovyGuavaFailureDemoWithCustomAnnotationProcessor/MyProcessor/build/libs/MyProcessor.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.4.0-rc-1/95865a25ae70317679c0c5739baaa884a7016422/groovy-2.4.0-rc-1.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/com.google.dagger/dagger/2.0-SNAPSHOT/cadd885d40b816fd98ec11c4421786024da7ca76/dagger-2.0-SNAPSHOT.jar
file:/Users/werickson/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar

==Queue Methods==
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue()
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue(int)
public static java.util.concurrent.LinkedBlockingQueue com.google.common.collect.Queues.newLinkedBlockingQueue(java.lang.Iterable)
public static int com.google.common.collect.Queues.drain(java.util.concurrent.BlockingQueue,java.util.Collection,int,long,java.util.concurrent.TimeUnit) throws java.lang.InterruptedException
public static java.util.concurrent.ArrayBlockingQueue com.google.common.collect.Queues.newArrayBlockingQueue(int)
public static java.util.ArrayDeque com.google.common.collect.Queues.newArrayDeque()
public static java.util.ArrayDeque com.google.common.collect.Queues.newArrayDeque(java.lang.Iterable)
public static java.util.concurrent.ConcurrentLinkedQueue com.google.common.collect.Queues.newConcurrentLinkedQueue()
public static java.util.concurrent.ConcurrentLinkedQueue com.google.common.collect.Queues.newConcurrentLinkedQueue(java.lang.Iterable)
public static java.util.concurrent.LinkedBlockingDeque com.google.common.collect.Queues.newLinkedBlockingDeque()
public static java.util.concurrent.LinkedBlockingDeque com.google.common.collect.Queues.newLinkedBlockingDeque(int)
public static java.util.concurrent.LinkedBlockingDeque com.google.common.collect.Queues.newLinkedBlockingDeque(java.lang.Iterable)
public static java.util.concurrent.PriorityBlockingQueue com.google.common.collect.Queues.newPriorityBlockingQueue(java.lang.Iterable)
public static java.util.concurrent.PriorityBlockingQueue com.google.common.collect.Queues.newPriorityBlockingQueue()
public static java.util.PriorityQueue com.google.common.collect.Queues.newPriorityQueue()
public static java.util.PriorityQueue com.google.common.collect.Queues.newPriorityQueue(java.lang.Iterable)
public static java.util.concurrent.SynchronousQueue com.google.common.collect.Queues.newSynchronousQueue()
public static int com.google.common.collect.Queues.drainUninterruptibly(java.util.concurrent.BlockingQueue,java.util.Collection,int,long,java.util.concurrent.TimeUnit)
public static java.util.Queue com.google.common.collect.Queues.synchronizedQueue(java.util.Queue)
public static java.util.Deque com.google.common.collect.Queues.synchronizedDeque(java.util.Deque)

Do you think I have enough here to file a bug with the Gradle team?

Sarev0k commented 9 years ago

Just filed a bug with the Gradle folks.

I'm closing this issue, and will be tracking it there from now on.

Thanks so much for your help!

melix commented 9 years ago

Thanks for the update!

mg6maciej commented 9 years ago

Hey @Sarev0k, Is there a workaround for this issue when using Cedric's plugin until this is fixed in Gradle ( https://github.com/gradle/gradle/pull/384 )?
Seems I've been following your footsteps, but I don't know how would I disable forking for groovyCompile on Android.

Sarev0k commented 9 years ago

You need to do the following:

androidGroovy {
    options {
        groovyOptions.fork = false
    }
}

However, once you get past this, you're going to run into this issue.

I've got a pull request open to fix this issue, but it hasn't been merged yet. I've been told that Gradle 2.4 will be the earliest version of Gradle that this makes it into.

Edit: Ah, just saw that you already saw my pull request. Aside from using my fork of Gradle, I don't think there's an immediate work around to this problem.

mg6maciej commented 9 years ago

Thanks for a really quick reply. I tried this snippet (could have figured it out from this repo's README.md) and that part worked. As you predicted, I also run into an issue, where stubs are not checked for annotations.

I switched to original Dagger as a last resort before abandoning Groovy... and it works. Didn't even have to disable forking.

Sarev0k commented 9 years ago

Are you sure you're using the original dagger in static mode instead of dynamic mode?

In Gradle builds, I'd expect the annotation processor not to process annotations on groovy stubs regardless of what version of dagger you're using.

The only way you could get around that I believe is if dagger is using reflection to do dependency injection. And if that's the case, I'm not sure how much more performant it is to guice on Android.

mg6maciej commented 9 years ago

You are correct about such setup using dynamic mode. I see no $$InjectAdapter classes in build/ directory and there is normally plenty of them.
I'll stick to it for now and compile your fork of Gradle if there will be need for a faster startup.

cgruber commented 9 years ago

Ok - I'm sorry I missed this ping - and am only getting up to speed on it now. The specific issue in play was a guava version mismatch, as com.google.common.collect.Queues.newArrayDeque. But I'd have to dig in and replicate this a bit. It is entirely possible that it works with latter daggers because there's an implicit dep on Guava at a later version, so you're getting the later version which then has this method.

I'm not sure if the issue is gradle related or a mis-configuration. If the build tool/compiler classpath is not aligned with the dagger-compiler plugin, it's entirely possible that this issue could rear its head the way it seems to above.

The issue could be fixed by shading (jar-jaring, whatever) dagger as we have with AutoValue and the like. In such a case it would have no deps conflicts (reasonable since it's a deployable binary in a sense anyway) but that would simply mean that dagger is resistant to isolation issues in your setup, but those isolation issues remain.

Sarev0k commented 9 years ago

Hi @cgruber this was a bug with how Gradle isolates its classpath for forked builds.

There's already an issue file to fix this

I don't think jar-jaring should be necessary.

cgruber commented 9 years ago

Ok - though for other reasons I plan to do it (avoiding incompatible guava versions or auto-common versions between processors, etc.)

On Tue Feb 03 2015 at 2:00:46 PM Will Erickson notifications@github.com wrote:

Hi @cgruber https://github.com/cgruber this was a bug with how Gradle isolates its classpath for forked builds.

There's already an issue file to fix this http://forums.gradle.org/gradle/topics/gradle-overrides-the-specified-guava-dependencies-with-its-own-when-running-an-annotation-processor-from-a-groovy-build

I don't think jar-jaring should be necessary.

— Reply to this email directly or view it on GitHub https://github.com/groovy/groovy-android-gradle-plugin/issues/32#issuecomment-72744460 .

cgruber commented 9 years ago

(thanks though!)

On Wed Feb 04 2015 at 11:06:56 AM Christian Gruber < christianedwardgruber@gmail.com> wrote:

Ok - though for other reasons I plan to do it (avoiding incompatible guava versions or auto-common versions between processors, etc.)

On Tue Feb 03 2015 at 2:00:46 PM Will Erickson notifications@github.com wrote:

Hi @cgruber https://github.com/cgruber this was a bug with how Gradle isolates its classpath for forked builds.

There's already an issue file to fix this http://forums.gradle.org/gradle/topics/gradle-overrides-the-specified-guava-dependencies-with-its-own-when-running-an-annotation-processor-from-a-groovy-build

I don't think jar-jaring should be necessary.

— Reply to this email directly or view it on GitHub https://github.com/groovy/groovy-android-gradle-plugin/issues/32#issuecomment-72744460 .

Sarev0k commented 9 years ago

Gradle 2.4 will ship with a fix to this issue.

It's also introducing a change that allows java annotation processors to run against Groovy stubs.

mg6maciej commented 9 years ago

That's a great news. Thanks Will! That's one reason not to use Groovy for Android going to disappear.

FireZenk commented 9 years ago

Im trying to get it work (Dagger2 + Groovy) and it does not auto-generate classes (DaggerGlobalComponent):

Application:

component = DaggerGlobalComponent.builder()
                .busModule(new BusModule())
                .serviceModule(new ServiceModule())
                .contextModule(new ContextModule(this))
                .persistenceModule(new PersistenceModule(this))
                .build();

build.gradle:

androidGroovy {
    options {
        sourceCompatibility = '1.7'
        targetCompatibility = '1.7'
        groovyOptions.fork = false
    }
}

gradle-wrapper.properties:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip

The error:

Error:(37, 21) error: cannot find symbol variable DaggerGlobalComponent
startup failed:
Compilation failed; see the compiler error output for details.
Error:Execution failed for task ':app:compileDevelDebugGroovy'.
> Compilation failed; see the compiler error output for details.
BUILD FAILED

any ideas?

Sarev0k commented 9 years ago

It works for me.

I've created the following demo that demonstrates it working.

Your problem is most likely caused by your DaggerGlobalComponent being named differently than you think. If it's an inner class of an Application for instance, it would be called DaggerMyApplicationName_GlobalComponent.

If you take a look at your generated java sources under build/intermediates/classes/debug/your/package/name/, you should be able to figure out what your component is called.

FireZenk commented 9 years ago

@Sarev0k with your demo:

Error:Execution failed for task ':compileDebugAidl'.
> Executor Singleton not started

And on generated sources folder of my project:

BuildConfig.class
Manifest.class
Manifest$permission.class
R.class
R$anim.class
R$attr.class
R$bool.class
R$color.class
R$dimen.class
R$drawable.class
R$id.class
R$integer.class
R$layout.class
R$mipmap.class
R$plurals.class
R$raw.class
R$string.class
R$style.class
R$styleable.class
Sarev0k commented 9 years ago

That demo works. Make sure you've modified the project.properties to point at your android SDK.

Since you don't have any generated classes it doesn't look like you've defined your dependencies on Dagger2 correctly in your build.gradle.

If you would like more help, please recreate your issue with a simple project and provide a link to a zip of it.

FireZenk commented 9 years ago

Here is the project, launching them you can reproduce the issue: https://github.com/FireZenk/maat Also into MaatApplication.groovy you can see that DaggerGlobalComponent can't be imported Thanks for your help

Sarev0k commented 9 years ago

Build is failing because of this error:

/home/werickson/temp/maat/app/build/tmp/compileDevelDebugGroovy/groovy-java-stubs/org/firezenk/maat/managers/BaseActivity.java:10: error: cannot access DrawerLayoutImpl
public abstract class BaseActivity
                ^
  class file for android.support.v4.widget.DrawerLayoutImpl not found
startup failed:
Compilation failed; see the compiler error output for details.

I don't think this is a very simple sample. Looking at the build.gradle for this package, there are a lot of moving parts, including additional plugins that are likely to introduce noise into this investigation.

Can you please reproduce this in a package that just integrates with Gradle, Groovy, Dagger2 and Android using no other plugins or dependencies? This will allow us to better isolate what your problem really is.

I've verified that the sample project I already linked you to works on multiple machines. Perhaps you should try to figure out why that sample is failing on your machine, as there might be something wrong with your setup.

mg6maciej commented 9 years ago

The DrawerLayoutImpl issue is "normal" for me. You just need to build the app again and it will work. Issue was also described here: https://github.com/groovy/groovy-android-gradle-plugin/issues/34

Sarev0k commented 9 years ago

You're right, building again solved that issue. However, this project's build still fails due to a Crashalytics problem:

java.lang.IllegalArgumentException: Crashlytics found an invalid API key: CRASHLYTICS_API_KEY. 
Check the Crashlytics plugin to make sure that the application has been added successfully! 
Contact support@crashlytics.com for assistance.

However, I did get far enough to see that Dagger2 did generate the class that @FireZenk said was missing:

$ tree ./app/build/intermediates/classes/production/debug/org/firezenk/maat/
./app/build/intermediates/classes/production/debug/org/firezenk/maat/
├── annotations
│   └── AnnotationParser.class
├── BuildConfig.class
├── fragments
│   └── BaseFragment.class
├── MaatApplication.class
├── managers
│   ├── BaseActivity.class
│   └── MainActivity.class
├── Manifest.class
├── Manifest$permission.class
├── modules
│   ├── BusModule.class
│   ├── BusModule_ProvideEventBusFactory.class
│   ├── BusModule_ProvideEventBusFactory.java
│   ├── ContextModule.class
│   ├── ContextModule_ProvideContextFactory.class
│   ├── ContextModule_ProvideContextFactory.java
│   ├── DaggerGlobalComponent$1.class
│   ├── DaggerGlobalComponent$Builder.class
│   ├── DaggerGlobalComponent.class
│   ├── DaggerGlobalComponent.java
│   ├── GlobalComponent.class
│   ├── PersistenceModule.class
│   ├── PersistenceModule_ProvidePersistenceFactory.class
│   ├── PersistenceModule_ProvidePersistenceFactory.java
│   ├── ServiceModule$1.class
│   ├── ServiceModule.class
│   ├── ServiceModule_ProvideServiceFactory.class
│   └── ServiceModule_ProvideServiceFactory.java
├── network
│   ├── MaatService.class
│   └── TokenEntity.class
├── persistence
│   └── Persistence.class
├── R$anim.class
├── R$attr.class
├── R$bool.class
├── R.class
├── R$color.class
├── R$dimen.class
├── R$drawable.class
├── R$id.class
├── R$integer.class
├── R$layout.class
├── R$mipmap.class
├── R$string.class
├── R$styleable.class
├── R$style.class
├── utils
│   ├── DateUtils.class
│   └── Preferences.class
└── views
    └── notifications
        └── PushNotificationsReceiver.class

And it looks like the reason why DaggerGlobalComponent is unable to be imported is because you're missing the import for this class.

From the above output you can see that DaggerGlobalComponent's full path is: org.firezenk.maat.modules.DaggerGlobalComponent

However, the class that's referencing it has the path of: org.firezenk.maat.MaatApplication

If classes aren't in the same Java package, an import is required.

FireZenk commented 9 years ago

Regarding crashlytics issue: you need an api_key or remove the following line on the Android Manifest: <meta-data android:name="com.crashlytics.ApiKey" android:value="CRASHLYTICS_API_KEY"/>

Also comment lines from 30 to 35 of MaatAplication is needed.

I tried to put: import org.firezenk.maat.modules.DaggerGlobalComponent on MaatApplication.groovy and IDE say that cannot resolve it

Sarev0k commented 9 years ago

I tried to put: import org.firezenk.maat.modules.DaggerGlobalComponent on MaatApplication.groovy and IDE say that cannot resolve it

This is expected. Android Studio is kind of crap when it comes to its annotation processor integration.

However, building from Android Studio or command line should still work, and you shouldn't have a runtime failure when trying to use that class.

Perhaps the Dagger2 folks have some suggestions for the best way to resolve generated sources in Android Studio.

mg6maciej commented 9 years ago

Most people use apt instead of provided for compile-time dependencies like dagger-compiler.

Here is some info how to set it up: https://bitbucket.org/hvisser/android-apt