Make sbt aware of Dotty

This small set of changes, together with the compiler-bridge I wrote
(https://github.com/smarter/dotty-bridge) enables us to compile code
using Dotty in sbt, see https://github.com/smarter/dotty-example-project
for an example.
This commit is contained in:
Guillaume Martres 2015-12-30 22:38:55 +01:00
parent bcb98ab6bb
commit 10265efd9c
6 changed files with 67 additions and 14 deletions

View File

@ -30,7 +30,11 @@ final class CompilerArguments(scalaInstance: xsbti.compile.ScalaInstance, cp: xs
}
def finishClasspath(classpath: Seq[File]): Seq[File] =
filterLibrary(classpath) ++ include(cp.compiler, scalaInstance.compilerJar) ++ include(cp.extra, scalaInstance.otherJars: _*)
private[this] def include(flag: Boolean, jars: File*) = if (flag) jars else Nil
private[this] def include(flag: Boolean, jars: File*) =
if (flag || ScalaInstance.isDotty(scalaInstance.version))
jars
else
Nil
private[this] def abs(files: Seq[File]) = files.map(_.getAbsolutePath).sortWith(_ < _)
private[this] def checkScalaHomeUnset(): Unit = {
val scalaHome = System.getProperty("scala.home")

View File

@ -21,16 +21,29 @@ class RawCompiler(val scalaInstance: xsbti.compile.ScalaInstance, cp: ClasspathO
val arguments = compilerArguments(sources, classpath, Some(outputDirectory), options)
log.debug("Plain interface to Scala compiler " + scalaInstance.actualVersion + " with arguments: " + arguments.mkString("\n\t", "\n\t", ""))
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader)
val process = mainClass.getMethod("process", classOf[Array[String]])
process.invoke(null, arguments.toArray)
checkForFailure(mainClass, arguments.toArray)
val args = arguments.toArray
val reporter =
if (ScalaInstance.isDotty(scalaInstance.version)) {
val mainClass = Class.forName("dotty.tools.dotc.Main", true, scalaInstance.loader)
val process = mainClass.getMethod("process", classOf[Array[String]])
process.invoke(null, args)
} else {
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaInstance.loader)
val process = mainClass.getMethod("process", classOf[Array[String]])
process.invoke(null, arguments.toArray)
mainClass.getMethod("reporter").invoke(null)
}
checkForFailure(reporter, arguments.toArray)
}
def compilerArguments = new CompilerArguments(scalaInstance, cp)
protected def checkForFailure(mainClass: Class[_], args: Array[String]): Unit = {
val reporter = mainClass.getMethod("reporter").invoke(null)
protected def checkForFailure(reporter: AnyRef, args: Array[String]): Unit = {
val failed = reporter.getClass.getMethod("hasErrors").invoke(reporter).asInstanceOf[Boolean]
if (failed) throw new CompileFailed(args, "Plain compile failed", Array())
}
@deprecated("Use `checkForFailure(AnyRef, Array[String])`", "0.13.10")
protected def checkForFailure(mainClass: Class[_], args: Array[String]): Unit = {
val reporter = mainClass.getMethod("reporter").invoke(null)
checkForFailure(reporter, args)
}
}
class CompileFailed(val arguments: Array[String], override val toString: String, val problems: Array[xsbti.Problem]) extends xsbti.CompileFailed with FeedbackProvidedException

View File

@ -17,12 +17,20 @@ object ScalaArtifacts {
val LibraryID = ScalaLibraryID
val CompilerID = ScalaCompilerID
val ReflectID = "scala-reflect"
val DottyIDPrefix = "dotty"
def dottyID(binaryVersion: String): String = s"${DottyIDPrefix}_${binaryVersion}"
def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version)
private[sbt] def toolDependencies(org: String, version: String): Seq[ModuleID] = Seq(
scalaToolDependency(org, ScalaArtifacts.CompilerID, version),
scalaToolDependency(org, ScalaArtifacts.LibraryID, version)
)
private[sbt] def toolDependencies(org: String, version: String, isDotty: Boolean = false): Seq[ModuleID] =
if (isDotty)
Seq(ModuleID(org, DottyIDPrefix, version, Some(Configurations.ScalaTool.name + "->compile"),
crossVersion = CrossVersion.binary))
else
Seq(scalaToolDependency(org, ScalaArtifacts.CompilerID, version),
scalaToolDependency(org, ScalaArtifacts.LibraryID, version))
private[this] def scalaToolDependency(org: String, id: String, version: String): ModuleID =
ModuleID(org, id, version, Some(Configurations.ScalaTool.name + "->default,optional(default)"))
}

View File

@ -390,7 +390,11 @@ object Defaults extends BuildCommon {
def file(id: String) = files(id).headOption getOrElse sys.error(s"Missing ${id}.jar")
val allFiles = toolReport.modules.flatMap(_.artifacts.map(_._2))
val libraryJar = file(ScalaArtifacts.LibraryID)
val compilerJar = file(ScalaArtifacts.CompilerID)
val compilerJar =
if (ScalaInstance.isDotty(scalaVersion.value))
file(ScalaArtifacts.dottyID(scalaBinaryVersion.value))
else
file(ScalaArtifacts.CompilerID)
val otherJars = allFiles.filterNot(x => x == libraryJar || x == compilerJar)
ScalaInstance(scalaVersion.value, libraryJar, compilerJar, otherJars: _*)(makeClassLoader(state.value))
}
@ -1250,8 +1254,11 @@ object Classpaths {
val pluginAdjust = if (sbtPlugin.value) sbtDependency.value.copy(configurations = Some(Provided.name)) +: base else base
if (scalaHome.value.isDefined || ivyScala.value.isEmpty || !managedScalaInstance.value)
pluginAdjust
else
ScalaArtifacts.toolDependencies(scalaOrganization.value, scalaVersion.value) ++ pluginAdjust
else {
val version = scalaVersion.value
val isDotty = ScalaInstance.isDotty(version)
ScalaArtifacts.toolDependencies(scalaOrganization.value, version, isDotty) ++ pluginAdjust
}
}
)
@deprecated("Split into ivyBaseSettings and jvmBaseSettings.", "0.13.2")

View File

@ -0,0 +1,16 @@
[Dotty]: https://github.com/lampepfl/dotty
[@smarter]: https://github.com/smarter
### Fixes with compatibility implications
### Improvements
- sbt is now aware of [Dotty][Dotty], it will assume
that Dotty is used when `scalaVersion` starts with `0.`, the sbt
compiler-bridge does not support Dotty but a separate compiler-bridge is being
developed at https://github.com/smarter/dotty-bridge and an example project
that uses it is available at https://github.com/smarter/dotty-example-project
by [@smarter][@smarter].
### Bug fixes

View File

@ -41,6 +41,11 @@ object ScalaInstance {
val ScalaOrg = ScalaOrganization
val VersionPrefix = "version "
def isDotty(version: String): Boolean =
// We rely on the fact that the first public version of Scala was 1.0 and
// that Dotty will keep being version 0.x for a long time.
version.startsWith("0.")
def apply(org: String, version: String, launcher: xsbti.Launcher): ScalaInstance =
// Due to incompatibility with previous launchers if scalaOrg has default value revert to an existing method
if (org == ScalaOrg)