Skip to content

Commit a6755ff

Browse files
committed
Store source generators' states
Previously, Bloop would not store the state of source generators after their run, which would cause them to always be re-run, even when there are no changes.
1 parent c51f8d9 commit a6755ff

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

frontend/src/main/scala/bloop/engine/caches/SourceGeneratorCache.scala

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,25 @@ import bloop.logging.Logger
99
import bloop.task.Task
1010

1111
final class SourceGeneratorCache private (
12-
cache: ConcurrentHashMap[SourceGenerator, SourceGenerator.Run]
12+
cache: ConcurrentHashMap[SourceGenerator, Task[SourceGenerator.Run]]
1313
) {
1414
def update(
1515
sourceGenerator: SourceGenerator,
1616
logger: Logger,
1717
opts: CommonOptions
1818
): Task[List[AbsolutePath]] = {
19-
val previous = getStateFor(sourceGenerator)
20-
sourceGenerator.update(previous, logger, opts).map {
21-
case SourceGenerator.NoRun => Nil
22-
case SourceGenerator.PreviousRun(_, outputs) => outputs.keys.toList.sortBy(_.syntax)
23-
}
24-
}
25-
26-
private def getStateFor(sourceGenerator: SourceGenerator): SourceGenerator.Run = {
27-
Option(cache.get(sourceGenerator)).getOrElse(SourceGenerator.NoRun)
19+
cache
20+
.compute(
21+
sourceGenerator,
22+
{ (_, prev) =>
23+
val previous = Option(prev).getOrElse(Task.now(SourceGenerator.NoRun))
24+
previous.flatMap(sourceGenerator.update(_, logger, opts)).memoize
25+
}
26+
)
27+
.map {
28+
case SourceGenerator.NoRun => Nil
29+
case SourceGenerator.PreviousRun(_, outputs) => outputs.keys.toList.sortBy(_.syntax)
30+
}
2831
}
2932
}
3033

frontend/src/test/scala/bloop/SourceGeneratorSpec.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,25 @@ object SourceGeneratorSpec extends bloop.testing.BaseSuite {
209209
}
210210
}
211211

212+
test("source generator is not re-run when nothing has changed") {
213+
singleProjectWithSourceGenerator("glob:*.in" :: Nil) { (workspace, project, state) =>
214+
val generatorOutput = project.config.sourceGenerators
215+
.flatMap(_.headOption)
216+
.map(p => AbsolutePath(p.outputDirectory))
217+
writeFile(workspace.resolve("hello.in"), "hello")
218+
writeFile(project.srcFor("test.scala", exists = false), assertNInputs(n = 1))
219+
val compiledState1 = state.compile(project)
220+
val origHash = sourceHashFor("NameLengths_1.scala", project, compiledState1)
221+
assertExitStatus(compiledState1, ExitStatus.Ok)
222+
223+
val compiledState2 = compiledState1.compile(project)
224+
assertExitStatus(compiledState2, ExitStatus.Ok)
225+
226+
val newHash = sourceHashFor("NameLengths_1.scala", project, compiledState2)
227+
assertEquals(origHash, newHash)
228+
}
229+
}
230+
212231
private def assertNInputs(n: Int): String =
213232
s"""class Foo {
214233
| val length = generated.NameLengths.args_${n}

0 commit comments

Comments
 (0)