Add `compilations` field to `Analysis`.

We store `Seq[xsbt.api.Compilation]` in `Analysis`. Compilation are
being appended to sequence for every iteration of the incremental
compiler.

Note that `Compilation`s are never removed from the sequence unless
you start from scratch with empty `Analysis`. You can do that by using
sbt's `clean` command.

The main use-case for using `compilations` field is to determine how
many iterations it took to compilen give code. The `Compilation` object
are also stored in `Source` objects so there's an indirect way to recover
information about files being recompiled in every iteration.

Since `Analysis` is persisted you can use this mechanism to track entire
sessions spanning multiple `compile` commands.
This commit is contained in:
Grzegorz Kossakowski 2012-11-06 09:49:01 -08:00 committed by Mark Harrah
parent 532c72e60e
commit 78b0f485fa
5 changed files with 61 additions and 11 deletions

View File

@ -13,10 +13,13 @@ trait Analysis
val apis: APIs
val relations: Relations
val infos: SourceInfos
/** Information about compiler runs accumulated since `clean` command has been run. */
val compilations: Compilations
def ++(other: Analysis): Analysis
def -- (sources: Iterable[File]): Analysis
def copy(stamps: Stamps = stamps, apis: APIs = apis, relations: Relations = relations, infos: SourceInfos = infos): 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, internalDeps: Iterable[File], info: SourceInfo): Analysis
def addBinaryDep(src: File, dep: File, className: String, stamp: Stamp): Analysis
@ -30,7 +33,7 @@ trait Analysis
object Analysis
{
lazy val Empty: Analysis = new MAnalysis(Stamps.empty, APIs.empty, Relations.empty, SourceInfos.empty)
lazy val Empty: Analysis = new MAnalysis(Stamps.empty, APIs.empty, Relations.empty, SourceInfos.empty, Compilations.empty)
def summary(a: Analysis): String =
{
val (j, s) = a.apis.allInternalSources.partition(_.getName.endsWith(".java"))
@ -57,9 +60,11 @@ object Analysis
}
}
private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relations, val infos: SourceInfos) extends Analysis
private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relations, val infos: SourceInfos,
val compilations: Compilations) extends Analysis
{
def ++ (o: Analysis): Analysis = new MAnalysis(stamps ++ o.stamps, apis ++ o.apis, relations ++ o.relations, infos ++ o.infos)
def ++ (o: Analysis): Analysis = new MAnalysis(stamps ++ o.stamps, apis ++ o.apis, relations ++ o.relations,
infos ++ o.infos, compilations ++ o.compilations)
def -- (sources: Iterable[File]): Analysis =
{
val newRelations = relations -- sources
@ -68,9 +73,10 @@ private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relat
val newAPIs = apis.removeInternal(sources).filterExt( keep(_ usesExternal _) )
val newStamps = stamps.filter( keep(_ produced _), sources, keep(_ usesBinary _))
val newInfos = infos -- sources
new MAnalysis(newStamps, newAPIs, newRelations, newInfos)
new MAnalysis(newStamps, newAPIs, newRelations, newInfos, compilations)
}
def copy(stamps: Stamps, apis: APIs, relations: Relations, infos: SourceInfos): Analysis = new MAnalysis(stamps, apis, relations, infos)
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, internalDeps: Iterable[File], info: SourceInfo): Analysis =
copy( stamps.markInternalSource(src, stamp), apis.markInternalSource(src, api), relations.addInternalSrcDeps(src, internalDeps), infos.add(src, info) )
@ -93,7 +99,8 @@ private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relat
stampsMap.getOrElse(k, Stamps.empty),
apisMap.getOrElse(k, APIs.empty),
relationsMap.getOrElse(k, Relations.empty),
infosMap.getOrElse(k, SourceInfos.empty)
infosMap.getOrElse(k, SourceInfos.empty),
compilations
)
val keys = (stampsMap.keySet ++ apisMap.keySet ++ relationsMap.keySet ++ infosMap.keySet).toList
Map( keys map( (k: K) => (k, kAnalysis(k)) ) :_*)

View File

@ -0,0 +1,20 @@
package sbt.inc
import xsbti.api.Compilation
/** Information about compiler runs accumulated since `clean` command has been run. */
trait Compilations {
def allCompilations: Seq[Compilation]
def ++(o: Compilations): Compilations
def add(c: Compilation): Compilations
}
object Compilations {
val empty: Compilations = new MCompilations(Seq.empty)
def make(s: Seq[Compilation]): Compilations = new MCompilations(s)
}
private final class MCompilations(val allCompilations: Seq[Compilation]) extends Compilations {
def ++(o: Compilations): Compilations = new MCompilations(allCompilations ++ o.allCompilations)
def add(c: Compilation): Compilations = new MCompilations(allCompilations :+ c)
}

View File

@ -129,7 +129,7 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
def endSource(sourcePath: File): Unit =
assert(apis.contains(sourcePath))
def get: Analysis = addExternals( addBinaries( addProducts( addSources(Analysis.Empty) ) ) )
def get: Analysis = addCompilation( addExternals( addBinaries( addProducts( addSources(Analysis.Empty) ) ) ) )
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 =
@ -144,6 +144,7 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
}
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)) => a.addExternalDep(source, name, api) }
def addCompilation(base: Analysis): Analysis = base.copy(compilations = base.compilations.add(compilation))
def addAll[A,B](base: Analysis, m: Map[A, Set[B]])( f: (Analysis, A, B) => Analysis): Analysis =
(base /: m) { case (outer, (a, bs)) =>

View File

@ -4,7 +4,7 @@
package sbt
package inc
import xsbti.api.Source
import xsbti.api.{Source, Compilation}
import xsbti.{Position,Problem,Severity}
import xsbti.compile.{CompileOrder, Output => APIOutput, SingleOutput, MultipleOutput}
import MultipleOutput.OutputGroup
@ -45,8 +45,9 @@ object AnalysisFormats
}
}
implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations], infosF: Format[SourceInfos]): Format[Analysis] =
asProduct4( Analysis.Empty.copy _)( a => (a.stamps, a.apis, a.relations, a.infos))(stampsF, apisF, relationsF, infosF)
implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations],
infosF: Format[SourceInfos], compilationsF: Format[Compilations]): Format[Analysis] =
asProduct5( Analysis.Empty.copy _)( a => (a.stamps, a.apis, a.relations, a.infos, a.compilations))(stampsF, apisF, relationsF, infosF, compilationsF)
implicit def infosFormat(implicit infoF: Format[Map[File, SourceInfo]]): Format[SourceInfos] =
wrap[SourceInfos, Map[File, SourceInfo]]( _.allInfos, SourceInfos.make _)
@ -56,6 +57,11 @@ object AnalysisFormats
implicit def problemFormat: Format[Problem] = asProduct4(problem _)( p => (p.category, p.position, p.message, p.severity))
implicit def compilationsFormat: Format[Compilations] = {
implicit val compilationSeqF = seqFormat(xsbt.api.CompilationFormat)
wrap[Compilations, Seq[Compilation]](_.allCompilations, Compilations.make _)
}
implicit def positionFormat: Format[Position] =
asProduct7( position _ )( p => (m2o(p.line), p.lineContent, m2o(p.offset), m2o(p.pointer), m2o(p.pointerSpace), m2o(p.sourcePath), m2o(p.sourceFile)))

View File

@ -0,0 +1,16 @@
package xsbt.api
import xsbti.api._
import sbinary._
object CompilationFormat extends Format[Compilation] {
import java.io._
def reads(in: Input): Compilation = {
val oin = new ObjectInputStream(new InputWrapperStream(in))
try { oin.readObject.asInstanceOf[Compilation] } finally { oin.close() }
}
def writes(out: Output, src: Compilation) {
val oout = new ObjectOutputStream(new OutputWrapperStream(out))
try { oout.writeObject(src) } finally { oout.close() }
}
}