diff --git a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala b/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala index 2ff410e4d..b9f62b912 100644 --- a/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala +++ b/compile/integration/src/main/scala/sbt/compiler/AggressiveCompile.scala @@ -180,7 +180,8 @@ class AggressiveCompile(cacheFile: File) private[this] def explicitBootClasspath(options: Seq[String]): Seq[File] = options.dropWhile(_ != CompilerArguments.BootClasspathOption).drop(1).take(1).headOption.toList.flatMap(IO.parseClasspath) - import AnalysisFormats._ + val formats = new sbt.inc.InternedAnalysisFormats() + import formats._ val store = AggressiveCompile.staticCache(cacheFile, AnalysisStore.sync(AnalysisStore.cached(FileBasedStore(cacheFile)))) } object AggressiveCompile diff --git a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala b/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala index daad493f1..fddb04f09 100644 --- a/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala +++ b/compile/integration/src/main/scala/sbt/compiler/IncrementalCompiler.scala @@ -42,7 +42,8 @@ object IC extends IncrementalCompiler[Analysis, AnalyzingCompiler] def readCacheUncaught(file: File): (Analysis, CompileSetup) = { import sbinary.DefaultProtocol.{immutableMapFormat, immutableSetFormat, StringFormat, tuple2Format} - import sbt.inc.AnalysisFormats._ + val formats = new sbt.inc.InternedAnalysisFormats() + import formats._ sbt.IO.gzipFileIn(file)( in => sbinary.Operations.read[(Analysis, CompileSetup)](in) ) } } diff --git a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala b/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala index cf6e05bd9..dc452565a 100644 --- a/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala +++ b/compile/persist/src/main/scala/sbt/inc/AnalysisFormats.scala @@ -15,6 +15,7 @@ package inc import Logger.{m2o, position, problem} import Relations.{Source => RSource} +@deprecated("Replaced by InternedAnalysisFormats. OK to remove in 0.14.", since="0.13.1") object AnalysisFormats { type RFF = Relation[File, File] diff --git a/compile/persist/src/main/scala/sbt/inc/InternedAnalysisFormats.scala b/compile/persist/src/main/scala/sbt/inc/InternedAnalysisFormats.scala new file mode 100644 index 000000000..c9c299722 --- /dev/null +++ b/compile/persist/src/main/scala/sbt/inc/InternedAnalysisFormats.scala @@ -0,0 +1,98 @@ +/* sbt -- Simple Build Tool + * Copyright 2010 Mark Harrah + */ +package sbt +package inc + +import java.io.File +import sbinary._ +import DefaultProtocol.{FileFormat => _, _} +import xsbti.api._ +import xsbti.compile.{Output => APIOutput, SingleOutput, MultipleOutput} +import MultipleOutput.OutputGroup + + +/** + * sbinary serialization format for Analysis objects. + * + * Uses an intern pool for entities that are repeated many times in relations: File and Compilation instances. + * + * Has state, so cannot be an object. To use, wild-card import the implicits from an instance of this class. + */ +private[sbt] final class InternedAnalysisFormats +{ + // Formats that reads/write internable objects directly (i.e., not via the intern pool). + // Note: Not implicit. We only want to use the full format explicitly, in specific places (such as when + // serializing the pool itself). + val fullFileFormat: Format[File] = AnalysisFormats.fileFormat + + // Format that reads/writes files via the intern pool. + val filePool = new InternPoolFormats[File](fullFileFormat) + implicit val fileFormat = filePool.itemFormat + + // A convenient wrapper class for pool serialization. We can add more pools here in the future if needed. + final class Pools(var fileP: InternPool[File]) + + def initPools(analysis: Analysis) = new Pools( + filePool.initPool(allFiles(analysis)) + ) + + val poolsFormat: Format[Pools] = wrap[Pools, InternPool[File]](_.fileP, new Pools(_))(filePool.poolFormat) + + // Get all files mentioned in an Analysis. + // We take the union of all possible file sets, even though in theory some of these should be contained in others. + // This is safer in cases where the Analysis has discrepancies, for whatever reason. + def allFiles(analysis: Analysis): Array[File] = { + def all(rel: Relation[File, File]) = rel._1s ++ rel._2s + (analysis.stamps.allInternalSources ++ analysis.stamps.allBinaries ++ analysis.stamps.allProducts ++ + analysis.stamps.classNames.keySet ++ analysis.apis.allInternalSources ++ + all(analysis.relations.srcProd) ++ all(analysis.relations.binaryDep) ++ all(analysis.relations.internalSrcDep) ++ + analysis.relations.externalDep._1s ++ analysis.relations.classes._1s ++ + analysis.infos.allInfos.keySet).toArray + } + + // Format that tacks a serialized version of the File and Compilation intern pools in front of the serialized Analysis. + def tupleToAnalysis(pools: Pools, stamps: Stamps, apis: APIs, relations: Relations, infos: SourceInfos, compilations: Compilations) = { + Analysis.Empty.copy(stamps, apis, relations, infos, compilations) // Ignore the pools. Their work is done. + } + def analysisToTuple(a: Analysis) = (initPools(a), a.stamps, a.apis, a.relations, a.infos, a.compilations) + implicit def analysisFormat(implicit stampsF: Format[Stamps], apisF: Format[APIs], relationsF: Format[Relations], + infosF: Format[SourceInfos], compilationsF: Format[Compilations]): Format[Analysis] = + asProduct6(tupleToAnalysis)(analysisToTuple)(poolsFormat, stampsF, apisF, relationsF, infosF, compilationsF) + + // Formats in which we use the fullFileFormat. We don't want to use the pool when serializing CompileSetup. + implicit val outputGroupFormat: Format[OutputGroup] = + asProduct2((a: File,b: File) => new OutputGroup{ def sourceDirectory = a; def outputDirectory = b })({ out => (out.sourceDirectory, out.outputDirectory) })(fullFileFormat, fullFileFormat) + + val multipleOutputFormat: Format[MultipleOutput] = + wrap[MultipleOutput, Array[OutputGroup]](_.outputGroups, { groups => new MultipleOutput { def outputGroups = groups } }) + + val singleOutputFormat: Format[SingleOutput] = + wrap[SingleOutput, File](_.outputDirectory,{ out => new SingleOutput{def outputDirectory = out } })(fullFileFormat) + + implicit val outputFormat: Format[APIOutput] = asUnion(singleOutputFormat, multipleOutputFormat) + + // We use these implementations unchanged. + // TODO: Refactor out so we can import them with a wildcard? + implicit def relationFormat[A,B](implicit af: Format[Map[A, Set[B]]], bf: Format[Map[B, Set[A]]]) = AnalysisFormats.relationFormat(af, bf) + implicit def seqFormat[T](implicit optionFormat: Format[T]): Format[Seq[T]] = AnalysisFormats.seqFormat(optionFormat) + implicit val integerFormat = AnalysisFormats.integerFormat + implicit val severityFormat = AnalysisFormats.severityFormat + implicit val orderFormat = AnalysisFormats.orderFormat + implicit val existsFormat = AnalysisFormats.existsFormat + implicit val infoFormat = AnalysisFormats.infoFormat + implicit val infosFormat = AnalysisFormats.infosFormat + implicit val problemFormat = AnalysisFormats.problemFormat + implicit val positionFormat = AnalysisFormats.positionFormat + implicit val sourceFormat = AnalysisFormats.sourceFormat + implicit val apisFormat = AnalysisFormats.apisFormat + implicit val relationsSourceFormat = AnalysisFormats.relationsSourceFormat + implicit val relationsFormat = AnalysisFormats.relationsFormat + implicit val optsFormat = AnalysisFormats.optsFormat + implicit val hashStampFormat = AnalysisFormats.hashStampFormat + implicit val lastModFormat = AnalysisFormats.lastModFormat + implicit val stampFormat = AnalysisFormats.stampFormat + implicit val stampsFormat = AnalysisFormats.stampsFormat + implicit val setupFormat = AnalysisFormats.setupFormat + implicit val compilationsFormat = AnalysisFormats.compilationsFormat +} diff --git a/main/actions/src/main/scala/sbt/Sync.scala b/main/actions/src/main/scala/sbt/Sync.scala index 6c64d9b66..2851e2414 100644 --- a/main/actions/src/main/scala/sbt/Sync.scala +++ b/main/actions/src/main/scala/sbt/Sync.scala @@ -71,7 +71,8 @@ object Sync import sbinary._ import Operations.{read, write} import DefaultProtocol.{FileFormat => _, _} - import inc.AnalysisFormats._ + val formats = new sbt.inc.InternedAnalysisFormats() + import formats._ def writeInfo[F <: FileInfo](file: File, relation: Relation[File, File], info: Map[File, F])(implicit infoFormat: Format[F]): Unit = IO.gzipFileOut(file) { out =>