mirror of https://github.com/sbt/sbt.git
Move dependency extraction into separate compiler phase.
This is the first step towards using new mechanism for dependency extraction that is based on tree walking. We need dependency extraction in separate phase because the code walking trees should run before refchecks whereas analyzer phase runs at the very end of phase pipeline. This change also includes a work-around for phase ordering issue with continuations plugin. See included comment and SI-7217 for details.
This commit is contained in:
parent
fea18a4fbe
commit
838416360a
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue