Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

255 lines (235 sloc) 10.582 kb
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
package xsbt
import xsbti.{AnalysisCallback,Logger,Problem,Reporter,Severity}
import xsbti.compile._
import{backend, io, reporters, symtab, util, Phase, Global, Settings, SubComponent}
import backend.JavaPlatform
import symtab.SymbolLoaders
import util.{ClassPath,DirectoryClassPath,MergedClassPath,JavaClassPath}
import ClassPath.{ClassPathContext,JavaContext}
import io.AbstractFile
import scala.annotation.tailrec
import scala.collection.mutable
import Log.debug
final class CompilerInterface
def newCompiler(options: Array[String], output: Output, initialLog: Logger, initialDelegate: Reporter, resident: Boolean): CachedCompiler =
new CachedCompiler0(options, output, new WeakLog(initialLog, initialDelegate), resident)
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress, cached: CachedCompiler): Unit =, changes, callback, log, delegate, progress)
// for compatibility with Scala versions without Global.registerTopLevelSym (2.8.1 and earlier)
sealed trait GlobalCompat { self: Global =>
def registerTopLevelSym(sym: Symbol): Unit
sealed trait RunCompat {
def informUnitStarting(phase: Phase, unit: CompilationUnit) {}
sealed abstract class CallbackGlobal(settings: Settings, reporter: reporters.Reporter, output: Output) extends Global(settings, reporter) with GlobalCompat {
def callback: AnalysisCallback
def findClass(name: String): Option[(AbstractFile,Boolean)]
lazy val outputDirs: Iterable[File] = {
output match {
case single: SingleOutput => List(single.outputDirectory)
case multi: MultipleOutput => multi.outputGroups.toStream map (_.outputDirectory)
// Map source files to public inherited dependencies. These dependencies are tracked as the symbol for the dealiased base class.
val inheritedDependencies = new mutable.HashMap[File, mutable.Set[Symbol]]
def addInheritedDependencies(file: File, deps: Iterable[Symbol]) {
inheritedDependencies.getOrElseUpdate(file, new mutable.HashSet) ++= deps
class InterfaceCompileFailed(val arguments: Array[String], val problems: Array[Problem], override val toString: String) extends xsbti.CompileFailed
class InterfaceCompileCancelled(val arguments: Array[String], override val toString: String) extends xsbti.CompileCancelled
private final class WeakLog(private[this] var log: Logger, private[this] var delegate: Reporter)
def apply(message: String) {
assert(log ne null, "Stale reference to logger")
def logger: Logger = log
def reporter: Reporter = delegate
def clear() {
log = null
delegate = null
private final class CachedCompiler0(args: Array[String], output: Output, initialLog: WeakLog, resident: Boolean) extends CachedCompiler
val settings = new Settings(s => initialLog(s))
output match {
case multi: MultipleOutput =>
for (out <- multi.outputGroups)
settings.outputDirs.add(out.sourceDirectory.getAbsolutePath, out.outputDirectory.getAbsolutePath)
case single: SingleOutput =>
val command = Command(args.toList, settings)
private[this] val dreporter = DelegatingReporter(settings, initialLog.reporter)
try {
if(!noErrors(dreporter)) {
handleErrors(dreporter, initialLog.logger)
} finally
def noErrors(dreporter: DelegatingReporter) = !dreporter.hasErrors && command.ok
def commandArguments(sources: Array[File]): Array[String] =
(command.settings.recreateArgs ++[String]
def run(sources: Array[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, progress: CompileProgress): Unit = synchronized
debug(log, "Running cached compiler " + hashCode.toHexString + ", interfacing (CompilerInterface) with Scala compiler " +
val dreporter = DelegatingReporter(settings, delegate)
try { run(sources.toList, changes, callback, log, dreporter, progress) }
finally { dreporter.dropDelegate() }
private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, dreporter: DelegatingReporter, compileProgress: CompileProgress)
{, command.getInfoMessage(compiler), true)
throw new InterfaceCompileFailed(args, Array(), "Compiler option supplied that disabled actual compilation.")
debug(log, args.mkString("Calling Scala compiler with arguments (CompilerInterface):\n\t", "\n\t", ""))
compiler.set(callback, dreporter)
val run = new compiler.Run with compiler.RunCompat {
override def informUnitStarting(phase: Phase, unit: compiler.CompilationUnit) {
compileProgress.startUnit(, unit.source.path)
override def progress(current: Int, total: Int) {
if (!compileProgress.advance(current, total))
val sortedSourceFiles = < _)
run compile sortedSourceFiles
dreporter.problems foreach { p => callback.problem(p.category, p.position, p.message, p.severity, true) }
if(!noErrors(dreporter)) handleErrors(dreporter, log)
// the case where we cancelled compilation _after_ some compilation errors got reported
// will be handled by line above so errors still will be reported properly just potentially not
// all of them (because we cancelled the compilation)
if (dreporter.cancelled) handleCompilationCancellation(dreporter, log)
def handleErrors(dreporter: DelegatingReporter, log: Logger): Nothing =
debug(log, "Compilation failed (CompilerInterface)")
throw new InterfaceCompileFailed(args, dreporter.problems, "Compilation failed")
def handleCompilationCancellation(dreporter: DelegatingReporter, log: Logger): Nothing = {
assert(dreporter.cancelled, "We should get here only if when compilation got cancelled")
debug(log, "Compilation cancelled (CompilerInterface)")
throw new InterfaceCompileCancelled(args, "Compilation has been cancelled")
def processUnreportedWarnings(run: compiler.Run)
// allConditionalWarnings and the ConditionalWarning class are only in 2.10+
final class CondWarnCompat(val what: String, val warnings: mutable.ListBuffer[(compiler.Position, String)])
implicit def compat(run: AnyRef): Compat = new Compat
final class Compat { def allConditionalWarnings = List[CondWarnCompat]() }
val warnings = run.allConditionalWarnings
compiler.logUnreportedWarnings( => ("" /*cw.what*/, cw.warnings.toList)))
val compiler: Compiler = {
if (command.settings.Yrangepos.value)
new Compiler() with RangePositions // unnecessary in 2.11
new Compiler()
class Compiler extends CallbackGlobal(command.settings, dreporter, output)
object dummy // temporary fix for #4426
object sbtAnalyzer extends
val global: Compiler.this.type = Compiler.this
val phaseName =
val runsAfter = List("jvm")
override val runsBefore = List("terminal")
val runsRightAfter = None
with SubComponent
val analyzer = new Analyzer(global)
def newPhase(prev: Phase) = analyzer.newPhase(prev)
def name = phaseName
/** This phase walks trees and constructs a representation of the public API, which is used for incremental recompilation.
* We extract the api after picklers, since that way we see the same symbol information/structure
* irrespective of whether we were typechecking from source / unpickling previously compiled classes.
object apiExtractor extends
val global: Compiler.this.type = Compiler.this
val phaseName =
val runsAfter = List("typer")
override val runsBefore = List("erasure")
// allow apiExtractor's phase to be overridden using the sbt.api.phase property
// (in case someone would like the old timing, which was right after typer)
// TODO: consider migrating to simply specifying "pickler" for `runsAfter` and "uncurry" for `runsBefore`
val runsRightAfter = Option(System.getProperty("sbt.api.phase")) orElse Some("pickler")
with SubComponent
val api = new API(global)
def newPhase(prev: Phase) = api.newPhase(prev)
def name = phaseName
override lazy val phaseDescriptors =
phasesSet += sbtAnalyzer
phasesSet += apiExtractor
// Required because computePhaseDescriptors is private in 2.8 (changed to protected sometime later).
private[this] def superComputePhaseDescriptors() = superCall("computePhaseDescriptors").asInstanceOf[List[SubComponent]]
private[this] def superDropRun(): Unit =
try { superCall("dropRun") } catch { case e: NoSuchMethodException => () } // dropRun not in 2.8.1
private[this] def superCall(methodName: String): AnyRef =
val meth = classOf[Global].getDeclaredMethod(methodName)
def logUnreportedWarnings(seq: Seq[(String, List[(Position,String)])]): Unit = // Scala 2.10.x and later
val drep = reporter.asInstanceOf[DelegatingReporter]
for( (what, warnings) <- seq; (pos, msg) <- warnings) yield
callback.problem(what, drep.convert(pos), msg, Severity.Warn, false)
def set(callback: AnalysisCallback, dreporter: DelegatingReporter)
this.callback0 = callback
reporter = dreporter
def clear()
callback0 = null
reporter = null
def findClass(name: String): Option[(AbstractFile, Boolean)] =
getOutputClass(name).map(f => (f,true)) orElse findOnClassPath(name).map(f =>(f, false))
def getOutputClass(name: String): Option[AbstractFile] =
// This could be improved if a hint where to look is given.
val className = name.replace('.', '/') + ".class"
outputDirs map (new File(_, className)) find (_.exists) map (AbstractFile.getFile(_))
def findOnClassPath(name: String): Option[AbstractFile] =
private[this] var callback0: AnalysisCallback = null
def callback: AnalysisCallback = callback0
Jump to Line
Something went wrong with that request. Please try again.