Merge pull request #1340 from Duhemm/abstract-over-dependency-kind

Abstract over dependency kind in Analysis
This commit is contained in:
Grzegorz Kossakowski 2014-11-18 16:32:06 +01:00
commit b2c2ff698e
7 changed files with 341 additions and 99 deletions

View File

@ -51,9 +51,19 @@ trait Analysis {
def copy(stamps: Stamps = stamps, apis: APIs = apis, relations: Relations = relations, infos: SourceInfos = infos,
compilations: Compilations = compilations): Analysis
def addSource(src: File, api: Source, stamp: Stamp, info: SourceInfo,
products: Iterable[(File, String, Stamp)],
internalDeps: Iterable[InternalDependency],
externalDeps: Iterable[ExternalDependency],
binaryDeps: Iterable[(File, String, Stamp)]): Analysis
@deprecated("Register all products and dependencies in addSource.", "0.13.8")
def addSource(src: File, api: Source, stamp: Stamp, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis
@deprecated("Register all products and dependencies in addSource.", "0.13.8")
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis
@deprecated("Register all products and dependencies in addSource.", "0.13.8")
def addExternalDep(src: File, dep: String, api: Source, inherited: Boolean): Analysis
@deprecated("Register all products and dependencies in addSource.", "0.13.8")
def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis
/** Partitions this Analysis using the discriminator function. Externalizes internal deps that cross partitions. */
@ -155,17 +165,49 @@ private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relat
def copy(stamps: Stamps, apis: APIs, relations: Relations, infos: SourceInfos, compilations: Compilations = compilations): Analysis =
new MAnalysis(stamps, apis, relations, infos, compilations)
def addSource(src: File, api: Source, stamp: Stamp, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis =
copy(stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, directInternal, inheritedInternal), infos.add(src, info))
def addSource(src: File, api: Source, stamp: Stamp, info: SourceInfo,
products: Iterable[(File, String, Stamp)],
internalDeps: Iterable[InternalDependency],
externalDeps: Iterable[ExternalDependency],
binaryDeps: Iterable[(File, String, Stamp)]): Analysis = {
val newStamps = {
val productStamps = products.foldLeft(stamps.markInternalSource(src, stamp)) {
case (tmpStamps, (toProduct, _, prodStamp)) => tmpStamps.markProduct(toProduct, prodStamp)
}
binaryDeps.foldLeft(productStamps) {
case (tmpStamps, (toBinary, className, binStamp)) => tmpStamps.markBinary(toBinary, className, binStamp)
}
}
val newAPIs = externalDeps.foldLeft(apis.markInternalSource(src, api)) {
case (tmpApis, ExternalDependency(_, toClassName, classApi, _)) => tmpApis.markExternalAPI(toClassName, classApi)
}
val newRelations = relations.addSource(src, products map (p => (p._1, p._2)), internalDeps, externalDeps, binaryDeps)
copy(newStamps, newAPIs, newRelations, infos.add(src, info))
}
def addSource(src: File, api: Source, stamp: Stamp, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis = {
val directDeps = directInternal.map(InternalDependency(src, _, DependencyByMemberRef))
val inheritedDeps = inheritedInternal.map(InternalDependency(src, _, DependencyByInheritance))
addSource(src, api, stamp, info, products = Nil, directDeps ++ inheritedDeps, Nil, Nil)
}
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis =
copy(stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDep(src, dep), infos)
copy(stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDeps(src, (dep, className, stamp) :: Nil), infos)
def addExternalDep(src: File, dep: String, depAPI: Source, inherited: Boolean): Analysis =
copy(stamps, apis.markExternalAPI(dep, depAPI), relations.addExternalDep(src, dep, inherited), infos)
def addExternalDep(src: File, dep: String, depAPI: Source, inherited: Boolean): Analysis = {
val context = if (inherited) DependencyByInheritance else DependencyByMemberRef
copy(stamps, apis.markExternalAPI(dep, depAPI), relations.addExternalDeps(src, ExternalDependency(src, dep, depAPI, context) :: Nil), infos)
}
def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis =
copy(stamps.markProduct(product, stamp), apis, relations.addProduct(src, product, name), infos)
copy(stamps.markProduct(product, stamp), apis, relations.addProducts(src, (product, name) :: Nil), infos)
def groupBy[K](discriminator: File => K): Map[K, Analysis] = {
if (relations.nameHashing)

View File

@ -79,9 +79,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
private[this] val sourceDeps = new HashMap[File, Set[File]]
// inherited internal source dependencies
private[this] val inheritedSourceDeps = new HashMap[File, Set[File]]
// external source dependencies:
// (internal source, external source depended on, API of external dependency, true if an inheritance dependency)
private[this] val extSrcDeps = new ListBuffer[(File, String, Source, Boolean)]
// external source dependencies
private[this] val extSrcDeps = new HashMap[File, Iterable[ExternalDependency]]
private[this] val binaryClassName = new HashMap[File, String]
// source files containing a macro def.
private[this] val macroSources = Set[File]()
@ -106,7 +105,12 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
binaryClassName.put(binary, className)
add(binaryDeps, source, binary)
}
def externalSourceDependency(t4: (File, String, Source, Boolean)) = extSrcDeps += t4
// 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))
}
def binaryDependency(classFile: File, name: String, source: File, inherited: Boolean) =
internalMap(classFile) match {
@ -161,10 +165,26 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
def nameHashing: Boolean = options.nameHashing
def get: Analysis = addUsedNames(addCompilation(addExternals(addBinaries(addProducts(addSources(Analysis.empty(nameHashing = nameHashing)))))))
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))
def addSources(base: Analysis): Analysis =
def get: Analysis = addUsedNames(addCompilation(addProductsAndDeps(Analysis.empty(nameHashing = nameHashing))))
def getOrNil[A, B](m: collection.Map[A, Seq[B]], a: A): Seq[B] = m.get(a).toList.flatten
def addCompilation(base: Analysis): Analysis = base.copy(compilations = base.compilations.add(compilation))
def addUsedNames(base: Analysis): Analysis = (base /: usedNames) {
case (a, (src, names)) =>
(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)) =>
val stamp = current.internalSource(src)
@ -175,21 +195,15 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
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])
a.addSource(src, s, stamp, direct, publicInherited, info)
}
def getOrNil[A, B](m: collection.Map[A, Seq[B]], a: A): Seq[B] = m.get(a).toList.flatten
def addExternals(base: Analysis): Analysis = (base /: extSrcDeps) { case (a, (source, name, api, inherited)) => a.addExternalDep(source, name, api, inherited) }
def addCompilation(base: Analysis): Analysis = base.copy(compilations = base.compilations.add(compilation))
def addUsedNames(base: Analysis): Analysis = (base /: usedNames) {
case (a, (src, names)) =>
(a /: names) { case (a, name) => a.copy(relations = a.relations.addUsedName(src, name)) }
}
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 binDeps = binaries.map(d => (d, binaryClassName(d), current binary d))
a.addSource(src, s, stamp, info, products, internalDeps, externalDeps, binDeps)
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)
}
}
}

View File

@ -0,0 +1,30 @@
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
/**
* Represents the kind of dependency that exists between `sourceFile` and either `targetFile`
* or `targetClassName`.
*
* `InternalDependency` represent dependencies that exist between the files of a same project,
* while `ExternalDependency` represent cross-project dependencies.
*/
private[inc] final case class InternalDependency(sourceFile: File, targetFile: File, context: DependencyContext)
private[inc] final case class ExternalDependency(sourceFile: File, targetClassName: String, targetSource: Source, context: DependencyContext)

View File

@ -7,6 +7,7 @@ package inc
import java.io.File
import Relations.Source
import Relations.SourceDependencies
import xsbti.api.{ Source => APISource }
/**
* Provides mappings between source files, generated classes (products), and binaries.
@ -61,16 +62,19 @@ trait Relations {
private[inc] def usedNames(src: File): Set[String]
/** Records internal source file `src` as generating class file `prod` with top-level class `name`. */
@deprecated("Record all products using `addProducts`.", "0.13.8")
def addProduct(src: File, prod: File, name: String): Relations
/**
* Records internal source file `src` as depending on class `dependsOn` in an external source file.
* If `inherited` is true, this dependency is recorded as coming from a public template in `src` extending something in `dependsOn` (an inheritance dependency).
* Whatever the value of `inherited`, the dependency is also recorded as a direct dependency.
* Records internal source file `src` as dependending on `dependsOn`. If this dependency is introduced
* by an inheritance relation, `inherited` is set to true. Note that in this case, the dependency is
* also registered as a direct dependency.
*/
@deprecated("Record all external dependencies using `addExternalDeps`.", "0.13.8")
def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations
/** Records internal source file `src` depending on a dependency binary dependency `dependsOn`.*/
@deprecated("Record all binary dependencies using `addBinaryDeps`.", "0.13.8")
def addBinaryDep(src: File, dependsOn: File): Relations
/**
@ -78,8 +82,40 @@ trait Relations {
* and inheritance dependencies on `inheritedDependsOn`. Everything in `inheritedDependsOn` must be included in `directDependsOn`;
* this method does not automatically record direct dependencies like `addExternalDep` does.
*/
@deprecated("Record all internal dependencies using `addInternalSrcDeps(File, Iterable[InternalDependencies])`.", "0.13.8")
def addInternalSrcDeps(src: File, directDependsOn: Iterable[File], inheritedDependsOn: Iterable[File]): Relations
/**
* Records that the file `src` generates products `products`, has internal dependencies `internalDeps`,
* has external dependencies `externalDeps` and binary dependencies `binaryDeps`.
*/
def addSource(src: File,
products: Iterable[(File, String)],
internalDeps: Iterable[InternalDependency],
externalDeps: Iterable[ExternalDependency],
binaryDeps: Iterable[(File, String, Stamp)]): Relations =
addProducts(src, products).addInternalSrcDeps(src, internalDeps).addExternalDeps(src, externalDeps).addBinaryDeps(src, binaryDeps)
/**
* Records all the products `prods` generated by `src`
*/
private[inc] def addProducts(src: File, prods: Iterable[(File, String)]): Relations
/**
* Records all the internal source dependencies `deps` of `src`
*/
private[inc] def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]): Relations
/**
* Records all the external dependencies `deps` of `src`
*/
private[inc] def addExternalDeps(src: File, deps: Iterable[ExternalDependency]): Relations
/**
* Records all the binary dependencies `deps` of `src`
*/
private[inc] def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]): Relations
private[inc] def addUsedName(src: File, name: String): Relations
/** Concatenates the two relations. Acts naively, i.e., doesn't internalize external deps on added files. */
@ -103,6 +139,12 @@ trait Relations {
/** The dependency relation between internal and external sources. This includes both direct and inherited dependencies.*/
def externalDep: Relation[File, String]
/** All the internal dependencies */
private[inc] def internalDependencies: InternalDependencies
/** All the external dependencies */
private[inc] def externalDependencies: ExternalDependencies
/**
* The source dependency relation between source files introduced by member reference.
*
@ -227,9 +269,11 @@ object Relations {
assert(nameHashing || (memberRefSrcDeps == emptySourceDependencies), "When name hashing is disabled the `memberRef` relation should be empty.")
assert(!nameHashing || (directSrcDeps == emptySource), "When name hashing is enabled the `direct` relation should be empty.")
if (nameHashing)
Relations.make(srcProd, binaryDep, memberRefSrcDeps, inheritanceSrcDeps, classes, names)
else {
if (nameHashing) {
val internal = InternalDependencies(Map(DependencyByMemberRef -> mri.asInstanceOf[Relation[File, File]], DependencyByInheritance -> ii.asInstanceOf[Relation[File, File]]))
val external = ExternalDependencies(Map(DependencyByMemberRef -> mre.asInstanceOf[Relation[File, String]], DependencyByInheritance -> ie.asInstanceOf[Relation[File, String]]))
Relations.make(srcProd, binaryDep, internal, external, classes, names)
} else {
assert(names.all.isEmpty, s"When `nameHashing` is disabled `names` relation should be empty: $names")
Relations.make(srcProd, binaryDep, directSrcDeps, publicInheritedSrcDeps, classes)
}
@ -239,7 +283,9 @@ object Relations {
/** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/
final class Source private[sbt] (val internal: Relation[File, File], val external: Relation[File, String]) {
def addInternal(source: File, dependsOn: Iterable[File]): Source = new Source(internal + (source, dependsOn), external)
@deprecated("Use addExternal(File, Iterable[String])", "0.13.8")
def addExternal(source: File, dependsOn: String): Source = new Source(internal, external + (source, dependsOn))
def addExternal(source: File, dependsOn: Iterable[String]): Source = new Source(internal, external + (source, dependsOn))
/** Drops all dependency mappings from `sources`. Acts naively, i.e., doesn't externalize internal deps on removed files.*/
def --(sources: Iterable[File]): Source = new Source(internal -- sources, external -- sources)
def ++(o: Source): Source = new Source(internal ++ o.internal, external ++ o.external)
@ -264,7 +310,9 @@ object Relations {
/** Tracks internal and external source dependencies for a specific dependency type, such as direct or inherited.*/
private[inc] final class SourceDependencies(val internal: Relation[File, File], val external: Relation[File, String]) {
def addInternal(source: File, dependsOn: Iterable[File]): SourceDependencies = new SourceDependencies(internal + (source, dependsOn), external)
@deprecated("Use addExternal(File, Iterable[String])", "0.13.8")
def addExternal(source: File, dependsOn: String): SourceDependencies = new SourceDependencies(internal, external + (source, dependsOn))
def addExternal(source: File, dependsOn: Iterable[String]): SourceDependencies = new SourceDependencies(internal, external + (source, dependsOn))
/** Drops all dependency mappings from `sources`. Acts naively, i.e., doesn't externalize internal deps on removed files.*/
def --(sources: Iterable[File]): SourceDependencies = new SourceDependencies(internal -- sources, external -- sources)
def ++(o: SourceDependencies): SourceDependencies = new SourceDependencies(internal ++ o.internal, external ++ o.external)
@ -288,7 +336,7 @@ object Relations {
def empty: Relations = empty(nameHashing = IncOptions.nameHashingDefault)
private[inc] def empty(nameHashing: Boolean): Relations =
if (nameHashing)
new MRelationsNameHashing(e, e, emptySourceDependencies, emptySourceDependencies, estr, estr)
new MRelationsNameHashing(e, e, InternalDependencies.empty, ExternalDependencies.empty, estr, estr)
else
new MRelationsDefaultImpl(e, e, es, es, estr)
@ -296,14 +344,73 @@ object Relations {
new MRelationsDefaultImpl(srcProd, binaryDep, direct = direct, publicInherited = publicInherited, classes)
private[inc] def make(srcProd: Relation[File, File], binaryDep: Relation[File, File],
memberRef: SourceDependencies, inheritance: SourceDependencies, classes: Relation[File, String],
names: Relation[File, String]): Relations =
new MRelationsNameHashing(srcProd, binaryDep, memberRef = memberRef, inheritance = inheritance,
classes, names)
internalDependencies: InternalDependencies, externalDependencies: ExternalDependencies,
classes: Relation[File, String], names: Relation[File, String]): Relations =
new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies, externalDependencies = externalDependencies, classes, names)
def makeSource(internal: Relation[File, File], external: Relation[File, String]): Source = new Source(internal, external)
private[inc] def makeSourceDependencies(internal: Relation[File, File], external: Relation[File, String]): SourceDependencies = new SourceDependencies(internal, external)
}
private object DependencyCollection {
/**
* Combine `m1` and `m2` such that the result contains all the dependencies they represent.
* `m1` is expected to be smaller than `m2`.
*/
def joinMaps[T](m1: Map[DependencyContext, Relation[File, T]], m2: Map[DependencyContext, Relation[File, T]]) =
m1.foldLeft(m2) { case (tmp, (key, values)) => tmp.updated(key, tmp.getOrElse(key, Relation.empty) ++ values) }
}
private object InternalDependencies {
/**
* Constructs an empty `InteralDependencies`
*/
def empty = InternalDependencies(Map.empty)
}
private case class InternalDependencies(dependencies: Map[DependencyContext, Relation[File, File]]) {
/**
* Adds `dep` to the dependencies
*/
def +(dep: InternalDependency): InternalDependencies =
InternalDependencies(dependencies.updated(dep.context, dependencies.getOrElse(dep.context, Relation.empty) + (dep.sourceFile, dep.targetFile)))
/**
* Adds all `deps` to the dependencies
*/
def ++(deps: Iterable[InternalDependency]): InternalDependencies = deps.foldLeft(this)(_ + _)
def ++(deps: InternalDependencies): InternalDependencies = InternalDependencies(DependencyCollection.joinMaps(dependencies, deps.dependencies))
/**
* Removes all dependencies from `sources` to another file from the dependencies
*/
def --(sources: Iterable[File]): InternalDependencies = InternalDependencies(dependencies.mapValues(_ -- sources).filter(_._2.size > 0))
}
private object ExternalDependencies {
/**
* Constructs an empty `ExternalDependencies`
*/
def empty = ExternalDependencies(Map.empty)
}
private case class ExternalDependencies(dependencies: Map[DependencyContext, Relation[File, String]]) {
/**
* Adds `dep` to the dependencies
*/
def +(dep: ExternalDependency): ExternalDependencies = ExternalDependencies(dependencies.updated(dep.context, dependencies.getOrElse(dep.context, Relation.empty) + (dep.sourceFile, dep.targetClassName)))
/**
* Adds all `deps` to the dependencies
*/
def ++(deps: Iterable[ExternalDependency]): ExternalDependencies = deps.foldLeft(this)(_ + _)
def ++(deps: ExternalDependencies): ExternalDependencies = ExternalDependencies(DependencyCollection.joinMaps(dependencies, deps.dependencies))
/**
* Removes all dependencies from `sources` to another file from the dependencies
*/
def --(sources: Iterable[File]): ExternalDependencies = ExternalDependencies(dependencies.mapValues(_ -- sources).filter(_._2.size > 0))
}
/**
* An abstract class that contains common functionality inherited by two implementations of Relations trait.
*
@ -392,18 +499,46 @@ private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Re
new MRelationsDefaultImpl(srcProd + (src, prod), binaryDep, direct = direct,
publicInherited = publicInherited, classes + (src, name))
def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations = {
val newI = if (inherited) publicInherited.addExternal(src, dependsOn) else publicInherited
val newD = direct.addExternal(src, dependsOn)
def addProducts(src: File, products: Iterable[(File, String)]): Relations =
new MRelationsDefaultImpl(srcProd ++ products.map(p => (src, p._1)), binaryDep, direct = direct,
publicInherited = publicInherited, classes ++ products.map(p => (src, p._2)))
def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]) = {
val depsByInheritance = deps.collect { case InternalDependency(_, targetFile, DependencyByInheritance) => targetFile }
val newD = direct.addInternal(src, deps.map(_.targetFile))
val newI = publicInherited.addInternal(src, depsByInheritance)
new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes)
}
def addInternalSrcDeps(src: File, dependsOn: Iterable[File], inherited: Iterable[File]): Relations =
{
val newI = publicInherited.addInternal(src, inherited)
val newD = direct.addInternal(src, dependsOn)
new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes)
}
def addInternalSrcDeps(src: File, directDependsOn: Iterable[File], inheritedDependsOn: Iterable[File]): Relations = {
val directDeps = directDependsOn.map(d => InternalDependency(src, d, DependencyByMemberRef))
val inheritedDeps = inheritedDependsOn.map(d => InternalDependency(src, d, DependencyByInheritance))
addInternalSrcDeps(src, directDeps ++ inheritedDeps)
}
def addExternalDeps(src: File, deps: Iterable[ExternalDependency]) = {
val depsByInheritance = deps.collect { case ExternalDependency(_, targetClassName, _, DependencyByInheritance) => targetClassName }
val newD = direct.addExternal(src, deps.map(_.targetClassName))
val newI = publicInherited.addExternal(src, depsByInheritance)
new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes)
}
def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations = {
val newI = if (inherited) publicInherited.addExternal(src, dependsOn :: Nil) else publicInherited
val newD = direct.addExternal(src, dependsOn :: Nil)
new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes)
}
def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]) =
new MRelationsDefaultImpl(srcProd, binaryDep + (src, deps.map(_._1)), direct, publicInherited, classes)
def addBinaryDep(src: File, dependsOn: File): Relations =
new MRelationsDefaultImpl(srcProd, binaryDep + (src, dependsOn), direct = direct,
publicInherited = publicInherited, classes)
def names: Relation[File, String] =
throw new UnsupportedOperationException("Tracking of used names is not supported " +
@ -413,9 +548,8 @@ private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Re
throw new UnsupportedOperationException("Tracking of used names is not supported " +
"when `nameHashing` is disabled.")
def addBinaryDep(src: File, dependsOn: File): Relations =
new MRelationsDefaultImpl(srcProd, binaryDep + (src, dependsOn), direct = direct,
publicInherited = publicInherited, classes)
override def externalDependencies: ExternalDependencies = ExternalDependencies(Map(DependencyByMemberRef -> direct.external, DependencyByInheritance -> publicInherited.external))
override def internalDependencies: InternalDependencies = InternalDependencies(Map(DependencyByMemberRef -> direct.internal, DependencyByInheritance -> publicInherited.internal))
def ++(o: Relations): Relations = {
if (nameHashing != o.nameHashing)
@ -498,8 +632,8 @@ private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Re
* needed by the name hashing invalidation algorithm.
*/
private class MRelationsNameHashing(srcProd: Relation[File, File], binaryDep: Relation[File, File],
// memberRef should include everything in inherited
val memberRef: SourceDependencies, val inheritance: SourceDependencies,
val internalDependencies: InternalDependencies,
val externalDependencies: ExternalDependencies,
classes: Relation[File, String],
val names: Relation[File, String]) extends MRelationsCommon(srcProd, binaryDep, classes) {
def direct: Source =
@ -515,42 +649,59 @@ private class MRelationsNameHashing(srcProd: Relation[File, File], binaryDep: Re
def externalDep: Relation[File, String] = memberRef.external
def addProduct(src: File, prod: File, name: String): Relations =
new MRelationsNameHashing(srcProd + (src, prod), binaryDep, memberRef = memberRef,
inheritance = inheritance, classes + (src, name), names = names)
new MRelationsNameHashing(srcProd + (src, prod), binaryDep, internalDependencies = internalDependencies,
externalDependencies = externalDependencies, classes + (src, name), names = names)
def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations = {
val newIH = if (inherited) inheritance.addExternal(src, dependsOn) else inheritance
val newMR = memberRef.addExternal(src, dependsOn)
new MRelationsNameHashing(srcProd, binaryDep, memberRef = newMR, inheritance = newIH, classes,
names = names)
}
def addProducts(src: File, products: Iterable[(File, String)]): Relations =
new MRelationsNameHashing(srcProd ++ products.map(p => (src, p._1)), binaryDep,
internalDependencies = internalDependencies, externalDependencies = externalDependencies,
classes ++ products.map(p => (src, p._2)), names = names)
def addInternalSrcDeps(src: File, deps: Iterable[InternalDependency]) =
new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies ++ deps,
externalDependencies = externalDependencies, classes, names)
def addInternalSrcDeps(src: File, dependsOn: Iterable[File], inherited: Iterable[File]): Relations = {
val newIH = inheritance.addInternal(src, inherited)
val newMR = memberRef.addInternal(src, dependsOn)
new MRelationsNameHashing(srcProd, binaryDep, memberRef = newMR, inheritance = newIH, classes,
names = names)
val memberRefDeps = dependsOn.map(InternalDependency(src, _, DependencyByMemberRef))
val inheritedDeps = inherited.map(InternalDependency(src, _, DependencyByInheritance))
addInternalSrcDeps(src, memberRefDeps ++ inheritedDeps)
}
def addUsedName(src: File, name: String): Relations =
new MRelationsNameHashing(srcProd, binaryDep, memberRef = memberRef,
inheritance = inheritance, classes, names = names + (src, name))
def addExternalDeps(src: File, deps: Iterable[ExternalDependency]) =
new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies,
externalDependencies = externalDependencies ++ deps, classes, names)
def addExternalDep(src: File, dependsOn: String, inherited: Boolean): Relations =
throw new UnsupportedOperationException("This method is not supported when `nameHashing` flag is enabled.")
def addBinaryDeps(src: File, deps: Iterable[(File, String, Stamp)]) =
new MRelationsNameHashing(srcProd, binaryDep + (src, deps.map(_._1)), internalDependencies = internalDependencies,
externalDependencies = externalDependencies, classes, names)
def addBinaryDep(src: File, dependsOn: File): Relations =
new MRelationsNameHashing(srcProd, binaryDep + (src, dependsOn), memberRef = memberRef,
inheritance = inheritance, classes, names = names)
new MRelationsNameHashing(srcProd, binaryDep + (src, dependsOn), internalDependencies = internalDependencies,
externalDependencies = externalDependencies, classes, names = names)
def addUsedName(src: File, name: String): Relations =
new MRelationsNameHashing(srcProd, binaryDep, internalDependencies = internalDependencies,
externalDependencies = externalDependencies, classes, names = names + (src, name))
override def inheritance: SourceDependencies =
new SourceDependencies(internalDependencies.dependencies.getOrElse(DependencyByInheritance, Relation.empty), externalDependencies.dependencies.getOrElse(DependencyByInheritance, Relation.empty))
override def memberRef: SourceDependencies =
new SourceDependencies(internalDependencies.dependencies.getOrElse(DependencyByMemberRef, Relation.empty), externalDependencies.dependencies.getOrElse(DependencyByMemberRef, Relation.empty))
def ++(o: Relations): Relations = {
if (!o.nameHashing)
throw new UnsupportedOperationException("The `++` operation is not supported for relations " +
"with different values of `nameHashing` flag.")
new MRelationsNameHashing(srcProd ++ o.srcProd, binaryDep ++ o.binaryDep,
memberRef = memberRef ++ o.memberRef, inheritance = inheritance ++ o.inheritance,
internalDependencies = internalDependencies ++ o.internalDependencies, externalDependencies = externalDependencies ++ o.externalDependencies,
classes ++ o.classes, names = names ++ o.names)
}
def --(sources: Iterable[File]) =
new MRelationsNameHashing(srcProd -- sources, binaryDep -- sources,
memberRef = memberRef -- sources, inheritance = inheritance -- sources, classes -- sources,
internalDependencies = internalDependencies -- sources, externalDependencies = externalDependencies -- sources, classes -- sources,
names = names -- sources)
def groupBy[K](f: File => K): Map[K, Relations] = {

View File

@ -21,34 +21,35 @@ object AnalysisTest extends Properties("Analysis") {
val sourceInfos = SourceInfos.makeInfo(Nil, Nil)
// a
var a = Analysis.empty(false)
a = a.addProduct(aScala, f("A.class"), exists, "A")
a = a.addProduct(aScala, f("A$.class"), exists, "A$")
a = a.addSource(aScala, aSource, exists, Nil, Nil, sourceInfos)
a = a.addBinaryDep(aScala, f("x.jar"), "x", exists)
a = a.addExternalDep(aScala, "C", cSource, inherited = false)
val aProducts = (f("A.class"), "A", exists) :: (f("A$.class"), "A$", exists) :: Nil
val aInternal = Nil
val aExternal = ExternalDependency(aScala, "C", cSource, DependencyByMemberRef) :: Nil
val aBinary = (f("x.jar"), "x", exists) :: Nil
val a = Analysis.empty(false).addSource(aScala, aSource, exists, sourceInfos, aProducts, aInternal, aExternal, aBinary)
// b
var b = Analysis.empty(false)
b = b.addProduct(bScala, f("B.class"), exists, "B")
b = b.addProduct(bScala, f("B$.class"), exists, "B$")
b = b.addSource(bScala, bSource, exists, Nil, Nil, sourceInfos)
b = b.addBinaryDep(bScala, f("x.jar"), "x", exists)
b = b.addBinaryDep(bScala, f("y.jar"), "y", exists)
b = b.addExternalDep(bScala, "A", aSource, inherited = true)
val bProducts = (f("B.class"), "B", exists) :: (f("B$.class"), "B$", exists) :: Nil
val bInternal = Nil
val bExternal = ExternalDependency(bScala, "A", aSource, DependencyByInheritance) :: Nil
val bBinary = (f("x.jar"), "x", exists) :: (f("y.jar"), "y", exists) :: Nil
val b = Analysis.empty(false).addSource(bScala, bSource, exists, sourceInfos, bProducts, bInternal, bExternal, bBinary)
// ab
var ab = Analysis.empty(false)
ab = ab.addProduct(aScala, f("A.class"), exists, "A")
ab = ab.addProduct(aScala, f("A$.class"), exists, "A$")
ab = ab.addProduct(bScala, f("B.class"), exists, "B")
ab = ab.addProduct(bScala, f("B$.class"), exists, "B$")
ab = ab.addSource(aScala, aSource, exists, Nil, Nil, sourceInfos)
ab = ab.addSource(bScala, bSource, exists, aScala :: Nil, aScala :: Nil, sourceInfos)
ab = ab.addBinaryDep(aScala, f("x.jar"), "x", exists)
ab = ab.addBinaryDep(bScala, f("x.jar"), "x", exists)
ab = ab.addBinaryDep(bScala, f("y.jar"), "y", exists)
ab = ab.addExternalDep(aScala, "C", cSource, inherited = false)
// `b` has an external dependency on `a` that will be internalized
val abAProducts = (f("A.class"), "A", exists) :: (f("A$.class"), "A$", exists) :: Nil
val abAInternal = Nil
val abAExternal = ExternalDependency(aScala, "C", cSource, DependencyByMemberRef) :: Nil
val abABinary = (f("x.jar"), "x", exists) :: Nil
val abBProducts = (f("B.class"), "B", exists) :: (f("B$.class"), "B$", exists) :: Nil
val abBInternal = InternalDependency(bScala, aScala, DependencyByMemberRef) :: InternalDependency(bScala, aScala, DependencyByInheritance) :: Nil
val abBExternal = Nil
val abBBinary = (f("x.jar"), "x", exists) :: (f("y.jar"), "y", exists) :: Nil
val ab = Analysis.empty(false).addSource(aScala, aSource, exists, sourceInfos, abAProducts, abAInternal, abAExternal, abABinary)
.addSource(bScala, bSource, exists, sourceInfos, abBProducts, abBInternal, abBExternal, abBBinary)
val split: Map[String, Analysis] = ab.groupBy({ f: File => f.getName.substring(0, 1) })

View File

@ -175,7 +175,9 @@ object TestCaseGenerators {
inheritance <- genSubRSourceDependencies(memberRef)
classes <- genStringRelation(srcs)
names <- genStringRelation(srcs)
} yield Relations.make(srcProd, binaryDep, memberRef, inheritance, classes, names)
internal <- InternalDependencies(Map(DependencyByMemberRef -> memberRef.internal, DependencyByInheritance -> inheritance.internal))
external <- ExternalDependencies(Map(DependencyByMemberRef -> memberRef.external, DependencyByInheritance -> inheritance.external))
} yield Relations.make(srcProd, binaryDep, internal, external, classes, names)
def genAnalysis(nameHashing: Boolean): Gen[Analysis] = for {
rels <- if (nameHashing) genRelationsNameHashing else genRelations

View File

@ -103,7 +103,9 @@ object AnalysisFormats {
sourceDependencies.internal.all.isEmpty && sourceDependencies.external.all.isEmpty
// we check direct dependencies only because publicInherited dependencies are subset of direct
assert(isEmpty(direct), "Direct dependencies are not empty but `nameHashing` flag is enabled.")
Relations.make(srcProd, binaryDep, memberRef, inheritance, classes, names)
val internalDependencies = InternalDependencies(Map(DependencyByMemberRef -> memberRef.internal, DependencyByInheritance -> inheritance.internal))
val externalDependencies = ExternalDependencies(Map(DependencyByMemberRef -> memberRef.external, DependencyByInheritance -> inheritance.external))
Relations.make(srcProd, binaryDep, internalDependencies, externalDependencies, classes, names)
} else {
def isEmpty(sourceDependencies: SourceDependencies): Boolean =
sourceDependencies.internal.all.isEmpty && sourceDependencies.external.all.isEmpty