diff --git a/compile/interface/src/main/scala/xsbt/Analyzer.scala b/compile/interface/src/main/scala/xsbt/Analyzer.scala index 0e1c89bf9..dd11fe0e0 100644 --- a/compile/interface/src/main/scala/xsbt/Analyzer.scala +++ b/compile/interface/src/main/scala/xsbt/Analyzer.scala @@ -23,39 +23,13 @@ final class Analyzer(val global: CallbackGlobal) extends LocateClassFile def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev) private class AnalyzerPhase(prev: Phase) extends Phase(prev) { - override def description = "Extracts dependency information, finds concrete instances of provided superclasses, and application entry points." + override def description = "Finds concrete instances of provided superclasses, and application entry points." def name = Analyzer.name def run { for(unit <- currentRun.units if !unit.isJava) { - // build dependencies structure val sourceFile = unit.source.file.file - for(on <- unit.depends) processDependency(on, inherited=false) - for(on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, inherited=true) - def processDependency(on: Symbol, inherited: Boolean) - { - def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, inherited) - val onSource = on.sourceFile - if(onSource == null) - { - classFile(on) match - { - case Some((f,className,inOutDir)) => - if(inOutDir && on.isJavaDefined) registerTopLevelSym(on) - f match - { - case ze: ZipArchive#Entry => for(zip <- ze.underlyingSource; zipFile <- Option(zip.file) ) binaryDependency(zipFile, className) - case pf: PlainFile => binaryDependency(pf.file, className) - case _ => () - } - case None => () - } - } - else - callback.sourceDependency(onSource.file, sourceFile, inherited) - } - // build list of generated classes for(iclass <- unit.icode) { @@ -77,6 +51,5 @@ final class Analyzer(val global: CallbackGlobal) extends LocateClassFile } } } - } diff --git a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala b/compile/interface/src/main/scala/xsbt/CompilerInterface.scala index 7f94d1dab..ea60f1422 100644 --- a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala +++ b/compile/interface/src/main/scala/xsbt/CompilerInterface.scala @@ -176,6 +176,60 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial def newPhase(prev: Phase) = analyzer.newPhase(prev) def name = phaseName } + + /** Phase that extracts dependency information */ + object sbtDependency extends + { + val global: Compiler.this.type = Compiler.this + val phaseName = Dependency.name + val runsAfter = List(API.name) + override val runsBefore = List("refchecks") + /* We set runsRightAfter to work-around a bug with phase ordering related to + * continuations plugin. See SI-7217. + * + * If runsRightAfter == None, we get the following set of phases (with continuations + * begin enabled): + * + * typer 4 the meat and potatoes: type the trees + * superaccessors 5 add super accessors in traits and nested classes + * pickler 6 serialize symbol tables + * xsbt-api 7 + * selectiveanf 8 + * xsbt-dependency 9 + * refchecks 10 reference/override checking, translate nested objects + * selectivecps 11 + * liftcode 12 reify trees + * uncurry 13 uncurry, translate function values to anonymous classes + * + * Notice that `selectiveanf` (one of continuations phases) runs before `refchecks` + * and that causes NPEs in `selectiveansf`. + * However, the default ordering for Scala 2.9.2 is: + * + * typer 4 the meat and potatoes: type the trees + * superaccessors 5 add super accessors in traits and nested classes + * pickler 6 serialize symbol tables + * refchecks 7 reference/override checking, translate nested objects + * selectiveanf 8 + * liftcode 9 reify trees + * selectivecps 10 + * uncurry 11 uncurry, translate function values to anonymous classes + * + * Here `selectiveanf` runs after refchecks and that's the correct ordering. The + * true issue is that `selectiveanf` has hidden dependency on `refchecks` and + * that bites us when we insert xsbt-dependency phase. + * + * By declaring `runsRightAfter` we make the phase ordering algorithm to schedule + * `selectiveanf` to run after `refchecks` again. + */ + val runsRightAfter = Some(API.name) + } + with SubComponent + { + val dependency = new Dependency(global) + def newPhase(prev: Phase) = dependency.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 @@ -202,6 +256,7 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial override lazy val phaseDescriptors = { phasesSet += sbtAnalyzer + phasesSet += sbtDependency phasesSet += apiExtractor superComputePhaseDescriptors } diff --git a/compile/interface/src/main/scala/xsbt/Dependency.scala b/compile/interface/src/main/scala/xsbt/Dependency.scala new file mode 100644 index 000000000..602eab49a --- /dev/null +++ b/compile/interface/src/main/scala/xsbt/Dependency.scala @@ -0,0 +1,59 @@ +/* sbt -- Simple Build Tool + * Copyright 2008, 2009 Mark Harrah + */ +package xsbt + +import scala.tools.nsc.{io, symtab, Phase} +import io.{AbstractFile, PlainFile, ZipArchive} +import symtab.Flags + +import java.io.File + +object Dependency +{ + def name = "xsbt-dependency" +} +final class Dependency(val global: CallbackGlobal) extends LocateClassFile +{ + import global._ + + def newPhase(prev: Phase): Phase = new DependencyPhase(prev) + private class DependencyPhase(prev: Phase) extends Phase(prev) + { + override def description = "Extracts dependency information" + def name = Dependency.name + def run + { + for(unit <- currentRun.units if !unit.isJava) + { + // build dependencies structure + val sourceFile = unit.source.file.file + for(on <- unit.depends) processDependency(on, inherited=false) + for(on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, inherited=true) + def processDependency(on: Symbol, inherited: Boolean) + { + def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, inherited) + val onSource = on.sourceFile + if(onSource == null) + { + classFile(on) match + { + case Some((f,className,inOutDir)) => + if(inOutDir && on.isJavaDefined) registerTopLevelSym(on) + f match + { + case ze: ZipArchive#Entry => for(zip <- ze.underlyingSource; zipFile <- Option(zip.file) ) binaryDependency(zipFile, className) + case pf: PlainFile => binaryDependency(pf.file, className) + case _ => () + } + case None => () + } + } + else + callback.sourceDependency(onSource.file, sourceFile, inherited) + } + } + } + } + +}