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:
Grzegorz Kossakowski 2013-10-24 12:25:37 +02:00
parent fea18a4fbe
commit 838416360a
3 changed files with 115 additions and 28 deletions

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -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)
}
}
}
}
}