Skip to content

Commit

Permalink
Fix issue with BE compilations incorrectly reusing successful artifacts
Browse files Browse the repository at this point in the history
Also fixes an issue with best effort compilation not being properly
cached, if recompiled from successful compilations
  • Loading branch information
jchyb committed Nov 28, 2024
1 parent e79a78e commit 32dbfb1
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 4 deletions.
6 changes: 4 additions & 2 deletions backend/src/main/scala/bloop/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ object Compiler {
val newHash = BestEffortUtils.hashResult(
previousCompilationResults.newClassesDir,
compileInputs.sources,
compileInputs.classpath
compileInputs.classpath,
List(compileOut.internalReadOnlyClassesDir, compileOut.internalNewClassesDir)
)

if (newHash == previousHash) {
Expand Down Expand Up @@ -1036,7 +1037,8 @@ object Compiler {
BestEffortUtils.hashResult(
products.newClassesDir,
compileInputs.sources,
compileInputs.classpath
compileInputs.classpath,
List(compileOut.internalReadOnlyClassesDir, compileOut.internalNewClassesDir)
)
else ""
val failedProblems = findFailedProblems(reporter, errorCause)
Expand Down
8 changes: 6 additions & 2 deletions backend/src/main/scala/bloop/util/BestEffortUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ object BestEffortUtils {
def hashResult(
outputDir: Path,
sources: Array[AbsolutePath],
classpath: Array[AbsolutePath]
classpath: Array[AbsolutePath],
ignoredClasspathDirectory: List[AbsolutePath]
): String = {
val md = MessageDigest.getInstance("SHA-1")

Expand All @@ -52,7 +53,10 @@ object BestEffortUtils {

md.update("<classpath>".getBytes())
classpath.map(_.underlying).foreach { classpathFile =>
if (!Files.exists(classpathFile)) ()
if (
!Files.exists(classpathFile) || ignoredClasspathDirectory
.exists(_.underlying == classpathFile)
) ()
else if (Files.isRegularFile(classpathFile)) {
md.update(Files.readAllBytes(classpathFile))
} else if (Files.isDirectory(classpathFile)) {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/main/scala/bloop/engine/tasks/CompileTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,11 @@ object CompileTask {
inputs.reporter.reset()
val emptyResult =
PreviousResult.of(Optional.empty[CompileAnalysis], Optional.empty[MiniSetup])
val nonIncrementalClasspath =
inputs.classpath.filter(_ != inputs.out.internalReadOnlyClassesDir)
val newInputs = inputs.copy(
sources = inputs.sources,
classpath = nonIncrementalClasspath,
previousCompilerResult = result,
previousResult = emptyResult
)
Expand Down
54 changes: 54 additions & 0 deletions frontend/src/test/scala/bloop/bsp/BspMetalsClientSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,60 @@ class BspMetalsClientSpec(
}
}

test(
"best-effort: do not use previous successful compilation artifacts on best effort compilations"
) {
val initFile =
"""/Source.scala
|object A {
| def usesB: B = ???
|}
|trait B
|""".stripMargin
val updatedFile =
"""/Source.scala
|object A {
| def usesB: B = ???
|}
|""".stripMargin
TestUtil.withinWorkspace { workspace =>
val `A` = TestProject(
workspace,
"A",
List(initFile),
scalaVersion = Some(bestEffortScalaVersion)
)
def updateProject(content: String) =
Files.write(`A`.config.sources.head.resolve("Source.scala"), content.getBytes())
val projects = List(`A`)
TestProject.populateWorkspace(workspace, projects)
val logger = new RecordingLogger(ansiCodesSupported = false)
val extraParams = BloopExtraBuildParams(
ownsBuildFiles = None,
clientClassesRootDir = None,
semanticdbVersion = Some(semanticdbVersion),
supportedScalaVersions = Some(List(bestEffortScalaVersion)),
javaSemanticdbVersion = None,
enableBestEffortMode = Some(true)
)
loadBspState(workspace, projects, logger, "Metals", bloopExtraParams = extraParams) { state =>
val compiledState = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState
assert(compiledState.status == ExitStatus.Ok)
updateProject(updatedFile)
val compiledState2 = state.compile(`A`, arguments = Some(List("--best-effort"))).toTestState
val compilationResult =
compiledState2.results.latestResult(compiledState2.build.getProjectFor("A").get)
assert(compiledState2.status == ExitStatus.CompilationError)
compilationResult match {
case Compiler.Result.Failed(problems, _, _, _, _) =>
assert(problems.exists(_.problem.message.contains("Not found: type B")))
case _ =>
assert(false)
}
}
}
}

test("compile is successful with semanticDB and javac processorpath") {
TestUtil.withinWorkspace { workspace =>
val logger = new RecordingLogger(ansiCodesSupported = false)
Expand Down

0 comments on commit 32dbfb1

Please sign in to comment.