mirror of https://github.com/sbt/sbt.git
Sketch of abstracting over dependency details in incremental compiler
This represents a sketch of the idea that we can abstract over details
of a specific dependency kind. The goal would that only a few
implementations of methods in incremental would be sensitive to specific
dependency kind:
1. Dependency extraction logic
2. Implementation of Relations which adds dependencies to specific
relations (or abstract over specific relations)
3. Invalidation algorithm (Incremental.scala) which has different
of each kind of dependency
4. TextAnalysisFormat
In particular, adding a new dependency kind would not affect signatures
of existing methods.
What needs to be done:
- finish refactoring so the code compiles again and previous semantics
are preserved
- introduce deprecated overloads that preserve old method signatures
(this is required for preserving binary compatibility)
This commit is contained in:
parent
7885fd2558
commit
f7c00ab581
|
|
@ -51,9 +51,9 @@ 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, directInternal: Iterable[File], inheritedInternal: Iterable[File], info: SourceInfo): Analysis
|
||||
def addSource(src: File, api: Source, stamp: Stamp, dependencies: Iterable[Dependency], info: SourceInfo): Analysis
|
||||
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis
|
||||
def addExternalDep(src: File, dep: String, api: Source, inherited: Boolean): Analysis
|
||||
def addExternalDep(dep: Dependency): Analysis
|
||||
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,14 +155,24 @@ 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, dependencies: Iterable[Dependency], info: SourceInfo): Analysis = {
|
||||
copy(stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, dependencies), infos.add(src, info))
|
||||
}
|
||||
|
||||
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis =
|
||||
copy(stamps.markBinary(dep, className, stamp), apis, relations.addBinaryDep(src, dep), 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(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 addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis =
|
||||
copy(stamps.markProduct(product, stamp), apis, relations.addProduct(src, product, name), infos)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
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
|
||||
*
|
||||
* 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
|
||||
|
|
@ -74,11 +74,12 @@ trait Relations {
|
|||
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, directDependsOn: Iterable[File], inheritedDependsOn: Iterable[File]): Relations
|
||||
def addInternalSrcDeps(src: File, dependencies: Iterable[Dependency]): Relations
|
||||
|
||||
private[inc] def addUsedName(src: File, name: String): Relations
|
||||
|
||||
|
|
@ -398,12 +399,21 @@ private class MRelationsDefaultImpl(srcProd: Relation[File, File], binaryDep: Re
|
|||
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, 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)
|
||||
new MRelationsDefaultImpl(srcProd, binaryDep, direct = newD, publicInherited = newI, classes)
|
||||
}
|
||||
|
||||
def names: Relation[File, String] =
|
||||
throw new UnsupportedOperationException("Tracking of used names is not supported " +
|
||||
|
|
|
|||
Loading…
Reference in New Issue