Skip to content

Commit

Permalink
Update Zinc to 1.6.0
Browse files Browse the repository at this point in the history
Biggest changes required here involved the new VirtualFile interface as we needed to convert them to java.io.File in a number of places. I tried to keep the conversions to a minimum, so that we don't bump into unexpected things such as issues with `jrt` filesystem and tried using Path wherever possible. I am not 100% if everything is correct, but I double checked and everything seems in place.

We also needed to adjust ScalaInstance to make it work with the new interface and add an additional setting for output in the Java compilers, since otherwise everything gets produced to source directories (this mirrors the change in zinc itself.)
  • Loading branch information
tgodzik committed Jan 24, 2022
1 parent 7194388 commit dfd8e35
Show file tree
Hide file tree
Showing 30 changed files with 612 additions and 277 deletions.
33 changes: 21 additions & 12 deletions backend/src/main/scala/bloop/BloopClasspathEntryLookup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,38 @@ import java.util.concurrent.ConcurrentHashMap
import xsbti.compile.FileHash
import sbt.internal.inc.bloop.internal.BloopNameHashing
import sbt.internal.inc.bloop.internal.BloopStamps
import sbt.internal.inc.PlainVirtualFile
import xsbti.VirtualFile
import bloop.util.AnalysisUtils
import sbt.internal.inc.classpath.ClasspathUtil
import xsbti.FileConverter

final class BloopClasspathEntryLookup(
results: Map[File, PreviousResult],
classpathHashes: Vector[FileHash]
classpathHashes: Vector[FileHash],
converter: FileConverter
) extends PerClasspathEntryLookup {
override def analysis(classpathEntry: File): ju.Optional[CompileAnalysis] = {
InterfaceUtil.toOptional(results.get(classpathEntry)).flatMap(_.analysis())
override def analysis(classpathEntry: VirtualFile): ju.Optional[CompileAnalysis] = {
val file = converter.toPath(classpathEntry).toFile()
InterfaceUtil.toOptional(results.get(file)).flatMap(_.analysis())
}

override def definesClass(entry: File): DefinesClass = {
if (!entry.exists) FalseDefinesClass
override def definesClass(entry: VirtualFile): DefinesClass = {
val file = converter.toPath(entry).toFile()
if (!file.exists) FalseDefinesClass
else {
classpathHashes.find(fh => fh.file() == entry) match {
classpathHashes.find(fh => fh.file() == file) match {
case None => FalseDefinesClass
case Some(entryHash) =>
def computeDefinesClassForJar = {
if (!ClasspathUtilities.isArchive(entry, contentFallback = true)) FalseDefinesClass
else new JarDefinesClass(entry)
if (!ClasspathUtil.isArchive(file.toPath(), contentFallback = true)) FalseDefinesClass
else new JarDefinesClass(file)
}

if (BloopStamps.isDirectoryHash(entryHash)) new DirectoryDefinesClass(entry)
if (BloopStamps.isDirectoryHash(entryHash)) new DirectoryDefinesClass(file)
else {
val (_, cachedDefinesClass) = BloopClasspathEntryLookup.definedClasses.compute(
entry,
file,
(entry, definesClass) => {
definesClass match {
case null =>
Expand Down Expand Up @@ -119,11 +127,12 @@ object BloopClasspathEntryLookup {
): Option[File] = {
def findClassFile(t: (File, PreviousResult)): Option[File] = {
val (classesDir, result) = t
val targetClassFile = new File(classesDir, relativeClassFile)
val targetFile = new File(classesDir.toPath().toFile(), relativeClassFile)
val targetClassFile = PlainVirtualFile(targetFile.toPath())
InterfaceUtil.toOption(result.analysis()).flatMap { analysis0 =>
val analysis = analysis0.asInstanceOf[sbt.internal.inc.Analysis]
val definedClass = analysis.relations.allProducts.contains(targetClassFile)
if (definedClass) Some(targetClassFile) else None
if (definedClass) Some(targetFile) else None
}
}

Expand Down
45 changes: 32 additions & 13 deletions backend/src/main/scala/bloop/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import sbt.internal.inc.bloop.internal.BloopStamps
import sbt.internal.inc.bloop.internal.BloopLookup
import bloop.reporter.Reporter
import bloop.logging.CompilationEvent
import sbt.internal.inc.PlainVirtualFile
import xsbti.VirtualFile
import xsbti.VirtualFileRef
import sbt.internal.inc.PlainVirtualFileConverter

case class CompileInputs(
scalaInstance: ScalaInstance,
Expand Down Expand Up @@ -160,6 +164,7 @@ object CompileOutPaths {

object Compiler {
private implicit val filter = bloop.logging.DebugFilter.Compilation
private val converter = PlainVirtualFileConverter.converter
private final class BloopProgress(
reporter: ZincReporter,
cancelPromise: Promise[Unit]
Expand All @@ -168,7 +173,12 @@ object Compiler {
reporter.reportNextPhase(phase, new java.io.File(unitPath))
}

override def advance(current: Int, total: Int): Boolean = {
override def advance(
current: Int,
total: Int,
prevPhase: String,
nextPhase: String
): Boolean = {
val isNotCancelled = !cancelPromise.isCompleted
if (isNotCancelled) {
reporter.reportCompilationProgress(current.toLong, total.toLong)
Expand Down Expand Up @@ -276,8 +286,10 @@ object Compiler {
}

def getCompilationOptions(inputs: CompileInputs): CompileOptions = {
val sources = inputs.sources // Sources are all files
val classpath = inputs.classpath.map(_.toFile)
// Sources are all files
val sources = inputs.sources.map(path => PlainVirtualFile(path.underlying): VirtualFile)

val classpath = inputs.classpath.map(path => PlainVirtualFile(path.underlying): VirtualFile)
val optionsWithoutFatalWarnings = inputs.scalacOptions.flatMap { option =>
if (option != "-Xfatal-warnings") List(option)
else {
Expand All @@ -292,8 +304,8 @@ object Compiler {

CompileOptions
.create()
.withClassesDirectory(newClassesDir.toFile)
.withSources(sources.map(_.toFile))
.withClassesDirectory(newClassesDir)
.withSources(sources)
.withClasspath(classpath)
.withScalacOptions(optionsWithoutFatalWarnings)
.withJavacOptions(inputs.javacOptions)
Expand All @@ -308,7 +320,11 @@ object Compiler {
newClassesDir.toFile -> compileInputs.previousResult
)

val lookup = new BloopClasspathEntryLookup(results, compileInputs.uniqueInputs.classpath)
val lookup = new BloopClasspathEntryLookup(
results,
compileInputs.uniqueInputs.classpath,
converter
)
val reporter = compileInputs.reporter
val compilerCache = new FreshCompilerCache
val cacheFile = compileInputs.baseDirectory.resolve("cache").toFile
Expand Down Expand Up @@ -516,7 +532,7 @@ object Compiler {

val resultForFutureCompilationRuns = {
resultForDependentCompilationsInSameRun.withAnalysis(
Optional.of(analysisForFutureCompilationRuns)
Optional.of(analysisForFutureCompilationRuns): Optional[CompileAnalysis]
)
}

Expand Down Expand Up @@ -718,12 +734,14 @@ object Compiler {
): Analysis = {
// Cast to the only internal analysis that we support
val analysis = analysis0.asInstanceOf[Analysis]
def rebase(file: File): File = {
val filePath = file.toPath.toAbsolutePath
def rebase(file: VirtualFileRef): VirtualFileRef = {

val filePath = PlainVirtualFileConverter.converter.toPath(file).toAbsolutePath()
if (!filePath.startsWith(readOnlyClassesDir)) file
else {
// Hash for class file is the same because the copy duplicates metadata
newClassesDir.resolve(readOnlyClassesDir.relativize(filePath)).toFile
val path = newClassesDir.resolve(readOnlyClassesDir.relativize(filePath))
PlainVirtualFile(path)
}
}

Expand All @@ -732,19 +750,20 @@ object Compiler {
val oldStamps = analysis.stamps
// Use empty stamps for files that have fatal warnings so that next compile recompiles them
val rebasedSources = oldStamps.sources.map {
case t @ (file, _) =>
case t @ (virtualFile, _) =>
val file = converter.toPath(virtualFile).toFile()
// Assumes file in reported diagnostic matches path in here
val fileHasFatalWarnings = sourceFilesWithFatalWarnings.contains(file)
if (!fileHasFatalWarnings) t
else file -> BloopStamps.emptyStampFor(file)
else virtualFile -> BloopStamps.emptyStampFor(file)
}
val rebasedProducts = oldStamps.products.map {
case t @ (file, _) =>
val rebased = rebase(file)
if (rebased == file) t else rebased -> t._2
}
// Changes the paths associated with the class file paths
Stamps(rebasedProducts, rebasedSources, oldStamps.binaries)
Stamps(rebasedProducts, rebasedSources, oldStamps.libraries)
}

val newRelations = {
Expand Down
35 changes: 29 additions & 6 deletions backend/src/main/scala/bloop/CompilerCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ import javax.tools.JavaFileObject.Kind
import javax.tools.{JavaCompiler => JavaxCompiler}
import scala.collection.mutable.HashSet
import scala.concurrent.ExecutionContext
import xsbti.VirtualFile
import bloop.util.AnalysisUtils
import xsbti.compile.{IncToolOptions, Output}
import sbt.internal.inc.CompilerArguments
import sbt.internal.inc.PlainVirtualFileConverter

final class CompilerCache(
componentProvider: ComponentProvider,
Expand Down Expand Up @@ -145,9 +150,12 @@ final class CompilerCache(
final class BloopForkedJavaCompiler(javaHome: Option[File]) extends JavaCompiler {
import xsbti.compile.IncToolOptions

private val converter = PlainVirtualFileConverter.converter

def run(
sources: Array[File],
sources: Array[VirtualFile],
options: Array[String],
output: Output,
topts: IncToolOptions,
reporter: XReporter,
log: XLogger
Expand Down Expand Up @@ -195,7 +203,14 @@ final class CompilerCache(

try {
import sbt.internal.inc.javac.BloopForkedJavaUtils
BloopForkedJavaUtils.launch(javaHome, "javac", sources, options, log, reporter)
BloopForkedJavaUtils.launch(
javaHome,
"javac",
sources.map(converter.toPath(_).toFile()),
options,
log,
reporter
)
} finally {
Paths.delete(newInvalidatedEntry)
}
Expand All @@ -215,9 +230,11 @@ final class CompilerCache(
import java.io.File
import xsbti.compile.IncToolOptions
import xsbti.Reporter
private val converter = PlainVirtualFileConverter.converter
override def run(
sources: Array[File],
sources: Array[VirtualFile],
options: Array[String],
output: Output,
incToolOptions: IncToolOptions,
reporter: Reporter,
log0: xsbti.Logger
Expand All @@ -241,8 +258,8 @@ final class CompilerCache(
import sbt.internal.inc.javac.WriteReportingFileManager
val zincFileManager = incToolOptions.classFileManager().get()
val fileManager = new BloopInvalidatingFileManager(fileManager0, zincFileManager)

val jfiles = fileManager0.getJavaFileObjectsFromFiles(sources.toList.asJava)
val sourceFiles: Array[File] = sources.map(converter.toPath(_).toFile())
val jfiles = fileManager0.getJavaFileObjectsFromFiles(sourceFiles.toList.asJava)
try {
// Create directories of java args that trigger error if they don't exist
def processJavaDirArgument(idx: Int): Unit = {
Expand All @@ -264,7 +281,13 @@ final class CompilerCache(
processJavaDirArgument(cleanedOptions.indexOf("-s"))
processJavaDirArgument(cleanedOptions.indexOf("-h"))

val newJavacOptions = cleanedOptions.toList.asJava
output.getSingleOutputAsPath match {
case p if p.isPresent => java.nio.file.Files.createDirectories(p.get)
case _ =>
}

val outputOption = CompilerArguments.outputOption(output)
val newJavacOptions = (cleanedOptions.toList ++ outputOption).asJava
log.debug(s"Invoking javac with ${newJavacOptions.asScala.mkString(" ")}")
val success = compiler
.getTask(logWriter, fileManager, diagnostics, newJavacOptions, null, jfiles)
Expand Down
30 changes: 29 additions & 1 deletion backend/src/main/scala/bloop/ScalaInstance.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ final class ScalaInstance private (
override val allJars: Array[File]
) extends xsbti.compile.ScalaInstance {

override lazy val loaderCompilerOnly: ClassLoader =
new URLClassLoader(compilerJars().map(_.toURI.toURL), ScalaInstance.topClassLoader)

override def compilerJars(): Array[File] = {
val all = allJars
.filter(f => isJar(f.getName) && isCompilerJar(f))
if (all.isEmpty) sys.error(s"Missing compiler jars in Scala jars ${allJars.mkString(", ")}")
all
}

override def libraryJars(): Array[File] = {
allJars
.filter(f => isJar(f.getName) && hasScalaLibraryName(f.getName))
Expand Down Expand Up @@ -58,7 +68,25 @@ final class ScalaInstance private (
filename.startsWith(ScalacCompilerName) ||
(isDotty && (filename.startsWith("dotty-compiler") || filename.startsWith("scala3-compiler")))
private def hasScalaLibraryName(filename: String): Boolean =
filename.startsWith("scala-library")
filename.startsWith("scala-library") || filename.startsWith("scala3-library")

private def hasScalaReflectName(filename: String): Boolean =
filename.startsWith("scala-reflect")

private def hasScalaXmlName(filename: String): Boolean =
filename.startsWith("scala-xml")

private def hasScala3AdditionalLibraryName(filename: String): Boolean =
isDotty &&
(filename.startsWith("scala3-interfaces") || filename.startsWith("tasty-core") ||
filename.startsWith("scala-asm"))

private def isCompilerJar(file: File) = {
val name = file.getName()
hasScalaReflectName(name) || hasScalaCompilerName(name) ||
hasScalaLibraryName(name) || hasScalaXmlName(name) ||
hasScala3AdditionalLibraryName(name)
}

/** Tells us what the real version of the classloaded scalac compiler in this instance is. */
override def actualVersion(): String = {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/main/scala/bloop/UniqueCompileInputs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bloop
import bloop.io.AbsolutePath
import bloop.util.CacheHashCode
import xsbti.compile.FileHash
import xsbti.VirtualFileRef

case class UniqueCompileInputs(
sources: Vector[UniqueCompileInputs.HashedSource],
Expand Down Expand Up @@ -36,7 +37,7 @@ case class UniqueCompileInputs(
}

object UniqueCompileInputs {
case class HashedSource(source: AbsolutePath, hash: Int)
case class HashedSource(source: VirtualFileRef, hash: Int)

def emptyFor(originPath: String): UniqueCompileInputs = {
UniqueCompileInputs(Vector.empty, Vector.empty, Vector.empty, Vector.empty, originPath)
Expand Down
Loading

0 comments on commit dfd8e35

Please sign in to comment.