-
Notifications
You must be signed in to change notification settings - Fork 201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove betasty directory on successful compilation backgroundTasks #2410
Conversation
Looks like this indeed does help in case of the repository I sent your way, but the Scala compiler is failing with cryptic stuff like:
any idea why? |
This is still pretty unfinished, however I am almost done with the working version. After just this change the incremental compiler interactions can be incorrect, so I also implemented a thing where we properly restart the compilation after the first unsuccessful one (this was also incorrect before, but it was not really noticable, due to us still having betasty we could use from the previous compilation - I'll try to explain this specific issue and the fix later, after I push the code here). Also one race condition I noticed caused a crash from one of the reports we got this week, so that also should be fixed. With that said none of these fixes fix the behavior in the compiler unfortunately. The main issue there seems to be with the java source files, which I am pretty sure we do not handle yet in best effort compilation :(. When I tested the best effort compilation in the compiler I ended up running into a compiler crash and errors related to missing java symbols - now I can't be 100% sure whether the crash was also caused by the java files, but the compiler is not always able to recover gracefully from the missing symbols, so that might be it... I am sure there is some roundabout way to work with those even without updating the compiler, however it might take more time (I'll work on it next week). |
Wouldn't that be caused by the problems with compilation? So if the Scala source do not compile and produce classfiles, the Java sources which use them will not be able to compile either. Especially that the compilation mode in the compiler is "mixed". I would say the Java compilation issues are not relevant to the actual problem. |
Also, obviously it should always recover after fixing the issues, no idea why it does not do that in the compiler codebase, so I'll probably start investigating with this. EDIT: I wrote this before I saw the comment above, but maybe it answers the doubt there? The java stuff should not matter for successful compilations, it only produces additional fake errors/other issues when the compilation is failing. |
(I pushed it but I still want to recheck it/ clean it up, so I am leaving this as a draft for now) |
In the meantime let me quickly explain why we do those recompilations here now:
Lets say the first Utils compilation would give us: |
5936fa2
to
9b1b8f6
Compare
6a92110
to
615db68
Compare
case t: Throwable => | ||
t.printStackTrace() | ||
val sw = new StringWriter() | ||
t.printStackTrace(new PrintWriter(sw)) | ||
logger.info(sw.toString()) | ||
val backgroundTasks = | ||
toBackgroundTasks(backgroundTasksForFailedCompilation.toList) | ||
Result.Failed(Nil, Some(t), elapsed, backgroundTasks, None) | ||
val failedProblems = findFailedProblems(reporter, None) | ||
Result.Failed(failedProblems, Some(t), elapsed, backgroundTasks, None) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes here are not strictly related to best effort, but not returning failedProblems in the case of compiler crash caused those annoying leftover errors in the problem tab in the compiler codebase (I can't see why it wouldn't behave the same way even for crashes unrelated to best effort). I also figured that it might be useful to also log the stacktraces of those crashes, although maybe a different log level would be better (debug?).
About the compiler crash itself (as seen while using best effort in the compiler codebase), the fix for that will have to be in the compiler (I already have a preliminary fix working there) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this!
Task | ||
.gatherUnordered(List(firstTask, secondTask)) | ||
.map(_ => ()) | ||
.forEach(path => if (Files.exists(path)) Files.delete(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.forEach(path => if (Files.exists(path)) Files.delete(path)) | |
.forEach(path => Files.delete(path) |
Isn't this checked above? We could probably join the 2 filters and a foreach now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this is unchanged? Would you mind including the change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed now, sorry about that
5f822e1
to
1bd2b10
Compare
Since the last review I figured out an elegant way to keep the previous successful artifacts along with CompileAnalysis and reuse those, while also being able to have the best effort information simultaneously. To do this, if we already have an analysis and successful artifacts, we always first try compiling everything changed/tasked with recompilation since last successful, and then if something fails along the way recompiling the whole project in best effort mode. Compiling twice in case of every failure might not seem performant, but it's actually the opposite. The first compilation is going to be short, since (likely) very few files are going to be recompiled. Second will be a bit longer, but still manageable, as we end the compilation at an earlier phase. This is better then the alternative, when in case of failure we replace everything with To put this into perspective, in the reproduction repository I was given, when we add a typer error to a compiled file, the first erroring compilation on my machine takes around I did make sure to test whether the standard incremental compilation behaviors still work - zinc schedules recompilations and updates the analyses only for successful compilations, so it all still works (e.g. I add an indirect error -> the file is successfully compiled -> another few files are scheduled for recompilation -> this recompilation still fails -> every "second" compilation will try to recompile those files and also newly changed ones). I made it a bit confusing here, but the main thing is that it I did not have to add any extra logic for that to work. Since that compilation order aspect started to get a bit complex, I added a chapter in the contributors guide about it (which might be a bit weird, since there is not much else like it there, so having implementation details there might be a bit confusing, but it's better than nothing in my opinion). |
frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala
Show resolved
Hide resolved
def compile(inputs: CompileInputs) = | ||
Compiler.compile(inputs, isBestEffort, isBestEffortDep) | ||
def compile(inputs: CompileInputs) = { | ||
val firstResult = Compiler.compile(inputs, isBestEffort, isBestEffortDep, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val firstResult = Compiler.compile(inputs, isBestEffort, isBestEffortDep, true) | |
val firstResult = Compiler.compile(inputs, isBestEffort, isBestEffortDep, firstCompilation = true) |
previousCompilerResult = result, | ||
previousResult = emptyResult | ||
) | ||
Compiler.compile(newInputs, isBestEffort, isBestEffortDep, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compiler.compile(newInputs, isBestEffort, isBestEffortDep, false) | |
Compiler.compile(newInputs, isBestEffort, isBestEffortDep, firstCompilation = false) |
Task | ||
.gatherUnordered(List(firstTask, secondTask)) | ||
.map(_ => ()) | ||
.forEach(path => if (Files.exists(path)) Files.delete(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this is unchanged? Would you mind including the change?
…lyses Also better cleanup directories
1bd2b10
to
8ec5c8d
Compare
frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Fix to scalameta/metals#6628
Usually it was fine to always put betasty files at the end of the classpath, but in the reproduction for some reason despite the code being correct, those files were still being used, which resulted in an incoherent state in bloop (we obtained only betasty files on what was being considered non-best effort compilation). This lead to trying to apply incremental compilation there, producing singular betasty files, and losing information about symbols from other files, producing errors.
The fix is very simple, but so far I have been unable to properly add any tests for it, as the
backgroundTasks
do not seem to be run in the tests suites.