From 82ff408722e8754eeb51480b226daa219c255808 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 30 Oct 2024 13:27:20 +0100 Subject: [PATCH] Revert "Merge pull request #2410 from jchyb/fix-leftover-betasty" This reverts commit 57eb43031cc5aa656961af8948502b7358d52d20, reversing changes made to fa29730058bc0c8e5ece623858aab3706069b6f8. --- .../scala/bloop/BloopClassFileManager.scala | 42 +++-- backend/src/main/scala/bloop/Compiler.scala | 164 ++++++------------ .../scala/bloop/util/BestEffortUtils.scala | 13 +- .../inc/bloop/BloopZincCompiler.scala | 7 +- docs/contributing-guide.md | 25 --- .../bloop/engine/tasks/CompileTask.scala | 89 ++-------- .../tasks/compilation/CompileGraph.scala | 15 +- .../scala/bloop/bsp/BspMetalsClientSpec.scala | 9 +- 8 files changed, 117 insertions(+), 247 deletions(-) diff --git a/backend/src/main/scala/bloop/BloopClassFileManager.scala b/backend/src/main/scala/bloop/BloopClassFileManager.scala index 53c8dcf9bc..103bbaa374 100644 --- a/backend/src/main/scala/bloop/BloopClassFileManager.scala +++ b/backend/src/main/scala/bloop/BloopClassFileManager.scala @@ -219,20 +219,36 @@ final class BloopClassFileManager( clientTracer.traceTaskVerbose("copy new products to external classes dir") { _ => val config = ParallelOps.CopyConfiguration(5, CopyMode.ReplaceExisting, Set.empty, Set.empty) - - ParallelOps - .copyDirectories(config)( - newClassesDir, - clientExternalClassesDir.underlying, - inputs.ioScheduler, - enableCancellation = false, - inputs.logger - ) - .map { walked => - readOnlyCopyDenylist.++=(walked.target) + val clientExternalBestEffortDir = + clientExternalClassesDir.underlying.resolve("META-INF/best-effort") + + // Deletes all previous best-effort artifacts to get rid of all of the outdated ones. + // Since best effort compilation is not affected by incremental compilation, + // all relevant files are always produced by the compiler. Because of this, + // we can always delete all previous files and copy newly created ones + // without losing anything in the process. + val deleteClientExternalBestEffortDir = + Task { + if (Files.exists(clientExternalBestEffortDir)) { + BloopPaths.delete(AbsolutePath(clientExternalBestEffortDir)) + } () - } - .flatMap(_ => deleteAfterCompilation) + }.memoize + + deleteClientExternalBestEffortDir *> + ParallelOps + .copyDirectories(config)( + newClassesDir, + clientExternalClassesDir.underlying, + inputs.ioScheduler, + enableCancellation = false, + inputs.logger + ) + .map { walked => + readOnlyCopyDenylist.++=(walked.target) + () + } + .flatMap(_ => deleteAfterCompilation) } } ) diff --git a/backend/src/main/scala/bloop/Compiler.scala b/backend/src/main/scala/bloop/Compiler.scala index 9c190e6d9e..759990d773 100644 --- a/backend/src/main/scala/bloop/Compiler.scala +++ b/backend/src/main/scala/bloop/Compiler.scala @@ -1,8 +1,6 @@ package bloop import java.io.File -import java.io.PrintWriter -import java.io.StringWriter import java.nio.file.Files import java.nio.file.Path import java.util.Optional @@ -251,8 +249,7 @@ object Compiler { def compile( compileInputs: CompileInputs, isBestEffortMode: Boolean, - isBestEffortDep: Boolean, - firstCompilation: Boolean + isBestEffortDep: Boolean ): Task[Result] = { val logger = compileInputs.logger val tracer = compileInputs.tracer @@ -378,9 +375,6 @@ object Compiler { reporter.reportStartCompilation(previousProblems, wasPreviousSuccessful) val fileManager = newFileManager - val shouldAttemptRestartingCompilationForBestEffort = - firstCompilation && !isBestEffortDep && previousAnalysis.isDefined - // Manually skip redundant best-effort compilations. This is necessary because compiler // phases supplying the data needed to skip compilations in zinc remain unimplemented for now. val noopBestEffortResult = compileInputs.previousCompilerResult match { @@ -389,9 +383,7 @@ object Compiler { t, elapsed, _, - bestEffortProducts @ Some( - BestEffortProducts(previousCompilationResults, previousHash, _) - ) + bestEffortProducts @ Some(BestEffortProducts(previousCompilationResults, previousHash)) ) if isBestEffortMode => val newHash = BestEffortUtils.hashResult( previousCompilationResults.newClassesDir, @@ -466,8 +458,7 @@ object Compiler { fileManager, cancelPromise, tracer, - classpathOptions, - !(isBestEffortMode && isBestEffortDep) + classpathOptions ) .materialize .doOnCancel(Task(cancel())) @@ -480,9 +471,10 @@ object Compiler { () => elapsed, reporter, backgroundTasksWhenNewSuccessfulAnalysis, + allInvalidatedClassFilesForProject, + allInvalidatedExtraCompileProducts, previousSuccessfulProblems, - errorCause = None, - shouldAttemptRestartingCompilationForBestEffort + None ) case Success(result) => // Report end of compilation only after we have reported all warnings from previous runs @@ -508,17 +500,6 @@ object Compiler { Task(persist(out, analysis, result.setup, tracer, logger)).memoize } - // .betasty files are always produced with -Ybest-effort, even when - // the compilation is successful. - // We might want to change this in the compiler itself... - def deleteBestEffortDir() = - if (isBestEffortMode) - Task( - BloopPaths - .delete(compileOut.internalNewClassesDir.resolve("META-INF/best-effort")) - ) - else Task {} - val isNoOp = previousAnalysis.contains(analysis) if (isNoOp) { // If no-op, return previous result with updated classpath hashes @@ -548,25 +529,16 @@ object Compiler { val clientClassesDir = clientClassesObserver.classesDir clientLogger.debug(s"Triggering background tasks for $clientClassesDir") val updateClientState = - Task - .gatherUnordered( - List( - deleteClientExternalBestEffortDirTask(clientClassesDir), - deleteBestEffortDir() - ) - ) - .flatMap { _ => - updateExternalClassesDirWithReadOnly( - clientClassesDir, - clientTracer, - clientLogger, - compileInputs, - readOnlyClassesDir, - readOnlyCopyDenylist, - allInvalidatedClassFilesForProject, - allInvalidatedExtraCompileProducts - ) - } + updateExternalClassesDirWithReadOnly( + clientClassesDir, + clientTracer, + clientLogger, + compileInputs, + readOnlyClassesDir, + readOnlyCopyDenylist, + allInvalidatedClassFilesForProject, + allInvalidatedExtraCompileProducts + ) val writeAnalysisIfMissing = { if (compileOut.analysisOut.exists) Task.unit @@ -600,8 +572,7 @@ object Compiler { ) .flatMap(_ => publishClientAnalysis) .onErrorHandleWith(err => { - clientLogger.debug("Caught error in background tasks"); - clientLogger.trace(err); + clientLogger.debug("Caught error in background tasks"); clientLogger.trace(err); Task.raiseError(err) }) .doOnFinish(_ => Task(clientReporter.reportEndCompilation())) @@ -645,22 +616,11 @@ object Compiler { ): Task[Unit] = { val clientClassesDir = clientClassesObserver.classesDir val successBackgroundTasks = - Task - .gatherUnordered( - List( - deleteBestEffortDir(), - deleteClientExternalBestEffortDirTask(clientClassesDir) - ) - ) - .flatMap { _ => - Task.gatherUnordered( - backgroundTasksWhenNewSuccessfulAnalysis - .map(f => f(clientClassesDir, clientReporter, clientTracer)) - ) - } + backgroundTasksWhenNewSuccessfulAnalysis + .map(f => f(clientClassesDir, clientReporter, clientTracer)) val persistTask = persistAnalysis(analysisForFutureCompilationRuns, compileOut.analysisOut) - val initialTasks = List(persistTask, successBackgroundTasks) + val initialTasks = persistTask :: successBackgroundTasks.toList val allClientSyncTasks = Task.gatherUnordered(initialTasks).flatMap { _ => // Only start these tasks after the previous IO tasks in the external dir are done val firstTask = updateExternalClassesDirWithReadOnly( @@ -736,9 +696,10 @@ object Compiler { () => elapsed, reporter, backgroundTasksWhenNewSuccessfulAnalysis, + allInvalidatedClassFilesForProject, + allInvalidatedExtraCompileProducts, previousSuccessfulProblems, - errorCause = Some(cause), - shouldAttemptRestartingCompilationForBestEffort + Some(cause) ) case Failure(_: xsbti.CompileCancelled) => handleCancellation @@ -755,13 +716,9 @@ object Compiler { Result.Failed(failedProblems, None, elapsed, backgroundTasks, None) case t: Throwable => t.printStackTrace() - val sw = new StringWriter() - t.printStackTrace(new PrintWriter(sw)) - logger.error(sw.toString()) val backgroundTasks = toBackgroundTasks(backgroundTasksForFailedCompilation.toList) - val failedProblems = findFailedProblems(reporter, None) - Result.Failed(failedProblems, Some(t), elapsed, backgroundTasks, None) + Result.Failed(Nil, Some(t), elapsed, backgroundTasks, None) } } } @@ -964,11 +921,13 @@ object Compiler { elapsed: () => Long, reporter: ZincReporter, backgroundTasksWhenNewSuccessfulAnalysis: mutable.ListBuffer[CompileBackgroundTasks.Sig], + allInvalidatedClassFilesForProject: mutable.HashSet[File], + allInvalidatedExtraCompileProducts: mutable.HashSet[File], previousSuccessfulProblems: List[ProblemPerPhase], - errorCause: Option[xsbti.CompileFailed], - shouldAttemptRestartingCompilation: Boolean + errorCause: Option[xsbti.CompileFailed] ): Result = { val uniqueInputs = compileInputs.uniqueInputs + val readOnlyClassesDir = compileOut.internalReadOnlyClassesDir.underlying val newClassesDir = compileOut.internalNewClassesDir.underlying reporter.processEndCompilation( @@ -985,7 +944,7 @@ object Compiler { ) val products = CompileProducts( - newClassesDir, // let's not use readonly dir + readOnlyClassesDir, newClassesDir, noOpPreviousResult, noOpPreviousResult, @@ -1002,15 +961,22 @@ object Compiler { ): Task[Unit] = { val clientClassesDir = clientClassesObserver.classesDir val successBackgroundTasks = - deleteClientExternalBestEffortDirTask(clientClassesDir).flatMap { _ => - Task.gatherUnordered( - backgroundTasksWhenNewSuccessfulAnalysis - .map(f => f(clientClassesDir, clientReporter, clientTracer)) - ) - } - val allClientSyncTasks = successBackgroundTasks.flatMap { _ => - // Only start this task after the previous IO tasks in the external dir are done - Task { + backgroundTasksWhenNewSuccessfulAnalysis + .map(f => f(clientClassesDir, clientReporter, clientTracer)) + val allClientSyncTasks = Task.gatherUnordered(successBackgroundTasks.toList).flatMap { _ => + // Only start these tasks after the previous IO tasks in the external dir are done + val firstTask = updateExternalClassesDirWithReadOnly( + clientClassesDir, + clientTracer, + clientLogger, + compileInputs, + readOnlyClassesDir, + readOnlyCopyDenylist = mutable.HashSet.empty, + allInvalidatedClassFilesForProject, + allInvalidatedExtraCompileProducts + ) + + val secondTask = Task { // Delete everything outside of betasty and semanticdb val deletedCompileProducts = BloopClassFileManager.supportedCompileProducts.filter(_ != ".betasty") :+ ".class" @@ -1019,32 +985,28 @@ object Compiler { .filter(path => Files.isRegularFile(path)) .filter(path => deletedCompileProducts.exists(path.toString.endsWith(_))) .forEach(Files.delete(_)) - }.map(_ => ()) + } + Task + .gatherUnordered(List(firstTask, secondTask)) + .map(_ => ()) } allClientSyncTasks.doOnFinish(_ => Task(clientReporter.reportEndCompilation())) } } - if (shouldAttemptRestartingCompilation) { - BloopPaths.delete(compileOut.internalNewClassesDir) - } - - val newHash = - if (!shouldAttemptRestartingCompilation) - BestEffortUtils.hashResult( - products.newClassesDir, - compileInputs.sources, - compileInputs.classpath - ) - else "" + val newHash = BestEffortUtils.hashResult( + products.newClassesDir, + compileInputs.sources, + compileInputs.classpath + ) val failedProblems = findFailedProblems(reporter, errorCause) Result.Failed( failedProblems, None, elapsed(), backgroundTasksExecution, - Some(BestEffortProducts(products, newHash, shouldAttemptRestartingCompilation)) + Some(BestEffortProducts(products, newHash)) ) } @@ -1212,20 +1174,4 @@ object Compiler { } } } - - // Deletes all previous best-effort artifacts to get rid of all of the outdated ones. - // Since best effort compilation is not affected by incremental compilation, - // all relevant files are always produced by the compiler. Because of this, - // we can always delete all previous files and copy newly created ones - // without losing anything in the process. - def deleteClientExternalBestEffortDirTask(clientClassesDir: AbsolutePath) = { - val clientExternalBestEffortDir = - clientClassesDir.underlying.resolve("META-INF/best-effort") - Task { - if (Files.exists(clientExternalBestEffortDir)) { - BloopPaths.delete(AbsolutePath(clientExternalBestEffortDir)) - } - () - }.memoize - } } diff --git a/backend/src/main/scala/bloop/util/BestEffortUtils.scala b/backend/src/main/scala/bloop/util/BestEffortUtils.scala index f9dd5cbc37..318507fe0d 100644 --- a/backend/src/main/scala/bloop/util/BestEffortUtils.scala +++ b/backend/src/main/scala/bloop/util/BestEffortUtils.scala @@ -1,21 +1,16 @@ package bloop.util -import java.math.BigInteger -import java.nio.file.Files -import java.nio.file.Path import java.security.MessageDigest +import java.math.BigInteger +import java.nio.file.Files import scala.collection.JavaConverters._ - +import java.nio.file.Path import bloop.io.AbsolutePath object BestEffortUtils { - case class BestEffortProducts( - compileProducts: bloop.CompileProducts, - hash: String, - recompile: Boolean - ) + case class BestEffortProducts(compileProducts: bloop.CompileProducts, hash: String) /* Hashes results of a projects compilation, to mimic how it would have been handled in zinc. * Returns SHA-1 of a project. diff --git a/backend/src/main/scala/sbt/internal/inc/bloop/BloopZincCompiler.scala b/backend/src/main/scala/sbt/internal/inc/bloop/BloopZincCompiler.scala index 16ed24903d..775163ebc4 100644 --- a/backend/src/main/scala/sbt/internal/inc/bloop/BloopZincCompiler.scala +++ b/backend/src/main/scala/sbt/internal/inc/bloop/BloopZincCompiler.scala @@ -58,8 +58,7 @@ object BloopZincCompiler { manager: ClassFileManager, cancelPromise: Promise[Unit], tracer: BraveTracer, - classpathOptions: ClasspathOptions, - withPreviousResult: Boolean + classpathOptions: ClasspathOptions ): Task[CompileResult] = { val config = in.options() val setup = in.setup() @@ -82,8 +81,8 @@ object BloopZincCompiler { scalacOptions, javacOptions, classpathOptions, - if (withPreviousResult) in.previousResult.analysis.toOption else None, - if (withPreviousResult) in.previousResult.setup.toOption else None, + in.previousResult.analysis.toOption, + in.previousResult.setup.toOption, perClasspathEntryLookup, reporter, order, diff --git a/docs/contributing-guide.md b/docs/contributing-guide.md index 64d3349b30..807ce423c3 100644 --- a/docs/contributing-guide.md +++ b/docs/contributing-guide.md @@ -242,28 +242,3 @@ indices. - Push the new tag and wait for the release - Announce the release after the release notes are published in the most recent release. - - -# Best effort compilation pipeline - -As of writing this part of the doc this is an experimental set of settings implemented -in the Scala 3 compiler (starting with 3.5.x). They allow the compiler to return artifacts -even when the compilation fails (returning `.betasty` files instead of `.class` and `.tasty`). -It also at this point does not support incremental compilation. This all requires special -handling from the build tool, mostly located in `Compiler.scala`, `CompileTask.scala` -and `CompileGraph.scala`: -- We save best effort artifacts separately, and allow dependent projects to compile using -that, even when the compilation has failed. If the project compiles we discard the best effort -artifacts. -- First, we try compiling partially (only the modified files), expecting regular successful compilation -- If that at some point fails, we discard the immediate results and recompile the whole module -expecting .betasty files. We do not ever move them to a readOnly directory. That readOnly directory -is also not used in dependent compilations. -- We do not try to recompile if we know we are compiling the whole module to begin with (e.g. because we -are depending on .betasty from different project, or because this is the first compilation and we -do not have any previous incremental compilation analysis). -- If we try to recompile a module that we previously compiled for .betasty, we once again, try to -recompile it 2 times - once incrementally expecting success (recompiling all files changed since -the last successful compilation, as dictated by the incremental compilation analysis) and then -recompile all - this works out to be faster than discarding the last successful result and jumping -between full successful recompilation and full best effort recompilation. diff --git a/frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala b/frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala index 907bddcf9b..0366d8829c 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala @@ -1,7 +1,5 @@ package bloop.engine.tasks -import java.util.Optional - import scala.collection.mutable import scala.concurrent.Promise @@ -10,8 +8,8 @@ import bloop.CompileInputs import bloop.CompileOutPaths import bloop.CompileProducts import bloop.Compiler -import bloop.Compiler.Result.Failed import bloop.Compiler.Result.Success +import bloop.Compiler.Result.Failed import bloop.cli.ExitStatus import bloop.data.Project import bloop.data.WorkspaceSettings @@ -22,10 +20,10 @@ import bloop.engine.State import bloop.engine.caches.LastSuccessfulResult import bloop.engine.tasks.compilation.FinalCompileResult import bloop.engine.tasks.compilation._ -import bloop.io.AbsolutePath import bloop.io.ParallelOps import bloop.io.ParallelOps.CopyMode import bloop.io.{Paths => BloopPaths} +import bloop.io.AbsolutePath import bloop.logging.DebugFilter import bloop.logging.Logger import bloop.logging.LoggerAction @@ -41,9 +39,6 @@ import bloop.util.BestEffortUtils.BestEffortProducts import monix.execution.CancelableFuture import monix.reactive.MulticastStrategy import monix.reactive.Observable -import xsbti.compile.CompileAnalysis -import xsbti.compile.MiniSetup -import xsbti.compile.PreviousResult object CompileTask { private implicit val logContext: DebugFilter = DebugFilter.Compilation @@ -177,35 +172,8 @@ object CompileTask { // Block on the task associated with this result that sets up the read-only classes dir waitOnReadClassesDir.flatMap { _ => // Only when the task is finished, we kickstart the compilation - def compile(inputs: CompileInputs) = { - val firstResult = - Compiler.compile(inputs, isBestEffort, isBestEffortDep, firstCompilation = true) - firstResult.flatMap { - case result @ Compiler.Result.Failed( - _, - _, - _, - _, - Some(BestEffortProducts(_, _, recompile)) - ) if recompile => - // we restart the compilation, starting from scratch (without any previous artifacts) - inputs.reporter.reset() - val emptyResult = - PreviousResult.of(Optional.empty[CompileAnalysis], Optional.empty[MiniSetup]) - val newInputs = inputs.copy( - sources = inputs.sources, - previousCompilerResult = result, - previousResult = emptyResult - ) - Compiler.compile( - newInputs, - isBestEffort, - isBestEffortDep, - firstCompilation = false - ) - case result => Task(result) - } - } + def compile(inputs: CompileInputs) = + Compiler.compile(inputs, isBestEffort, isBestEffortDep) inputs.flatMap(inputs => compile(inputs)).map { result => def runPostCompilationTasks( backgroundTasks: CompileBackgroundTasks @@ -499,49 +467,22 @@ object CompileTask { logger.debug(s"Scheduling to delete ${previousClassesDir} superseded by $newClassesDir") Some(previousClassesDir) } - case _ => None - } - - val previousBestEffortToDelete = previousResult match { - case Some(Failed(_, _, _, _, Some(BestEffortProducts(previousProducts, _, _)))) => - val newClassesDirOpt = compilerResult match { - case Success(_, _, products, _, _, _, _) => Some(products.newClassesDir) - case Failed(_, _, _, _, Some(BestEffortProducts(products, _, _))) => - Some(products.newClassesDir) + case Failed(_, _, _, _, Some(BestEffortProducts(products, _))) => + val newClassesDir = products.newClassesDir + previousResult match { + case Some(Failed(_, _, _, _, Some(BestEffortProducts(previousProducts, _)))) => + val previousClassesDir = previousProducts.newClassesDir + if (previousClassesDir != newClassesDir) { + logger.debug( + s"Scheduling to delete ${previousClassesDir} superseded by $newClassesDir" + ) + Some(AbsolutePath(previousClassesDir)) + } else None case _ => None } - val previousClassesDir = previousProducts.newClassesDir - - newClassesDirOpt.flatMap { newClassesDir => - if (previousClassesDir != newClassesDir) { - logger.debug( - s"Scheduling to delete ${previousClassesDir} superseded by $newClassesDir" - ) - Some(AbsolutePath(previousClassesDir)) - } else None - } case _ => None } - def deleteOrphanDir(orphanDir: Option[AbsolutePath]): Task[Unit] = - orphanDir match { - case None => Task.unit - case Some(classesDir) => - Task.eval { - logger.debug(s"Deleting contents of orphan dir $classesDir") - BloopPaths.delete(classesDir) - } - } - - // Task - // .gatherUnordered( - // List( - // deleteOrphanDir(previousReadOnlyToDelete), - // deleteOrphanDir(previousBestEffortToDelete) - // ) - // ) - // .map(_ => ()) - // .asyncBoundary previousReadOnlyToDelete match { case None => Task.unit case Some(classesDir) => diff --git a/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala b/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala index 1aab0af1ef..8002ead70f 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/compilation/CompileGraph.scala @@ -27,9 +27,9 @@ import bloop.logging.DebugFilter import bloop.logging.LoggerAction import bloop.reporter.ReporterAction import bloop.task.Task -import bloop.util.BestEffortUtils.BestEffortProducts import bloop.util.JavaCompat.EnrichOptional import bloop.util.SystemProperties +import bloop.util.BestEffortUtils.BestEffortProducts import xsbti.compile.PreviousResult @@ -389,7 +389,7 @@ object CompileGraph { PartialFailure(bundle.project, FailedOrCancelledPromise, Task.now(results)) } - def loop(dag: Dag[Project]): CompileTraversal = { + def loop(dag: Dag[Project], isBestEffortDep: Boolean): CompileTraversal = { tasks.get(dag) match { case Some(task) => task case None => @@ -397,7 +397,6 @@ object CompileGraph { case Leaf(project) => val bundleInputs = BundleInputs(project, dag, Map.empty) setupAndDeduplicate(client, bundleInputs, computeBundle) { bundle => - val isBestEffortDep = false compile(Inputs(bundle, Map.empty), bestEffortAllowed && project.isBestEffort, isBestEffortDep).map { results => results.fromCompiler match { case Compiler.Result.Ok(_) => Leaf(partialSuccess(bundle, results)) @@ -407,13 +406,13 @@ object CompileGraph { } case Aggregate(dags) => - val downstream = dags.map(loop(_)) + val downstream = dags.map(loop(_, isBestEffortDep = false)) Task.gatherUnordered(downstream).flatMap { dagResults => Task.now(Parent(PartialEmpty, dagResults)) } case Parent(project, dependencies) => - val downstream = dependencies.map(loop(_)) + val downstream = dependencies.map(loop(_, isBestEffortDep = false)) Task.gatherUnordered(downstream).flatMap { dagResults => val depsSupportBestEffort = dependencies.map(Dag.dfs(_, mode = Dag.PreOrder)).flatten.forall(_.isBestEffort) @@ -434,7 +433,7 @@ object CompileGraph { case _ => false } val continue = bestEffortAllowed && depsSupportBestEffort && successfulBestEffort || failed.isEmpty - val dependsOnBestEffort = failed.nonEmpty && bestEffortAllowed && depsSupportBestEffort + val dependsOnBestEffort = failed.nonEmpty && bestEffortAllowed && depsSupportBestEffort || isBestEffortDep if (!continue) { // Register the name of the projects we're blocked on (intransitively) @@ -454,7 +453,7 @@ object CompileGraph { .+=(newProducts.readOnlyClassesDir.toFile -> newResult) case (p, ResultBundle(f: Compiler.Result.Failed, _, _, _)) => f.bestEffortProducts.foreach { - case BestEffortProducts(products, _, _) => + case BestEffortProducts(products, _) => dependentProducts += (p -> Right(products)) } case _ => () @@ -480,7 +479,7 @@ object CompileGraph { } } - loop(dag) + loop(dag, isBestEffortDep = false) } private def errorToString(err: Throwable): String = { diff --git a/frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala b/frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala index 955f687cbe..8f28bce51d 100644 --- a/frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala +++ b/frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala @@ -665,9 +665,8 @@ class BspMetalsClientSpec( ) loadBspState(workspace, projects, logger, "Metals", bloopExtraParams = extraParams) { state => val compiledState = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState - // we remove betasty from successful compilations - assertNoBetastyFile("A.betasty", compiledState, "A") - assertNoBetastyFile("B.betasty", compiledState, "A") + assertBetastyFile("A.betasty", compiledState, "A") + assertBetastyFile("B.betasty", compiledState, "A") assertCompilationFile("A.class", compiledState, "A") updateProject(updatedFile1WithError) val compiledState2 = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState @@ -678,8 +677,8 @@ class BspMetalsClientSpec( updateProject(updatedFile2WithoutError) val compiledState3 = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState assertNoBetastyFile("A.betasty", compiledState3, "A") - assertNoBetastyFile("B.betasty", compiledState3, "A") - assertNoBetastyFile("C.betasty", compiledState3, "A") + assertBetastyFile("B.betasty", compiledState3, "A") + assertBetastyFile("C.betasty", compiledState3, "A") assertCompilationFile("B.class", compiledState, "A") updateProject(updatedFile3WithError) val compiledState4 = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState