Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dotty support #460

Merged
merged 1 commit into from
May 1, 2018
Merged
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
6 changes: 5 additions & 1 deletion backend/src/main/scala/bloop/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import xsbti.T2
import java.util.Optional
import java.io.File

import bloop.internal.Ecosystem
import bloop.io.{AbsolutePath, Paths}
import bloop.logging.Logger
import bloop.reporter.Reporter
Expand Down Expand Up @@ -75,7 +76,10 @@ object Compiler {
val reporter = compileInputs.reporter
val compilerCache = new FreshCompilerCache
val cacheFile = compileInputs.baseDirectory.resolve("cache").toFile
val incOptions = IncOptions.create()
val incOptions = {
if (!compileInputs.scalaInstance.isDotty) IncOptions.create()
else Ecosystem.supportDotty(IncOptions.create())
}
val progress = Optional.empty[CompileProgress]
Setup.create(lookup, skip, cacheFile, compilerCache, incOptions, reporter, progress, empty)
}
Expand Down
6 changes: 5 additions & 1 deletion backend/src/main/scala/bloop/ScalaInstance.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ScalaInstance(

private def isJar(filename: String): Boolean = filename.endsWith(".jar")
private def hasScalaCompilerName(filename: String): Boolean =
filename.startsWith("scala-compiler")
if (isDotty) filename.startsWith("dotty-compiler") else filename.startsWith("scala-compiler")
private def hasScalaLibraryName(filename: String): Boolean =
filename.startsWith("scala-library")

Expand All @@ -33,6 +33,10 @@ class ScalaInstance(
isJar(filename) && !hasScalaCompilerName(filename) && !hasScalaLibraryName(filename)
}

/** Is this `ScalaInstance` using Dotty? */
def isDotty: Boolean =
organization == "ch.epfl.lamp" && sbt.internal.inc.ScalaInstance.isDotty(version)

/** Tells us what the real version of the classloaded scalac compiler in this instance is. */
override def actualVersion(): String = {
// TODO: Report when the `actualVersion` and the passed in version do not match.
Expand Down
45 changes: 45 additions & 0 deletions backend/src/main/scala/bloop/internal/Ecosystem.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package bloop.internal

import java.io.File
import java.util.Optional

import xsbti.compile.{
ClassFileManager,
ClassFileManagerUtil,
DefaultExternalHooks,
IncOptions,
WrappedClassFileManager
}

object Ecosystem {
def supportDotty(incOptions: IncOptions): IncOptions = {
val tastyFileManager = new ClassFileManager {
private[this] val inherited = ClassFileManagerUtil.getDefaultClassFileManager(incOptions)

def delete(classes: Array[File]): Unit = {
val tastySuffixes = List(".tasty", ".hasTasty")
inherited.delete(classes flatMap { classFile =>
if (classFile.getPath endsWith ".class") {
val prefix = classFile.getAbsolutePath.stripSuffix(".class")
tastySuffixes.map(suffix => new File(prefix + suffix)).filter(_.exists)
} else Nil
})
}

def generated(classes: Array[File]): Unit = {}
def complete(success: Boolean): Unit = {}
}
val inheritedHooks = incOptions.externalHooks
val externalClassFileManager: Optional[ClassFileManager] = Option(
inheritedHooks.getExternalClassFileManager.orElse(null)) match {
case Some(prevManager) =>
Optional.of(WrappedClassFileManager.of(prevManager, Optional.of(tastyFileManager)))
case None =>
Optional.of(tastyFileManager)
}

val newExternalHooks =
new DefaultExternalHooks(inheritedHooks.getExternalLookup, externalClassFileManager)
incOptions.withExternalHooks(newExternalHooks)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ object ZincInternals {
def compilerBridgeId(scalaVersion: String) = {
// Defaults to bridge for 2.13 for Scala versions bigger than 2.13.x
scalaVersion match {
case sc if (sc startsWith "0.") => "dotty-sbt-bridge"
case sc if (sc startsWith "2.10.") => "compiler-bridge_2.10"
case sc if (sc startsWith "2.11.") => "compiler-bridge_2.11"
case sc if (sc startsWith "2.12.") => "compiler-bridge_2.12"
case _ => "compiler-bridge_2.13"
case _ => "compiler-bridge_2.13"
}
}

val organization = if (scalaInstance.isDotty) scalaInstance.organization else "ch.epfl.scala"
val bridgeId = compilerBridgeId(scalaInstance.version)
ModuleID("ch.epfl.scala", bridgeId, latestVersion).withConfigurations(CompileConf).sources()
val version = if (scalaInstance.isDotty) scalaInstance.version else latestVersion

ModuleID(organization, bridgeId, version).withConfigurations(CompileConf).sources()
}

/**
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CompilationTaskTest {
val `B2.scala` = "package p1\ntrait B"
val `C.scala` = "package p2\nimport p0.A\nimport p1.B\nobject C extends A with B"
val `C2.scala` = "package p2\nimport p0.A\nobject C extends A"
val `Dotty.scala` = "package p0\nobject Foo { val x: String | Int = 1 }"
}

@Test
Expand Down Expand Up @@ -232,4 +233,16 @@ class CompilationTaskTest {
assert(msgs.exists(m => m._1 == "warn" && m._2.contains(targetMsg)))
}
}

@Test
def compileWithDotty080RC1: Unit = {
val logger = new RecordingLogger()
val scalaInstance =
ScalaInstance.resolve("ch.epfl.lamp", "dotty-compiler_0.8", "0.8.0-RC1", logger)
val structures = Map(RootProject -> Map("Dotty.scala" -> ArtificialSources.`Dotty.scala`))
checkAfterCleanCompilation(structures, Map.empty, scalaInstance = scalaInstance) { state =>
val projects = state.build.projects
assert(projects.forall(p => hasPreviousResult(p, state)))
}
}
}