diff --git a/compile/inc/src/main/scala/sbt/inc/Analysis.scala b/compile/inc/src/main/scala/sbt/inc/Analysis.scala index eaf4cd0b3..90e8c73fa 100644 --- a/compile/inc/src/main/scala/sbt/inc/Analysis.scala +++ b/compile/inc/src/main/scala/sbt/inc/Analysis.scala @@ -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, dependencies: Iterable[Dependency], info: SourceInfo): 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 - def addExternalDep(dep: Dependency): 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,27 +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, dependencies: Iterable[Dependency], info: SourceInfo): Analysis = { - copy(stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, dependencies), 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(dep: Dependency): Analysis = { - // TODO: there's no static enforcment that Dependency passed as parameter has ExternalDependencyEdge - // as its edge. We risk MathError below; We should have just one method for adding dependencies - // where all cases are handled - val ExternalDepndencyEdge(fromSrc, toClassName, classApi) = dep.edge - val inherited = dep.context match { - case DependencyByInheritance => true - case _ => false - } - copy(stamps, apis.markExternalAPI(toClassName, classApi), relations.addExternalDep(fromSrc, toClassName, 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) diff --git a/compile/inc/src/main/scala/sbt/inc/Dependency.scala b/compile/inc/src/main/scala/sbt/inc/Dependency.scala index a595d246c..a10d33d00 100644 --- a/compile/inc/src/main/scala/sbt/inc/Dependency.scala +++ b/compile/inc/src/main/scala/sbt/inc/Dependency.scala @@ -3,40 +3,12 @@ package sbt.inc import java.io.File import xsbti.api.Source -/** - * Dependency tracked by incremental compiler consists of two parts: - * - `edge` which describes an edge in dependency graph - * - `context` which stores context information from a src file where the dependency got introduced - * - * The context is needed for incremental compiler to decide what kind of invalidation strategy to use. - * It might also store some additional information useful for debugging. - */ -private[inc] final case class Dependency(edge: DependencyEdge, context: DependencyContext) - -private[inc] sealed abstract class DependencyEdge -/** - * External dependency from src file to class name. External means "outside of enclosing Analysis". - * Typically that means a dependency coming from another subproject in sbt. - * - * The classApi contains snapshot of information about the class (that `toClassName` points at) - * as observed from point of view of compilation that produced this dependency edge. - * - * The classApi is stored so we can detect changes to external apis by comparing snapshots - * of the api from two Analysis instances. - */ -private[inc] final case class ExternalDepndencyEdge(fromSrc: File, toClassName: String, classApi: Source) - extends DependencyEdge - -/** - * Represents a dependency edge between two source files in the same sbt subproject. - */ -private[inc] final case class InternalDependencyEdge(fromSrc: File, toSrc: File) extends DependencyEdge - /** * 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 * @@ -46,3 +18,13 @@ private[inc] sealed abstract class DependencyContext */ 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) diff --git a/compile/inc/src/main/scala/sbt/inc/Relations.scala b/compile/inc/src/main/scala/sbt/inc/Relations.scala index 6bb230322..5db719311 100644 --- a/compile/inc/src/main/scala/sbt/inc/Relations.scala +++ b/compile/inc/src/main/scala/sbt/inc/Relations.scala @@ -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,25 +62,59 @@ 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 /** - * TODO: Update comment below. * Records internal source file `src` as having direct dependencies on internal source files `directDependsOn` * and inheritance dependencies on `inheritedDependsOn`. Everything in `inheritedDependsOn` must be included in `directDependsOn`; * this method does not automatically record direct dependencies like `addExternalDep` does. */ - def addInternalSrcDeps(src: File, dependencies: Iterable[Dependency]): Relations + @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 @@ -104,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. * @@ -228,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) } @@ -240,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) @@ -265,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) @@ -289,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) @@ -297,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. * @@ -393,28 +499,47 @@ 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, dependencies: Iterable[Dependency]): Relations = { - val depsByInheritance = dependencies.collect { - case Dependency(InternalDependencyEdge(fromSrc, toSrc), DependencyByInheritance) => - assert(src == fromSrc) - toSrc - } - val depsByMemberRef = dependencies.collect { - case Dependency(InternalDependencyEdge(fromSrc, toSrc), DependencyByMemberRef) => - assert(src == fromSrc) - toSrc - } - val newI = publicInherited.addInternal(src, depsByInheritance) - val newD = direct.addInternal(src, depsByMemberRef) + 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 " + "when `nameHashing` is disabled.") @@ -423,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) @@ -508,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 = @@ -525,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] = { diff --git a/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala b/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala index 2c8d7a505..d0628a7e4 100644 --- a/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala +++ b/compile/inc/src/test/scala/sbt/inc/TestCaseGenerators.scala @@ -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 diff --git a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala b/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala index 53c7d8cdb..1af8278aa 100644 --- a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala +++ b/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala @@ -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