[2.x] feat: Cache failed compilation to avoid repeated failures#8490
[2.x] feat: Cache failed compilation to avoid repeated failures#8490eed3si9n merged 9 commits intosbt:developfrom
Conversation
When a compilation fails with CompileFailed, the failure is now cached so that subsequent builds with the same inputs don't re-run the failed compilation. This significantly improves the experience when using BSP clients like Metals that may trigger many compilations in a row. The implementation: - Adds CachedCompileFailure, CachedProblem, and CachedPosition types to serialize compilation failures - Modifies ActionCache.cache to catch CompileFailed exceptions and store them in the cache with exitCode=1 - On cache lookup, checks for cached failures first and re-throws the cached exception if found - Fixes DiskActionCacheStore.put to preserve exitCode from request - Adds unit test to verify cached failure behavior Fixes sbt#7662
|
all passed! |
...${OUT}/value/sha256-8769430c2339a7cbaaa5b190624c7baa20f54d092424131f0055699b423ce6cc/48.json
Outdated
Show resolved
Hide resolved
Address review feedback from eed3si9n: 1. Use existing ProblemFormats, SeverityFormats, and PositionFormats instead of custom CachedProblem and CachedPosition types. This simplifies CachedCompileFailure significantly. 2. Use single cache lookup with exitCode to distinguish success/failure. Avoids 2x network calls when remote cache is configured. Both success and failure use the same input digest. exitCode=0 for success, exitCode=1 for failure.
b168702 to
794279d
Compare
|
I think we should keep going with this idea, but one concern I have is that sometimes people use |
…band Address review feedback from eed3si9n: 1. Add GenericFailure type to actionresult.contra for binary compatibility. The type uses a 'kind' field to distinguish different failure types (e.g., 'CompileFailed') for future extensibility. 2. CachedCompileFailure now wraps GenericFailure instead of being a standalone case class. 3. Custom JsonFormat provided since xsbti.ProblemFormats doesn't exist in the xsbti package (it's in sbt.internal.util.codec).
|
all passed! please review it again and approve if it's ok |
|
I built sbt locally and tried to use it: sbt:aaa> compile
[info] compiling 2 Scala sources to /private/tmp/aaa/target/out/jvm/scala-3.7.3/aaa/classes ...
[error] -- [E103] Syntax Error: /private/tmp/aaa/src/main/scala/example/A.scala:3:0 ----
[error] 3 |aaclass A
[error] |^^^^^^^
[error] |Illegal start of toplevel definition
[error] |
[error] | longer explanation available when compiling with `-explain`
[error] -- [E040] Syntax Error: /private/tmp/aaa/src/main/scala/example/Main.scala:5:0 -
[error] 5 |
[error] |^
[error] |')' expected, but eof found
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] elapsed time: 0 s, cache 84%, 1 disk cache hit, 10 symlink cache hits, 2 onsite taskssbt:aaa> compile
[error] stack trace is suppressed; run last Compile / compileIncremental for the full output
[error] (Compile / compileIncremental) sbt.util.CachedCompileFailure$$anon$1: Compilation failed
[error] elapsed time: 0 s, cache 100%, 1 cached-failure cache hit, 1 disk cache hit, 11 symlink cache hitsFor command-line usage, I don't think this is usable without also relaying the problem information back to the log. Using scalaVersion := "3.7.3"the problem list contains scalaVersion := "2.13.18"the problem list contains Also for Metals usage, where OP originally wanted this feature to be useful, I wonder if we also need to replay the problem to LSP reporter so VS Code would have it. I haven't tested this with Metals yet. |
Address eed3si9n's feedback that cached failures weren't displaying errors/warnings on subsequent compile runs. Changes: 1. Add replay() method to CachedCompileFailure that logs problems to the console when a cached failure is thrown. 2. Add formatProblem() that handles both Scala 3 (uses rendered field) and Scala 2.13 (constructs message from position + message). 3. Add hasSufficientInfo() to check if problems have enough info to be useful when replayed (for future use). 4. Call replay() in ActionCache before throwing cached failure.
Thanks for testing! I've added a For Metals/LSP, I haven't tested that yet - will look into whether we need to replay to the BSP reporter as well. |
|
For Metals/LSP, I think the issue is that when a cached One option would be to wrap the cached compile task with a try-catch that calls the BSP methods on |
|
Looking at the code, TaskZero / compileIncremental is uncached, so it's possible that we already have that try catch for us. You can put println temporarily in there to check: sbt/main/src/main/scala/sbt/Defaults.scala Lines 2188 to 2192 in add43bd |
|
I tested with println and confirmed that the cached Debug output on second compile: So the BSP reporter notification should already work for Metals without additional changes. The error replay to the log is also working as shown in the output. |
|
All passed! Can you merge it now? |
When a compilation fails with CompileFailed, the failure is now cached so that subsequent builds with the same inputs don't re-run the failed compilation. This significantly improves the experience when using BSP clients like Metals that may trigger many compilations in a row.
The implementation:
Fixes #7662
Contribution by Gittensor, see my contribution statistics at https://gittensor.io/miners/details?githubId=94194147