sbt.internal.inc.Analysis.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zinc-core_2.13 Show documentation
Show all versions of zinc-core_2.13 Show documentation
Incremental compiler of Scala
The newest version!
/*
* Zinc - The incremental compiler for Scala.
* Copyright Scala Center, Lightbend, and Mark Harrah
*
* Licensed under Apache License 2.0
* SPDX-License-Identifier: Apache-2.0
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package sbt
package internal
package inc
import sbt.internal.inc.Analysis.{ LocalProduct, NonLocalProduct }
import java.nio.file.{ Path, Paths }
import xsbti.VirtualFileRef
import xsbti.api.{ AnalyzedClass, ExternalDependency, InternalDependency }
import xsbti.compile.CompileAnalysis
import xsbti.compile.analysis.{
ReadCompilations,
ReadSourceInfos,
ReadStamps,
SourceInfo,
Stamp => XStamp
}
trait Analysis extends CompileAnalysis {
val stamps: Stamps
val apis: APIs
/** Mappings between sources, classes, and binaries. */
val relations: Relations
val infos: SourceInfos
override def readStamps: ReadStamps = stamps
override def readSourceInfos: ReadSourceInfos = infos
override def readCompilations: ReadCompilations = compilations
/**
* Information about compiler runs accumulated since `clean` command has been run.
*
* 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.
*
* The incremental compilation algorithm doesn't use information stored in
* `compilations`. It's safe to prune contents of that field without breaking
* internal consistency of the entire Analysis object.
*/
val compilations: Compilations
/** Concatenates Analysis objects naively, i.e., doesn't internalize external deps on added files. See `Analysis.merge`. */
def ++(other: Analysis): Analysis
/** Drops all analysis information for `sources` naively, i.e., doesn't externalize internal deps on removed files. */
def --(sources: Iterable[VirtualFileRef]): Analysis
def copy(
stamps: Stamps = stamps,
apis: APIs = apis,
relations: Relations = relations,
infos: SourceInfos = infos,
compilations: Compilations = compilations
): Analysis
def addSource(
src: VirtualFileRef,
apis: Iterable[AnalyzedClass],
stamp: XStamp,
info: SourceInfo,
nonLocalProducts: Iterable[NonLocalProduct],
localProducts: Iterable[LocalProduct],
internalDeps: Iterable[InternalDependency],
externalDeps: Iterable[ExternalDependency],
libraryDeps: Iterable[(VirtualFileRef, String, XStamp)]
): Analysis
override lazy val toString = Analysis.summary(this)
}
object Analysis {
case class NonLocalProduct(
className: String,
binaryClassName: String,
classFile: VirtualFileRef,
classFileStamp: XStamp
)
case class LocalProduct(classFile: VirtualFileRef, classFileStamp: XStamp)
lazy val Empty: Analysis =
new MAnalysis(Stamps.empty, APIs.empty, Relations.empty, SourceInfos.empty, Compilations.empty)
def empty: Analysis = Empty
case class Sources(java: Set[String], scala: Set[String])
def sources(a: Analysis): Sources = {
def sourceFileForClass(className: String): VirtualFileRef =
a.relations.definesClass(className).headOption.getOrElse {
sys.error(s"Can't find source file for $className")
}
def isJavaClass(className: String) = sourceFileForClass(className).id.endsWith(".java")
val (j, s) = a.apis.allInternalClasses.partition(isJavaClass)
Sources(j.toSet, s.toSet)
}
def summary(a: Analysis): String = {
val Sources(j, s) = sources(a)
val c = a.stamps.allProducts
val ext = a.apis.allExternals
val jars = a.relations.allLibraryDeps.filter(_.id.endsWith(".jar"))
val unreportedCount = a.infos.allInfos.values.map(_.getUnreportedProblems.length).sum
val sections =
counted("Scala source", "", "s", s.size) ++
counted("Java source", "", "s", j.size) ++
counted("class", "", "es", c.size) ++
counted("external source dependenc", "y", "ies", ext.size) ++
counted("binary dependenc", "y", "ies", jars.size) ++
counted("unreported warning", "", "s", unreportedCount)
sections.mkString("Analysis: ", ", ", "")
}
def counted(prefix: String, single: String, plural: String, count: Int): Option[String] =
count match {
case 0 => None
case 1 => Some(s"1 $prefix$single")
case x => Some(s"$x $prefix$plural")
}
lazy val dummyOutputPath: Path = Paths.get("/tmp/dummy")
lazy val dummyOutputJarPath: Path = Paths.get("/tmp/dummy/output.jar")
}
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,
compilations ++ o.compilations
)
def --(sources: Iterable[VirtualFileRef]): Analysis = {
val newRelations = relations -- sources
def keep[T](f: (Relations, T) => Set[?]): T => Boolean = f(newRelations, _).nonEmpty
val classesInSrcs = sources.flatMap(relations.classNames)
val newAPIs = apis.removeInternal(classesInSrcs).filterExt(keep(_.usesExternal(_)))
val newStamps = stamps.filter(keep(_.produced(_)), sources, keep(_.usesLibrary(_)))
val newInfos = infos -- sources
new MAnalysis(newStamps, newAPIs, newRelations, newInfos, compilations)
}
def copy(
stamps: Stamps,
apis: APIs,
relations: Relations,
infos: SourceInfos,
compilations: Compilations = compilations
): Analysis =
new MAnalysis(stamps, apis, relations, infos, compilations)
def addSource(
src: VirtualFileRef,
apis: Iterable[AnalyzedClass],
stamp: XStamp,
info: SourceInfo,
nonLocalProducts: Iterable[NonLocalProduct],
localProducts: Iterable[LocalProduct],
internalDeps: Iterable[InternalDependency],
externalDeps: Iterable[ExternalDependency],
libraryDeps: Iterable[(VirtualFileRef, String, XStamp)]
): Analysis = {
val newStamps = {
val stamps0 = stamps.markSource(src, stamp)
val stamps1 = nonLocalProducts.foldLeft(stamps0) { (acc, nonLocalProduct) =>
acc.markProduct(nonLocalProduct.classFile, nonLocalProduct.classFileStamp)
}
val stamps2 = localProducts.foldLeft(stamps1) { (acc, localProduct) =>
acc.markProduct(localProduct.classFile, localProduct.classFileStamp)
}
libraryDeps.foldLeft(stamps2) {
case (acc, (toBinary, className, binStamp)) =>
acc.markLibrary(toBinary, className, binStamp)
}
}
val newAPIs = {
val apis1 = apis.foldLeft(this.apis) { (acc, analyzedClass) =>
acc.markInternalAPI(analyzedClass.name, analyzedClass)
}
externalDeps.foldLeft(apis1) { (acc, extDep) =>
acc.markExternalAPI(extDep.targetProductClassName, extDep.targetClass)
}
}
val products = nonLocalProducts.map(_.classFile) ++ localProducts.map(_.classFile)
val classes = nonLocalProducts.map(p => p.className -> p.binaryClassName)
val newRelations =
relations.addSource(src, products, classes, internalDeps, externalDeps, libraryDeps)
copy(newStamps, newAPIs, newRelations, infos.add(src, info))
}
override def equals(other: Any) = other match {
// Note: Equality doesn't consider source infos or compilations.
case o: MAnalysis => stamps == o.stamps && apis == o.apis && relations == o.relations
case _ => false
}
override lazy val hashCode = (stamps :: apis :: relations :: Nil).hashCode
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy