diff --git a/compile/inc/src/main/scala/sbt/inc/Compile.scala b/compile/inc/src/main/scala/sbt/inc/Compile.scala index e5dae387c..ce1803ccc 100644 --- a/compile/inc/src/main/scala/sbt/inc/Compile.scala +++ b/compile/inc/src/main/scala/sbt/inc/Compile.scala @@ -151,9 +151,6 @@ private final class AnalysisCallback(internalMap: File => Option[File], external apis(sourceFile) = (HashAPI(source), savedSource) } - def endSource(sourcePath: File): Unit = - assert(apis.contains(sourcePath)) - def get: Analysis = addCompilation( addExternals( addBinaries( addProducts( addSources(Analysis.Empty) ) ) ) ) def addProducts(base: Analysis): Analysis = addAll(base, classes) { case (a, src, (prod, name)) => a.addProduct(src, prod, current product prod, name ) } def addBinaries(base: Analysis): Analysis = addAll(base, binaryDeps)( (a, src, bin) => a.addBinaryDep(src, bin, binaryClassName(bin), current binary bin) ) @@ -178,6 +175,4 @@ private final class AnalysisCallback(internalMap: File => Option[File], external (outer /: bs) { (inner, b) => f(inner, a, b) } } - - def beginSource(source: File) {} } diff --git a/compile/interface/src/main/scala/xsbt/Analyzer.scala b/compile/interface/src/main/scala/xsbt/Analyzer.scala index 0f7737305..dd11fe0e0 100644 --- a/compile/interface/src/main/scala/xsbt/Analyzer.scala +++ b/compile/interface/src/main/scala/xsbt/Analyzer.scala @@ -23,40 +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 - callback.beginSource(sourceFile) - 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) { @@ -75,10 +48,8 @@ final class Analyzer(val global: CallbackGlobal) extends LocateClassFile else addGenerated(false) } - callback.endSource(sourceFile) } } } - } diff --git a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala b/compile/interface/src/main/scala/xsbt/CompilerInterface.scala index 7f94d1dab..9d1285640 100644 --- a/compile/interface/src/main/scala/xsbt/CompilerInterface.scala +++ b/compile/interface/src/main/scala/xsbt/CompilerInterface.scala @@ -176,6 +176,26 @@ 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") + // keep API and dependency close to each other + // we might want to merge them in the future and even if don't + // do that then it makes sense to run those phases next to each other + 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 +222,7 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial override lazy val phaseDescriptors = { phasesSet += sbtAnalyzer + phasesSet += sbtDependency phasesSet += apiExtractor superComputePhaseDescriptors } @@ -221,7 +242,7 @@ private final class CachedCompiler0(args: Array[String], output: Output, initial 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 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..8035574e6 --- /dev/null +++ b/compile/interface/src/main/scala/xsbt/Dependency.scala @@ -0,0 +1,74 @@ +/* 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" +} +/** + * Extracts dependency information from each compilation unit. + * + * This phase uses CompilationUnit.depends and CallbackGlobal.inheritedDependencies + * to collect all symbols that given compilation unit depends on. Those symbols are + * guaranteed to represent Class-like structures. + * + * The CallbackGlobal.inheritedDependencies is populated by the API phase. See, + * ExtractAPI class. + * + * When dependency symbol is processed, it is mapped back to either source file where + * it's defined in (if it's available in current compilation run) or classpath entry + * where it originates from. The Symbol->Classfile mapping is implemented by + * LocateClassFile that we inherit from. + */ +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) + } + } + } + } + +} diff --git a/interface/src/main/java/xsbti/AnalysisCallback.java b/interface/src/main/java/xsbti/AnalysisCallback.java index d00f5b7ed..55a90f011 100644 --- a/interface/src/main/java/xsbti/AnalysisCallback.java +++ b/interface/src/main/java/xsbti/AnalysisCallback.java @@ -7,8 +7,6 @@ import java.io.File; public interface AnalysisCallback { - /** Called before the source at the given location is processed. */ - public void beginSource(File source); /** Called to indicate that the source file source depends on the source file * dependsOn. Note that only source files included in the current compilation will * passed to this method. Dependencies on classes generated by sources not in the current compilation will @@ -24,8 +22,6 @@ public interface AnalysisCallback /** Called to indicate that the source file source produces a class file at * module contain class name.*/ public void generatedClass(File source, File module, String name); - /** Called after the source at the given location has been processed. */ - public void endSource(File sourcePath); /** Called when the public API of a source file is extracted. */ public void api(File sourceFile, xsbti.api.SourceAPI source); /** Provides problems discovered during compilation. These may be reported (logged) or unreported. diff --git a/interface/src/test/scala/TestCallback.scala b/interface/src/test/scala/TestCallback.scala index 061457723..5c0de068e 100644 --- a/interface/src/test/scala/TestCallback.scala +++ b/interface/src/test/scala/TestCallback.scala @@ -5,20 +5,15 @@ package xsbti class TestCallback extends AnalysisCallback { - val beganSources = new ArrayBuffer[File] - val endedSources = new ArrayBuffer[File] val sourceDependencies = new ArrayBuffer[(File, File, Boolean)] val binaryDependencies = new ArrayBuffer[(File, String, File, Boolean)] val products = new ArrayBuffer[(File, File, String)] val apis = new ArrayBuffer[(File, xsbti.api.SourceAPI)] - def beginSource(source: File) { beganSources += source } - def sourceDependency(dependsOn: File, source: File, inherited: Boolean) { sourceDependencies += ((dependsOn, source, inherited)) } def binaryDependency(binary: File, name: String, source: File, inherited: Boolean) { binaryDependencies += ((binary, name, source, inherited)) } def generatedClass(source: File, module: File, name: String) { products += ((source, module, name)) } - def endSource(source: File) { endedSources += source } def api(source: File, sourceAPI: xsbti.api.SourceAPI) { apis += ((source, sourceAPI)) } def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean) {} -} \ No newline at end of file +} diff --git a/util/classfile/src/main/scala/sbt/classfile/Analyze.scala b/util/classfile/src/main/scala/sbt/classfile/Analyze.scala index 117b718ae..724eb1a1e 100644 --- a/util/classfile/src/main/scala/sbt/classfile/Analyze.scala +++ b/util/classfile/src/main/scala/sbt/classfile/Analyze.scala @@ -33,12 +33,11 @@ private[sbt] object Analyze sourceFile <- classFile.sourceFile orElse guessSourceName(newClass.getName); source <- guessSourcePath(sourceMap, classFile, log)) { - analysis.beginSource(source) analysis.generatedClass(source, newClass, classFile.className) productToSource(newClass) = source sourceToClassFiles.getOrElseUpdate(source, new ArrayBuffer[ClassFile]) += classFile } - + // get class to class dependencies and map back to source to class dependencies for( (source, classFiles) <- sourceToClassFiles ) { @@ -65,17 +64,14 @@ private[sbt] object Analyze } } def processDependencies(tpes: Iterable[String], inherited: Boolean): Unit = tpes.foreach(tpe => processDependency(tpe, inherited)) - + val notInherited = classFiles.flatMap(_.types).toSet -- publicInherited processDependencies(notInherited, false) processDependencies(publicInherited, true) - analysis.endSource(source) } for( source <- sources filterNot sourceToClassFiles.keySet ) { - analysis.beginSource(source) analysis.api(source, new xsbti.api.SourceAPI(Array(), Array())) - analysis.endSource(source) } } private[this] def urlAsFile(url: URL, log: Logger): Option[File] = @@ -114,7 +110,7 @@ private[sbt] object Analyze } private def findSource(sourceNameMap: Map[String, Iterable[File]], pkg: List[String], sourceFileName: String): List[File] = refine( (sourceNameMap get sourceFileName).toList.flatten.map { x => (x,x.getParentFile) }, pkg.reverse) - + @tailrec private def refine(sources: List[(File, File)], pkgRev: List[String]): List[File] = { def make = sources.map(_._1) @@ -155,7 +151,7 @@ private[sbt] object Analyze method.getReturnType == unit && method.getParameterTypes.toList == strArray private def isMain(modifiers: Int): Boolean = (modifiers & mainModifiers) == mainModifiers && (modifiers & notMainModifiers) == 0 - + private val mainModifiers = STATIC | PUBLIC private val notMainModifiers = ABSTRACT }