mirror of https://github.com/sbt/sbt.git
Merge pull request #1340 from Duhemm/abstract-over-dependency-kind
Abstract over dependency kind in Analysis
This commit is contained in:
commit
b2c2ff698e
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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] = {
|
||||
|
|
|
|||
|
|
@ -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) })
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue