Cache pipelined Java compilation

This commit is contained in:
Anatolii Kmetiuk 2026-08-10 11:11:42 +09:00
parent d387b7fe11
commit d4dad933fa
6 changed files with 116 additions and 18 deletions

View File

@ -988,6 +988,22 @@ object Defaults extends BuildCommon with DefExtra {
val converter = fileConverter.value val converter = fileConverter.value
val cp = cp1.map(converter.toPath).map(converter.toVirtualFile) val cp = cp1.map(converter.toPath).map(converter.toVirtualFile)
opts.withClasspath(cp.toArray) opts.withClasspath(cp.toArray)
},
compileInputs2 := Def.uncached {
compileScalaBackend.value
val (_, _, packedBackendOutput) = compileIncremental.value
val inputs = compileInputs.value
val c = fileConverter.value
CompileInputs2(
packedBackendOutput +: data(dependencyClasspath.value).toVector,
sourcesVF.value,
scalacOptions.value.toVector,
javacOptions.value.toVector,
c.toVirtualFile(inputs.options.classesDirectory),
c.toVirtualFile(inputs.setup.cacheFile.toPath),
extraIncOptions.value.toVector,
scalaVersion.value,
)
} }
) )
) ++ ) ++
@ -2317,26 +2333,50 @@ object Defaults extends BuildCommon with DefExtra {
private val incCompiler = ZincUtil.defaultIncrementalCompiler private val incCompiler = ZincUtil.defaultIncrementalCompiler
private[sbt] def compileJavaTask: Initialize[Task[CompileResult]] = Def.task { private[sbt] def compileJavaTask: Initialize[Task[CompileResult]] = Def.task {
val s = streams.value val backendResult = compileScalaBackend.value
val projectId = thisProject.value.id val result = cachedCompileJavaTask.result.value
val r = compileScalaBackend.value
val in0 = (compileJava / compileInputs).value
val in = in0.withPreviousResult(PreviousResult.of(r.analysis, r.setup))
val reporter = (compile / bspReporter).value val reporter = (compile / bspReporter).value
val log = CompileDebugLogger(projectId, s.log) val inputs = (compileJava / compileInputs).value
try { val c = fileConverter.value
val result0 = incCompiler result match {
.asInstanceOf[sbt.internal.inc.IncrementalCompilerImpl] case Result.Value(hasModified) =>
.compileAllJava(in, log) val store = analysisStore(compileAnalysisFile.value.toPath(), c)
reporter.sendSuccessReport(result0.analysis()) val contents = store.unsafeGet()
result0.withHasModified(result0.hasModified || r.hasModified) reporter.sendSuccessReport(contents.getAnalysis())
} catch { CompileResult.of(
case NonFatal(e) => contents.getAnalysis(),
reporter.sendFailureReport(in.options.sources) contents.getMiniSetup(),
throw e hasModified || backendResult.hasModified
)
case Result.Inc(cause) =>
reporter.sendFailureReport(inputs.options.sources)
throw cause
} }
} }
private val cachedCompileJavaTask = Def
.cachedTask {
val s = streams.value
val projectId = projectIdFromScope(s)
val in0 = (compileJava / compileInputs).value
val ci2 = (compileJava / compileInputs2).value
val c = fileConverter.value
val store = analysisStore(compileAnalysisFile.value.toPath(), c)
val previous = store.unsafeGet()
val in = in0.withPreviousResult(
PreviousResult.of(previous.getAnalysis(), previous.getMiniSetup())
)
val log = CompileDebugLogger(projectId, s.log)
val result = incCompiler
.asInstanceOf[sbt.internal.inc.IncrementalCompilerImpl]
.compileAllJava(in, log)
store.set(AnalysisContents.create(result.analysis(), result.setup()))
Def.declareOutput(c.toVirtualFile(in.setup.cacheFile.toPath))
Def.declareOutputDirectory(c.toVirtualFile(in.options.classesDirectory))
result.hasModified
}
.tag(Tags.Compile, Tags.CPU)
private def compileIncrementalTaskImpl( private def compileIncrementalTaskImpl(
task: BspCompileTask, task: BspCompileTask,
s: TaskStreams, s: TaskStreams,
@ -4413,9 +4453,9 @@ object Classpaths {
def makeProducts: Initialize[Task[Seq[File]]] = Def.task { def makeProducts: Initialize[Task[Seq[File]]] = Def.task {
val c = fileConverter.value val c = fileConverter.value
val resourceDirs = resourceDirectories.value val resourceDirs = resourceDirectories.value
val _ = compile.value compile.value
val backendDir = c.toPath(backendOutput.value) val backendDir = c.toPath(backendOutput.value)
val _ = resources.value resources.value
backendDir.toFile() :: resourceDirs.toList.filter(_.exists()) backendDir.toFile() :: resourceDirs.toList.filter(_.exists())
} }

View File

@ -1,6 +1,31 @@
ThisBuild / scalaVersion := "2.13.18" ThisBuild / scalaVersion := "2.13.18"
ThisBuild / usePipelining := true ThisBuild / usePipelining := true
def countedCompilers(counter: File)(cs: xsbti.compile.Compilers): xsbti.compile.Compilers = {
val tools = cs.javaTools()
val underlying = tools.javac()
val counted = new xsbti.compile.JavaCompiler {
override def run(
sources: Array[xsbti.VirtualFile],
options: Array[String],
output: xsbti.compile.Output,
incToolOptions: xsbti.compile.IncToolOptions,
reporter: xsbti.Reporter,
log: xsbti.Logger
): Boolean = {
IO.append(counter, "javac\n")
underlying.run(sources, options, output, incToolOptions, reporter, log)
}
override def supportsDirectToJar(): Boolean =
underlying.supportsDirectToJar()
}
cs.withJavaTools(new xsbti.compile.JavaTools {
override def javac(): xsbti.compile.JavaCompiler = counted
override def javadoc(): xsbti.compile.Javadoc = tools.javadoc()
})
}
lazy val root = (project in file(".")) lazy val root = (project in file("."))
.aggregate(upstream, downstream) .aggregate(upstream, downstream)
@ -8,6 +33,7 @@ lazy val upstream = project
.settings( .settings(
exportJars := true, exportJars := true,
exportPipelining := false, exportPipelining := false,
Compile / compilers ~= countedCompilers(file("javac-invocations")),
) )
lazy val downstream = project lazy val downstream = project

View File

@ -0,0 +1,7 @@
package example;
public class Greeter {
public static String greet(String name) {
return "Updated, " + name;
}
}

View File

@ -0,0 +1,5 @@
package example
object Marker {
val tag = "updated"
}

View File

@ -2,4 +2,7 @@ package example
object Main { object Main {
def run(): String = Greeter.greet("world") def run(): String = Greeter.greet("world")
def main(args: Array[String]): Unit =
assert(run() == "Updated, world")
} }

View File

@ -2,3 +2,20 @@
$ exists target/**/upstream/classes/example/Greeter.class $ exists target/**/upstream/classes/example/Greeter.class
$ exists target/**/downstream/classes/example/Main.class $ exists target/**/downstream/classes/example/Main.class
$ copy-file javac-invocations javac-before
> compile
$ must-mirror javac-before javac-invocations
$ copy-file changes/Marker.scala upstream/src/main/scala/example/Marker.scala
> compile
$ copy-file changes/Greeter.java upstream/src/main/java/example/Greeter.java
> compile
> downstream / run
$ copy-file javac-invocations javac-after-java
$ delete target/out/jvm/scala-2.13.18/upstream/classes/example/Greeter.class
> compile
$ exists target/out/jvm/scala-2.13.18/upstream/classes/example/Greeter.class
$ must-mirror javac-after-java javac-invocations