Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package higherkindness.rules_scala.workers.common

import java.io.File
import java.net.URLClassLoader
import sbt.internal.inc.classpath.AbstractClassLoaderCache
import scala.collection.concurrent

/**
* [[AnnexClassLoaderCacheImpl]] is mostly identical to [[sbt.internal.inc.classpath.ClassLoaderCacheImpl]], with a few
* exceptions:
* - It doesn't check the modification time of the compiler classpath files. This is because within the context of
* [[higherkindness.rules_scala.workers.zinc.compile.ZincRunner]], file paths being equal implies the contents of
* those files are equal and checking the modification time is unnecessary overhead.
* - It doesn't use [[java.lang.ref.SoftReference]]s, to minimize the reloading of the compiler
* - It doesn't do any locking
*/
class AnnexClassLoaderCacheImpl(override val commonParent: ClassLoader) extends AbstractClassLoaderCache {
private val delegate = new concurrent.TrieMap[List[File], ClassLoader]()

override def apply(files: List[File]): ClassLoader =
cachedCustomClassloader(files, () => new URLClassLoader(files.map(_.toURI.toURL).toArray, commonParent))

override def cachedCustomClassloader(files: List[File], makeClassLoader: () => ClassLoader): ClassLoader =
delegate.getOrElseUpdate(files, makeClassLoader())

override def close(): Unit = {
delegate.values.foreach {
case classLoader: AutoCloseable => classLoader.close()
case _ =>
}

delegate.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import sbt.internal.inc.classpath.ClassLoaderCache
import sbt.internal.inc.javac.DiagnosticsReporter
import sbt.internal.inc.{CompileOutput, PlainVirtualFile, PlainVirtualFileConverter, ZincUtil}
import sbt.internal.util.LoggerWriter
import scala.collection.View
import scala.jdk.CollectionConverters.*
import scala.util.control.NonFatal
import xsbti.compile.{DependencyChanges, ScalaInstance}
Expand Down Expand Up @@ -57,10 +58,8 @@ object ZincRunner extends WorkerMain[Unit] {
// dynamic execution. The concurrency error happens very rarely, so it's hard to reproduce.
override protected val mayInterruptWorkerTasks = false

private val classloaderCache = new ClassLoaderCache(new URLClassLoader(Array()))
private val classloaderCache = new ClassLoaderCache(new AnnexClassLoaderCacheImpl(new URLClassLoader(Array.empty)))

// prevents GC of the soft reference in classloaderCache
private var lastCompiler: AnyRef = null
private def compileScala(
task: WorkTask[Unit],
parsedArguments: CommonArguments,
Expand All @@ -83,24 +82,25 @@ object ZincRunner extends WorkerMain[Unit] {
val shouldIncludeSourceRoot = !scalaInstance.actualVersion.startsWith("0.") &&
scalaInstance.actualVersion.startsWith("3")

val scalacOptions =
parsedArguments.plugins.view.map(p => s"-Xplugin:$p").toArray ++
parsedArguments.compilerOptions ++
parsedArguments.compilerOptionsReferencingPaths.toArray ++
val scalacOptions = (
// We don't use this phase, so we disable it to speed up compilation by a teeny tiny amount
View("-Yskip:xsbt-analyzer") ++
parsedArguments.plugins.view.map(p => s"-Xplugin:$p") ++
parsedArguments.compilerOptions.view ++
parsedArguments.compilerOptionsReferencingPaths.view ++
(
if (shouldIncludeSourceRoot) {
Array("-sourceroot", task.workDir.toAbsolutePath.toString)
View("-sourceroot", task.workDir.toAbsolutePath.toString)
} else {
Array.empty[String]
View.empty
}
)
).toArray

val scalaCompiler = ZincUtil
.scalaCompiler(scalaInstance, parsedArguments.compilerBridge)
.withClassLoaderCache(classloaderCache)

lastCompiler = scalaCompiler

InterruptUtil.throwIfInterrupted(task.isCancelled)

scalaCompiler.compile(
Expand Down
Loading