perf: Use sync instead of copying the directory

**Problem**
We currently need to split the output of the compiler from
the classes directory so we can cache just the output
without mixing it with the resource files
while maintaining similar classes directory as sbt 1.x
(as opposed to just splitting them completely).

However, copying directory is a performance bottleneck.

**Solution**
This uses Sync.sync function to synchronize the backend
directory to classes directory.
This is more reliable than using Analysis information,
since the compilers (and/or plugins) can generate non-class files
on the side in META-INF, or tasty file in case of Scala 3.
This commit is contained in:
Eugene Yokota 2025-11-13 04:07:54 -05:00
parent 2459663387
commit b7e9c02240
1 changed files with 15 additions and 12 deletions

View File

@ -4064,20 +4064,23 @@ object Classpaths {
def makeProducts: Initialize[Task[Seq[File]]] = Def.task {
val c = fileConverter.value
val resources = copyResources.value.map(_._2).toSet
val classDir = classDirectory.value
val syncDir = target.value / (prefix(configuration.value.name) + "sync")
val factory = CacheStoreFactory(syncDir)
val cacheStore = factory.make("make-product")
val t = classDirectory.value
val vfBackendDir = compileIncremental.value._2
val backendDir = c.toPath(vfBackendDir)
// delete outdated files
Path
.allSubpaths(classDir)
.collect { case (f, _) if f.isFile() && !resources.contains(f) => f }
.foreach(IO.delete)
IO.copyDirectory(
source = backendDir.toFile(),
target = classDir,
)
classDir :: Nil
val flt: File => Option[File] = flat(t)
val transform: File => Option[File] =
(f: File) => rebase(backendDir.toFile(), t)(f).orElse(flt(f))
val resources = copyResources.value.map(_._2).toSet
val view = fileTreeView.value
val classes = view.list((Glob(backendDir, RecursiveGlob / "*")))
val mappings: Seq[(File, File)] = classes.flatMap:
case (r, attr) if r != backendDir => transform(r.toFile()).map(r.toFile() -> _)
case _ => None
Sync.sync(cacheStore, fileConverter = c)(mappings)
t :: Nil
}
private[sbt] def makePickleProducts: Initialize[Task[Seq[VirtualFile]]] = Def.task {