mirror of https://github.com/sbt/sbt.git
Merge pull request #1736 from Duhemm/dependency-kind-compile
Abstract over dependency kind in Compile
This commit is contained in:
commit
015c61ad69
|
|
@ -5,6 +5,7 @@ package sbt
|
|||
package inc
|
||||
|
||||
import xsbti.api.Source
|
||||
import xsbti.DependencyContext._
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import xsbti.{ Position, Problem, Severity }
|
|||
import Logger.{ m2o, problem }
|
||||
import java.io.File
|
||||
import xsbti.api.Definition
|
||||
import xsbti.DependencyContext
|
||||
import xsbti.DependencyContext.{ DependencyByInheritance, DependencyByMemberRef }
|
||||
|
||||
object IncrementalCompile {
|
||||
def apply(sources: Set[File], entry: String => Option[File],
|
||||
|
|
@ -61,7 +63,7 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
new Compilation(System.currentTimeMillis, outputSettings)
|
||||
}
|
||||
|
||||
override def toString = (List("APIs", "Binary deps", "Products", "Source deps") zip List(apis, binaryDeps, classes, sourceDeps)).map { case (label, map) => label + "\n\t" + map.mkString("\n\t") }.mkString("\n")
|
||||
override def toString = (List("APIs", "Binary deps", "Products", "Source deps") zip List(apis, binaryDeps, classes, intSrcDeps)).map { case (label, map) => label + "\n\t" + map.mkString("\n\t") }.mkString("\n")
|
||||
|
||||
import collection.mutable.{ HashMap, HashSet, ListBuffer, Map, Set }
|
||||
|
||||
|
|
@ -75,12 +77,10 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
private[this] val classes = new HashMap[File, Set[(File, String)]]
|
||||
// generated class file to its source file
|
||||
private[this] val classToSource = new HashMap[File, File]
|
||||
// all internal source depenencies, including direct and inherited
|
||||
private[this] val sourceDeps = new HashMap[File, Set[File]]
|
||||
// inherited internal source dependencies
|
||||
private[this] val inheritedSourceDeps = new HashMap[File, Set[File]]
|
||||
// internal source dependencies
|
||||
private[this] val intSrcDeps = new HashMap[File, Set[InternalDependency]]
|
||||
// external source dependencies
|
||||
private[this] val extSrcDeps = new HashMap[File, Iterable[ExternalDependency]]
|
||||
private[this] val extSrcDeps = new HashMap[File, Set[ExternalDependency]]
|
||||
private[this] val binaryClassName = new HashMap[File, String]
|
||||
// source files containing a macro def.
|
||||
private[this] val macroSources = Set[File]()
|
||||
|
|
@ -96,46 +96,57 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
}
|
||||
}
|
||||
|
||||
def sourceDependency(dependsOn: File, source: File, context: DependencyContext) = {
|
||||
add(intSrcDeps, source, InternalDependency(source, dependsOn, context))
|
||||
}
|
||||
|
||||
@deprecated("Use `sourceDependency(File, File, DependencyContext)`.", "0.13.8")
|
||||
def sourceDependency(dependsOn: File, source: File, inherited: Boolean) =
|
||||
{
|
||||
add(sourceDeps, source, dependsOn)
|
||||
if (inherited) add(inheritedSourceDeps, source, dependsOn)
|
||||
val context = if (inherited) DependencyByInheritance else DependencyByMemberRef
|
||||
sourceDependency(dependsOn, source, context)
|
||||
}
|
||||
def externalBinaryDependency(binary: File, className: String, source: File, inherited: Boolean) {
|
||||
|
||||
private[this] def externalBinaryDependency(binary: File, className: String, source: File, context: DependencyContext) = {
|
||||
binaryClassName.put(binary, className)
|
||||
add(binaryDeps, source, binary)
|
||||
}
|
||||
|
||||
// The type corresponds to : (internal source, external source depended on, API of external dependency, true if an inheritance dependency)
|
||||
def externalSourceDependency(t4: (File, String, Source, Boolean)) = {
|
||||
val dependency = ExternalDependency(t4._1, t4._2, t4._3, if (t4._4) DependencyByInheritance else DependencyByMemberRef)
|
||||
extSrcDeps += t4._1 -> (extSrcDeps.getOrElse(t4._1, Nil) ++ List(dependency))
|
||||
private[this] def externalSourceDependency(sourceFile: File, dependsOn: String, source: Source, context: DependencyContext) = {
|
||||
val dependency = ExternalDependency(sourceFile, dependsOn, source, context)
|
||||
add(extSrcDeps, sourceFile, dependency)
|
||||
}
|
||||
|
||||
def binaryDependency(classFile: File, name: String, source: File, inherited: Boolean) =
|
||||
def binaryDependency(classFile: File, name: String, source: File, context: DependencyContext) =
|
||||
internalMap(classFile) match {
|
||||
case Some(dependsOn) =>
|
||||
// dependency is a product of a source not included in this compilation
|
||||
sourceDependency(dependsOn, source, inherited)
|
||||
sourceDependency(dependsOn, source, context)
|
||||
case None =>
|
||||
classToSource.get(classFile) match {
|
||||
case Some(dependsOn) =>
|
||||
// dependency is a product of a source in this compilation step,
|
||||
// but not in the same compiler run (as in javac v. scalac)
|
||||
sourceDependency(dependsOn, source, inherited)
|
||||
sourceDependency(dependsOn, source, context)
|
||||
case None =>
|
||||
externalDependency(classFile, name, source, inherited)
|
||||
externalDependency(classFile, name, source, context)
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def externalDependency(classFile: File, name: String, source: File, inherited: Boolean): Unit =
|
||||
@deprecated("Use `binaryDependency(File, String, File, DependencyContext)`.", "0.13.8")
|
||||
def binaryDependency(classFile: File, name: String, source: File, inherited: Boolean) = {
|
||||
val context = if (inherited) DependencyByInheritance else DependencyByMemberRef
|
||||
binaryDependency(classFile, name, source, context)
|
||||
}
|
||||
|
||||
private[this] def externalDependency(classFile: File, name: String, source: File, context: DependencyContext): Unit =
|
||||
externalAPI(classFile, name) match {
|
||||
case Some(api) =>
|
||||
// dependency is a product of a source in another project
|
||||
externalSourceDependency((source, name, api, inherited))
|
||||
externalSourceDependency(source, name, api, context)
|
||||
case None =>
|
||||
// dependency is some other binary on the classpath
|
||||
externalBinaryDependency(classFile, name, source, inherited)
|
||||
externalBinaryDependency(classFile, name, source, context)
|
||||
}
|
||||
|
||||
def generatedClass(source: File, module: File, name: String) =
|
||||
|
|
@ -174,16 +185,6 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
(a /: names) { case (a, name) => a.copy(relations = a.relations.addUsedName(src, name)) }
|
||||
}
|
||||
|
||||
// This is no longer used by the new implementation relative to Dependency contexts
|
||||
// See https://github.com/sbt/sbt/issues/1340
|
||||
def addAll[A, B](base: Analysis, m: Map[A, Set[B]])(f: (Analysis, A, B) => Analysis): Analysis =
|
||||
(base /: m) {
|
||||
case (outer, (a, bs)) =>
|
||||
(outer /: bs) { (inner, b) =>
|
||||
f(inner, a, b)
|
||||
}
|
||||
}
|
||||
|
||||
def addProductsAndDeps(base: Analysis): Analysis =
|
||||
(base /: apis) {
|
||||
case (a, (src, api)) =>
|
||||
|
|
@ -193,14 +194,12 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
val hasMacro: Boolean = macroSources.contains(src)
|
||||
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, publicNameHashes(src), hasMacro)
|
||||
val info = SourceInfos.makeInfo(getOrNil(reporteds, src), getOrNil(unreporteds, src))
|
||||
val direct = sourceDeps.getOrElse(src, Nil: Iterable[File])
|
||||
val publicInherited = inheritedSourceDeps.getOrElse(src, Nil: Iterable[File])
|
||||
val binaries = binaryDeps.getOrElse(src, Nil: Iterable[File])
|
||||
val prods = classes.getOrElse(src, Nil: Iterable[(File, String)])
|
||||
|
||||
val products = prods.map { case (prod, name) => (prod, name, current product prod) }
|
||||
val internalDeps = direct.map(InternalDependency(src, _, DependencyByMemberRef)) ++ publicInherited.map(InternalDependency(src, _, DependencyByInheritance))
|
||||
val externalDeps = extSrcDeps.getOrElse(src, Nil: Iterable[ExternalDependency])
|
||||
val internalDeps = intSrcDeps.getOrElse(src, Set.empty)
|
||||
val externalDeps = extSrcDeps.getOrElse(src, Set.empty)
|
||||
val binDeps = binaries.map(d => (d, binaryClassName(d), current binary d))
|
||||
|
||||
a.addSource(src, s, stamp, info, products, internalDeps, externalDeps, binDeps)
|
||||
|
|
|
|||
|
|
@ -2,22 +2,7 @@ package sbt.inc
|
|||
|
||||
import java.io.File
|
||||
import xsbti.api.Source
|
||||
|
||||
/**
|
||||
* Represents contextual information about particular depedency edge. See comments in
|
||||
* subtypes for examples of particular contexts.
|
||||
*/
|
||||
private[inc] sealed abstract class DependencyContext
|
||||
|
||||
/**
|
||||
* Marks dependency edge introduced by referring to a class through inheritance as in
|
||||
*
|
||||
* class A extends B
|
||||
*
|
||||
* Each dependency by inheritance introduces corresponding dependency by member reference.
|
||||
*/
|
||||
private[inc] final case object DependencyByInheritance extends DependencyContext
|
||||
private[inc] final case object DependencyByMemberRef extends DependencyContext
|
||||
import xsbti.DependencyContext
|
||||
|
||||
/**
|
||||
* Represents the kind of dependency that exists between `sourceFile` and either `targetFile`
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import java.io.File
|
|||
import Relations.Source
|
||||
import Relations.SourceDependencies
|
||||
import xsbti.api.{ Source => APISource }
|
||||
import xsbti.DependencyContext
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
/**
|
||||
* Provides mappings between source files, generated classes (products), and binaries.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import sbt.inc.TestCaseGenerators._
|
|||
import org.scalacheck._
|
||||
import Gen._
|
||||
import Prop._
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
object AnalysisTest extends Properties("Analysis") {
|
||||
// Merge and split a hard-coded trivial example.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import Gen._
|
|||
import sbt.Relation
|
||||
import xsbti.api._
|
||||
import xsbti.SafeLazy
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
/**
|
||||
* Scalacheck generators for Analysis objects and their substructures.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ package xsbt
|
|||
import scala.tools.nsc.{ io, symtab, Phase }
|
||||
import io.{ AbstractFile, PlainFile, ZipArchive }
|
||||
import symtab.Flags
|
||||
import xsbti.DependencyContext
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -41,22 +43,22 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
|||
if (global.callback.nameHashing) {
|
||||
val dependenciesByMemberRef = extractDependenciesByMemberRef(unit)
|
||||
for (on <- dependenciesByMemberRef)
|
||||
processDependency(on, inherited = false)
|
||||
processDependency(on, context = DependencyByMemberRef)
|
||||
|
||||
val dependenciesByInheritance = extractDependenciesByInheritance(unit)
|
||||
for (on <- dependenciesByInheritance)
|
||||
processDependency(on, inherited = true)
|
||||
processDependency(on, context = DependencyByInheritance)
|
||||
} else {
|
||||
for (on <- unit.depends) processDependency(on, inherited = false)
|
||||
for (on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, inherited = true)
|
||||
for (on <- unit.depends) processDependency(on, context = DependencyByMemberRef)
|
||||
for (on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, context = DependencyByInheritance)
|
||||
}
|
||||
/**
|
||||
* Handles dependency on given symbol by trying to figure out if represents a term
|
||||
* that is coming from either source code (not necessarily compiled in this compilation
|
||||
* run) or from class file and calls respective callback method.
|
||||
*/
|
||||
def processDependency(on: Symbol, inherited: Boolean) {
|
||||
def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, inherited)
|
||||
def processDependency(on: Symbol, context: DependencyContext) {
|
||||
def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, context)
|
||||
val onSource = on.sourceFile
|
||||
if (onSource == null) {
|
||||
classFile(on) match {
|
||||
|
|
@ -70,7 +72,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
|||
case None => ()
|
||||
}
|
||||
} else if (onSource.file != sourceFile)
|
||||
callback.sourceDependency(onSource.file, sourceFile, inherited)
|
||||
callback.sourceDependency(onSource.file, sourceFile, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import xsbti.api.Definition
|
|||
import xsbti.api.Def
|
||||
import xsbt.api.SameAPI
|
||||
import sbt.ConsoleLogger
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
import ScalaCompilerForUnitTesting.ExtractedSourceDependencies
|
||||
|
||||
|
|
@ -68,11 +69,11 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
|
|||
|
||||
val memberRefFileDeps = testCallback.sourceDependencies collect {
|
||||
// false indicates that those dependencies are not introduced by inheritance
|
||||
case (target, src, false) => (src, target)
|
||||
case (target, src, DependencyByMemberRef) => (src, target)
|
||||
}
|
||||
val inheritanceFileDeps = testCallback.sourceDependencies collect {
|
||||
// true indicates that those dependencies are introduced by inheritance
|
||||
case (target, src, true) => (src, target)
|
||||
case (target, src, DependencyByInheritance) => (src, target)
|
||||
}
|
||||
def toSymbols(src: File, target: File): (Symbol, Symbol) = (fileToSymbol(src), fileToSymbol(target))
|
||||
val memberRefDeps = memberRefFileDeps map { case (src, target) => toSymbols(src, target) }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package inc
|
|||
import xsbti.api.{ Source, Compilation }
|
||||
import xsbti.{ Position, Problem, Severity }
|
||||
import xsbti.compile.{ CompileOrder, Output => APIOutput, SingleOutput, MultipleOutput }
|
||||
import xsbti.DependencyContext._
|
||||
import MultipleOutput.OutputGroup
|
||||
import java.io.File
|
||||
import sbinary._
|
||||
|
|
|
|||
|
|
@ -12,13 +12,29 @@ public interface AnalysisCallback
|
|||
* passed to this method. Dependencies on classes generated by sources not in the current compilation will
|
||||
* be passed as class dependencies to the classDependency method.
|
||||
* If <code>publicInherited</code> is true, this dependency is a result of inheritance by a
|
||||
* template accessible outside of the source file. */
|
||||
* template accessible outside of the source file.
|
||||
* @deprecated Use `sourceDependency(File dependsOn, File source, DependencyContext context)` instead. */
|
||||
@Deprecated
|
||||
public void sourceDependency(File dependsOn, File source, boolean publicInherited);
|
||||
/** Called to indicate that the source file <code>source</code> depends on the source file
|
||||
* <code>dependsOn</code>. 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
|
||||
* be passed as class dependencies to the classDependency method.
|
||||
* <code>context</code> gives information about the context in which this dependency has been extracted.
|
||||
* See xsbti.DependencyContext for the list of existing dependency contexts. */
|
||||
public void sourceDependency(File dependsOn, File source, DependencyContext context);
|
||||
/** Called to indicate that the source file <code>source</code> depends on the top-level
|
||||
* class named <code>name</code> from class or jar file <code>binary</code>.
|
||||
* If <code>publicInherited</code> is true, this dependency is a result of inheritance by a
|
||||
* template accessible outside of the source file. */
|
||||
* template accessible outside of the source file.
|
||||
* @deprecated Use `binaryDependency(File binary, String name, File source, DependencyContext context)` instead. */
|
||||
@Deprecated
|
||||
public void binaryDependency(File binary, String name, File source, boolean publicInherited);
|
||||
/** Called to indicate that the source file <code>source</code> depends on the top-level
|
||||
* class named <code>name</code> from class or jar file <code>binary</code>.
|
||||
* <code>context</code> gives information about the context in which this dependency has been extracted.
|
||||
* See xsbti.DependencyContext for the list of existing dependency contexts. */
|
||||
public void binaryDependency(File binary, String name, File source, DependencyContext context);
|
||||
/** Called to indicate that the source file <code>source</code> produces a class file at
|
||||
* <code>module</code> contain class <code>name</code>.*/
|
||||
public void generatedClass(File source, File module, String name);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package xsbti;
|
||||
|
||||
/**
|
||||
* Enumeration of existing dependency contexts.
|
||||
* Dependency contexts represent the various kind of dependencies that
|
||||
* can exist between symbols.
|
||||
*/
|
||||
public enum DependencyContext {
|
||||
/**
|
||||
* Represents a direct dependency between two symbols :
|
||||
* object Foo
|
||||
* object Bar { def foo = Foo }
|
||||
*/
|
||||
DependencyByMemberRef,
|
||||
|
||||
/**
|
||||
* Represents an inheritance dependency between two symbols :
|
||||
* class A
|
||||
* class B extends A
|
||||
*/
|
||||
DependencyByInheritance
|
||||
}
|
||||
|
|
@ -3,17 +3,26 @@ package xsbti
|
|||
import java.io.File
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import xsbti.api.SourceAPI
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
class TestCallback(override val nameHashing: Boolean = false) extends AnalysisCallback
|
||||
{
|
||||
val sourceDependencies = new ArrayBuffer[(File, File, Boolean)]
|
||||
val binaryDependencies = new ArrayBuffer[(File, String, File, Boolean)]
|
||||
val sourceDependencies = new ArrayBuffer[(File, File, DependencyContext)]
|
||||
val binaryDependencies = new ArrayBuffer[(File, String, File, DependencyContext)]
|
||||
val products = new ArrayBuffer[(File, File, String)]
|
||||
val usedNames = scala.collection.mutable.Map.empty[File, Set[String]].withDefaultValue(Set.empty)
|
||||
val apis: scala.collection.mutable.Map[File, SourceAPI] = scala.collection.mutable.Map.empty
|
||||
|
||||
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 sourceDependency(dependsOn: File, source: File, inherited: Boolean) {
|
||||
val context = if(inherited) DependencyByInheritance else DependencyByMemberRef
|
||||
sourceDependency(dependsOn, source, context)
|
||||
}
|
||||
def sourceDependency(dependsOn: File, source: File, context: DependencyContext) { sourceDependencies += ((dependsOn, source, context)) }
|
||||
def binaryDependency(binary: File, name: String, source: File, inherited: Boolean) {
|
||||
val context = if(inherited) DependencyByInheritance else DependencyByMemberRef
|
||||
binaryDependency(binary, name, source, context)
|
||||
}
|
||||
def binaryDependency(binary: File, name: String, source: File, context: DependencyContext) { binaryDependencies += ((binary, name, source, context)) }
|
||||
def generatedClass(source: File, module: File, name: String) { products += ((source, module, name)) }
|
||||
|
||||
def usedName(source: File, name: String) { usedNames(source) += name }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import java.lang.annotation.Annotation
|
|||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier.{ STATIC, PUBLIC, ABSTRACT }
|
||||
import java.net.URL
|
||||
import xsbti.DependencyContext
|
||||
import xsbti.DependencyContext._
|
||||
|
||||
private[sbt] object Analyze {
|
||||
def apply[T](newClasses: Seq[File], sources: Seq[File], log: Logger)(analysis: xsbti.AnalysisCallback, loader: ClassLoader, readAPI: (File, Seq[Class[_]]) => Set[String]) {
|
||||
|
|
@ -41,26 +43,26 @@ private[sbt] object Analyze {
|
|||
for ((source, classFiles) <- sourceToClassFiles) {
|
||||
val publicInherited = readAPI(source, classFiles.toSeq.flatMap(c => load(c.className, Some("Error reading API from class file"))))
|
||||
|
||||
def processDependency(tpe: String, inherited: Boolean) {
|
||||
def processDependency(tpe: String, context: DependencyContext) {
|
||||
trapAndLog(log) {
|
||||
for (url <- Option(loader.getResource(tpe.replace('.', '/') + ClassExt)); file <- urlAsFile(url, log)) {
|
||||
if (url.getProtocol == "jar")
|
||||
analysis.binaryDependency(file, tpe, source, inherited)
|
||||
analysis.binaryDependency(file, tpe, source, context)
|
||||
else {
|
||||
assume(url.getProtocol == "file")
|
||||
productToSource.get(file) match {
|
||||
case Some(dependsOn) => analysis.sourceDependency(dependsOn, source, inherited)
|
||||
case None => analysis.binaryDependency(file, tpe, source, inherited)
|
||||
case Some(dependsOn) => analysis.sourceDependency(dependsOn, source, context)
|
||||
case None => analysis.binaryDependency(file, tpe, source, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def processDependencies(tpes: Iterable[String], inherited: Boolean): Unit = tpes.foreach(tpe => processDependency(tpe, inherited))
|
||||
def processDependencies(tpes: Iterable[String], context: DependencyContext): Unit = tpes.foreach(tpe => processDependency(tpe, context))
|
||||
|
||||
val notInherited = classFiles.flatMap(_.types).toSet -- publicInherited
|
||||
processDependencies(notInherited, false)
|
||||
processDependencies(publicInherited, true)
|
||||
processDependencies(notInherited, DependencyByMemberRef)
|
||||
processDependencies(publicInherited, DependencyByInheritance)
|
||||
}
|
||||
|
||||
for (source <- sources filterNot sourceToClassFiles.keySet) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue