From 838416360ae0b1c27aa0862b6856600b5ab2d652 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Thu, 24 Oct 2013 12:25:37 +0200 Subject: [PATCH] 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. --- .../src/main/scala/xsbt/Analyzer.scala | 29 +-------- .../main/scala/xsbt/CompilerInterface.scala | 55 +++++++++++++++++ .../src/main/scala/xsbt/Dependency.scala | 59 +++++++++++++++++++ 3 files changed, 115 insertions(+), 28 deletions(-) create mode 100644 compile/interface/src/main/scala/xsbt/Dependency.scala 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) + } + } + } + } + +}