evant / gradle-retrolambda

A gradle plugin for getting java lambda support in java 6, 7 and android
Apache License 2.0
5.3k stars 449 forks source link

apt generated directory not found. #65

Closed dbachelder closed 9 years ago

dbachelder commented 9 years ago

gradle-retrolambda version 2.4.1 / android-apt 1.4 / android gradle (0.14.1) / tools 21.1.0

Suddenly we are seeing strange issues in our build process.. there are no diffs to any gradle files.

However, we now see this:

:models:patchAndroidJar
:models:_compileReleaseJava
javac: directory not found: 
/projects/android/models/build/generated/source/apt/release

and this is the order of applied plugins:

apply plugin: 'com.android.library'
apply plugin: 'retrolambda'
apply plugin: 'android-apt'

Everything was working fine a few hours ago.. and checking out source to yesterday doesn't seem to help. Is anyone else noticing bizarre issues today? Or am I completely nuts? I'm at a bit of a loss at the moment.

dbachelder commented 9 years ago

well... just in case this rings anyones bell... I was trying to debug the problem on a new branch. I am able to do a ./gradlew clean :module:assembleDebug one time after checking out the branch then it fails each subsequent attempt... switching back to the other branch gives me the same behavior.. 1 success.. then failures. I can go back and forth like this ad nauseam.

I'll continue to debug.

dbachelder commented 9 years ago

So... I finally hacked a version of the plugin together that works consistently again...

I have no idea why this stopped working for us.. but suddenly I need to ensure the -s argument points at a directory that exists... so I did this in RetrolambdaPluginAndroid around line 82

 newJavaCompile.doFirst {
    var.javaCompile.options.compilerArgs[-1].mkdirs(); // added this line.... 
    newJavaCompile.options.compilerArgs = var.javaCompile.options.compilerArgs + ["-bootclasspath", "$jarPath/android.jar"]
}

I know this is not the correct answer, so if someone who understands the interplay between the various plugins could enlighten me on what is going wrong and why this hack works, I would appreciate it!

dbachelder commented 9 years ago

That change only worked for library projects... for our actual apps I needed to change it..

var.javaCompile.options.compilerArgs.find { it instanceof File }?.mkdirs()

Just more brute force... but if the assumption that the only compilerArg that's a File is the -s arg for both libraries and apps holds up.. this works. We can now build again with this hack... I would still like to understand what has happened to require this. It seems like android-apt is not being run at the correct time in the phase where it should be to create <module>/build/generated/source/apt/release

dbachelder commented 9 years ago

This is all becoming clear to me now... I have no idea how this was ever working.

in android-apt we do this:

variant.javaCompile.options.compilerArgs += [
                '-processorpath', processorPath,
                '-s', aptOutput
]
... snip ...
variant.javaCompile.doFirst {
    aptOutput.mkdirs()
}

UPDATE: I had the execution order backwards in my original description of what I ultimately thought the problem was... it's actually this... i think:

                    var.javaCompile.deleteAllActions()

Which removes the doFirst that was added by android-apt. @evant why is that done? @hvisser is there any reason not to create the aptOutput dir in afterEvaluate or somewhere else that is less prone to modifications to the actions on the compile task?

dbachelder commented 9 years ago

Ah, it looks like others have run into this issue as well:

https://bitbucket.org/hvisser/android-apt/issue/24/source-folders-generated-at-incorrect

evant commented 9 years ago

Thank you for looking into this, I have attempted to release a fix for the android-apt plugin but apparently it's not as effective as I had hoped. I've gone through several iterations on how to insert/replace the retrolambda tasks into the java compile sequence, and deleting the original task and creating my own is the best I've come up with so far. I'm aware that deleteAllActions() deletes the doFirst as well, but I don't know of any gradle api to only delete the original task action, nor to manually run the doFirst/doLast blocks.

hvisser commented 9 years ago

Like I commented on the android-apt issue: creating the directory at project eval time won't work, since it might get deleted when you do a clean for example. I'm not sure if there's a better way to make sure the directory exists at the time the compile tasks runs...

dbachelder commented 9 years ago

Thanks @hvisser

@evant I have created a PR with the fix I am now using locally... https://github.com/evant/gradle-retrolambda/pull/66

hvisser commented 9 years ago

@evant Wouldn't it be possible to copy the original tasks to the newJavaCompile as well? Or maybe a bit more hacky, invoke the actions in the Task.actions list one by one? doFirst and doLast only manipulate the list of actions if I understand correctly.

evant commented 9 years ago

Like I said before, I haven't seen any gradle api's that let me do that. If you come up with one I'm missing, I'm all ears.

hvisser commented 9 years ago

Something like javaCompile.actions.each { it -> newJavaCompile.doFirst(it) } Haven't tried it :)

evant commented 9 years ago

That would include the original javaCompile action which I'm trying to delete! I've looked at actions before, but their types are all internal, so I don't think I can even reliably switch on it.

evant commented 9 years ago

Just to note, the way I attempted to fix this in 2.4.1 was to try to make sure android-apt added it's doFirst block after retrolmabda set up the configuration. That way would be called after deleteAllActions() and still be run. I would like to try to figure out why exactly that is not working as expected.

hvisser commented 9 years ago

@evant Oh, sorry, I think I misunderstood what the code was doing then. I assumed there was a JavaCompile task (http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.compile.JavaCompile.html) that got configured. Does that have actions by default?

hamen commented 9 years ago

Any news about this? I'm still having

Error:Execution failed for task ':app:_compileDebugJava'.
> directory not found: [...] /app/build/generated/source/apt/debug

I'm using retrolambda 2.4.1, applying plugin like this:

apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.application'
apply plugin: 'retrolambda'
apply plugin: 'com.neenbedankt.android-apt'

Of course, I can manually create the folder locally and be able to build, but it will still crash on the remote CI server. Thank you

evant commented 9 years ago

Sorry, I've been busy this past week. This issue is not easy to solve and it doesn't help that it builds correctly on my machine. If you can give any insight into why the order of modifications (deleteAllActions() then android-apt adding it's deleteFirst block) isn't working correctly on your setup, it would be very helpful.

hamen commented 9 years ago

I'm using Travis as CI. You could try to reproduce the bug on Travis and check a setup that's different than yours.

almozavr commented 9 years ago

Reproduced with enroscar's reactive goodies. Sorting order plays no role. If I create apt/debug folder by hand – works with no problems.

apply plugin: 'com.android.application'
apply plugin: 'retrolambda'
apply plugin: 'com.stanfy.android.apt'

Maybe issue is linked with this issue?

hvisser commented 9 years ago

@almozavr No, totally unrelated. The issue is that the retrolambda plugin clears the custom actions set on the compile task and there's no real way to get those tasks back. @evant One dirty hack I can think of is to inspect the new JavaCompile task and check the number of actions. Then compare that to the task you are clearing and copy over the extra actions from the old task. From debugging it looks like tasks are added at the front on of the actions list. But it's a gross hack :)

mgrzechocinski commented 9 years ago

Hi, is there any progress with this?

You can check my sample project to reproduce it: https://github.com/mgrzechocinski/dagger2-example/tree/topic/retrolambda-issue ./gradlew clean assembleDebug ./gradlew clean assembleRelease

evant commented 9 years ago

Thanks for the sample project, that helped me narrow it down a bit. I think it's because the original javaCompile task is getting into a state where it thinks it's already up to date and so doesn't run again. See if doing it with --rerun-tasks once fixes it, at least temporarily.

evant commented 9 years ago

I've uploaded a SNAPSHOT version that appearers to fix the issue. Can I get some people who are experiencing this to test with the snapshot version?

buildscript {
  repositories {
    maven {
      url "https://oss.sonatype.org/content/repositories/snapshots"
    }
  }
  dependencies {
    classpath 'me.tatarka:gradle-retrolambda:2.4.2-SNAPSHOT'
  }
}

Important! If you are upgrading a project from an older version, run gradle build --rerun-tasks. You should only have to do this once.

hamen commented 9 years ago

The SNAPSHOT is working here. +1 kudos :) Thank you

evant commented 9 years ago

Alright, version 2.5.0 with this fix is released.

mgrzechocinski commented 9 years ago

Yep, seems like 2.5.0 fixed the issue. Thanks a lot for your support!

Being curious about the change, I reviewed your commit and have one possible hint/question regarding Gradle & overriding standard tasks behaviour.

My previous project had some native code build by NDK. Since standard NDK support was not enough for us, we developed custom NDK task which suited our needs. The only problem we had was to force Gradle to use our task instead of built-into Android plugin. At first, I did it by just creating native task dependent or our, and also make is actions as empty set:

    def nativeGradleComplikeNdkTask = "compile${variant.name.capitalize()}Ndk" {
        actions = [];
    }
    nativeGradleComplikeNdkTask.dependsOn(compileNdkLegacy)

I worked perfectly since Gradle plugin v0.13. Unfortunately, keeping set of actions as empty stopped working in v0.13 where Google guys (I guess) introduced some additional behaviour in doFirst/doLast methods. In our case it occurred as output generated by our task was just simply cleared by one of the actions in doFirst/doLast in native gradle tasks. Since native actions set was empty, no *.so file was bundled in final APK.

So how I fixed it? I eventually disabled native task by:

    def nativeGradleComplikeNdkTask = "compile${variant.name.capitalize()}Ndk" {
        enabled = false;
    }
    nativeGradleComplikeNdkTask.dependsOn(compileNdkLegacy)

That works like a charm in every version of Android Gradle plugin.

So the question is: in retrolambda case, isn't it better to just disable native compile task and introduce dependent one instead of removing it? Thanks in advance for your comments. I'm just curious about it.

evant commented 9 years ago

I believe I tried that first and it didn't work correctly, but I can't remember exactly what the issue was. Edit: I just tried doing var.javaCompile.enabled = false, and I'm getting the same apt directory not found error. I guess that causes it to disable doFirst/doLast blacks as well?

ultraon commented 9 years ago

I've got the same issue with retrolambda version 3.0.1, with version 2.5.0 - works good. Need to reopen this issue.

JoanZapata commented 9 years ago

@ultraon same thing here

ian-ellis commented 8 years ago

Happening here with 'me.tatarka:gradle-retrolambda:3.2.5'