Skip to content

Commit

Permalink
Make OutputJarContent a class to allow parallelism.
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaszwawrzyk committed Oct 8, 2018
1 parent 0130097 commit e9920ce
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 93 deletions.
11 changes: 9 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ lazy val zinc = (project in file("zinc"))
mimaBinaryIssueFilters ++= Seq(
exclude[DirectMissingMethodProblem]("sbt.internal.inc.IncrementalCompilerImpl.compileIncrementally"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.IncrementalCompilerImpl.inputs"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.IncrementalCompilerImpl.compile")
exclude[DirectMissingMethodProblem]("sbt.internal.inc.IncrementalCompilerImpl.compile"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.MixedAnalyzingCompiler.config"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.MixedAnalyzingCompiler.makeConfig"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.MixedAnalyzingCompiler.this"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.CompileConfiguration.this")
)
)

Expand Down Expand Up @@ -287,7 +291,10 @@ lazy val zincCore = (project in internalPath / "zinc-core")
exclude[ReversedMissingMethodProblem]("sbt.internal.inc.IncrementalCommon.invalidateClassesInternally"),
exclude[ReversedMissingMethodProblem]("sbt.internal.inc.IncrementalCommon.invalidateClassesExternally"),
exclude[ReversedMissingMethodProblem]("sbt.internal.inc.IncrementalCommon.findAPIChange"),
exclude[IncompatibleMethTypeProblem]("sbt.internal.inc.Incremental.prune")
exclude[IncompatibleMethTypeProblem]("sbt.internal.inc.Incremental.prune"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.IncrementalCompile.apply"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.AnalysisCallback#Builder.this"),
exclude[DirectMissingMethodProblem]("sbt.internal.inc.AnalysisCallback.this")
)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public static CompileOptions of(java.io.File[] _classpath, java.io.File[] _sourc
private java.util.function.Function<xsbti.Position, xsbti.Position> sourcePositionMapper;
/** Controls the order in which Java and Scala sources are compiled. */
private xsbti.compile.CompileOrder order;
/**
* Points to a temporary classes directory where the compiler can put compilation products
* of any kind. The lifetime of these compilation products is short and the temporary
* classes directory only needs to exist during one incremental compiler cycle.
*/
private java.util.Optional<java.io.File> temporaryClassesDirectory;
protected CompileOptions() {
super();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ object JarUtils {
*/
def readStamps(jar: File): File => Long = {
val stamps = new IndexBasedZipFsOps.CachedStamps(jar)
file => stamps.getStamp(ClassInJar.fromFile(file).toClassFilePath)
file =>
stamps.getStamp(ClassInJar.fromFile(file).toClassFilePath)
}

/**
Expand Down Expand Up @@ -301,10 +302,11 @@ object JarUtils {
}

/**
* This object provides access to current content of output jar.
* The returned `OutputJarContent` object provides access
* to current content of output jar.
* It is prepared to be accessed from zinc's custom compiler
* phases. With some assumptions on how it works the content
* can be read and cached only when necessary.
* can be cached and read only when necessary.
*
* Implementation details:
* The content has to be `reset` before each zinc run. This
Expand All @@ -331,17 +333,34 @@ object JarUtils {
* while pruning between iterations, which is done through
* `removeClasses` method.
*/
object OutputJarContent {
private var content: Option[Content] = None
def createOutputJarContent(output: Output): OutputJarContent = {
getOutputJar(output) match {
case Some(jar) => new ValidOutputJarContent(jar)
case None => NoOutputJar
}
}

sealed abstract class OutputJarContent {
def dependencyPhaseCompleted(): Unit
def scalacRunCompleted(): Unit
def addClasses(classes: Set[ClassFilePath]): Unit
def removeClasses(classes: Set[ClassFilePath]): Unit
def get(): Set[ClassFilePath]
}

private object NoOutputJar extends OutputJarContent {
def dependencyPhaseCompleted(): Unit = ()
def scalacRunCompleted(): Unit = ()
def addClasses(classes: Set[ClassFilePath]): Unit = ()
def removeClasses(classes: Set[ClassFilePath]): Unit = ()
def get(): Set[ClassFilePath] = Set.empty
}

private class ValidOutputJarContent(outputJar: File) extends OutputJarContent {
private var content: Set[ClassFilePath] = Set.empty
private var shouldReadJar: Boolean = false

def reset(output: Output): Unit = {
content = JarUtils.getOutputJar(output).map(new Content(_))
content.foreach { content =>
content.update()
shouldReadJar = false
}
}
update()

def dependencyPhaseCompleted(): Unit = {
shouldReadJar = true
Expand All @@ -352,33 +371,24 @@ object JarUtils {
}

def addClasses(classes: Set[ClassFilePath]): Unit = {
content.foreach(_.add(classes))
content ++= classes
}

def removeClasses(classes: Set[ClassFilePath]): Unit = {
content.foreach(_.remove(classes))
content --= classes
}

def get(): Set[ClassFilePath] = {
content.fold(Set.empty[ClassFilePath]) { content =>
if (shouldReadJar) content.update()
shouldReadJar = false
content.get()
}
if (shouldReadJar) update()
shouldReadJar = false
content
}

private class Content(outputJar: File) {
private var content: Set[ClassFilePath] = Set.empty
def get(): Set[ClassFilePath] = content
def add(classes: Set[ClassFilePath]): Unit = content ++= classes
def remove(classes: Set[ClassFilePath]): Unit = content --= classes
def update(): Unit = {
if (outputJar.exists()) {
content ++= JarUtils.listClassFiles(outputJar).toSet
}
private def update(): Unit = {
if (outputJar.exists()) {
content ++= JarUtils.listClassFiles(outputJar).toSet
}
}

}

/* Methods below are only used for test code. They are not optimized for performance. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ private[sbt] object Analyze {
private def findSource(sourceNameMap: Map[String, Iterable[File]],
pkg: List[String],
sourceFileName: String): List[File] =
refine((sourceNameMap get sourceFileName).toList.flatten.map { x => (x, x.getParentFile)
refine((sourceNameMap get sourceFileName).toList.flatten.map { x =>
(x, x.getParentFile)
}, pkg.reverse)

@tailrec private def refine(sources: List[(File, File)], pkgRev: List[String]): List[File] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,26 @@ object ClassFileManager {

def getDefaultClassFileManager(
classFileManagerType: Optional[ClassFileManagerType],
output: Output
output: Output,
outputJarContent: JarUtils.OutputJarContent
): XClassFileManager = {
if (classFileManagerType.isPresent) {
classFileManagerType.get match {
case _: DeleteImmediatelyManagerType => deleteImmediately(output)
case _: DeleteImmediatelyManagerType => deleteImmediately(output, outputJarContent)
case m: TransactionalManagerType =>
transactional(output, m.backupDirectory, m.logger)
transactional(output, outputJarContent, m.backupDirectory, m.logger)
}
} else deleteImmediately(output)
} else deleteImmediately(output, outputJarContent)
}

def getClassFileManager(options: IncOptions, output: Output): XClassFileManager = {
def getClassFileManager(
options: IncOptions,
output: Output,
outputJarContent: JarUtils.OutputJarContent
): XClassFileManager = {
import sbt.internal.inc.JavaInterfaceUtil.{ EnrichOptional, EnrichOption }
val internal = getDefaultClassFileManager(options.classfileManagerType, output)
val internal =
getDefaultClassFileManager(options.classfileManagerType, output, outputJarContent)
val external = Option(options.externalHooks())
.flatMap(ext => ext.getExternalClassFileManager.toOption)
xsbti.compile.WrappedClassFileManager.of(internal, external.toOptional)
Expand All @@ -78,12 +84,14 @@ object ClassFileManager {
*/
def deleteImmediately: XClassFileManager = new DeleteClassFileManager

def deleteImmediatelyFromJar(outputJar: File): XClassFileManager =
new DeleteClassFileManagerForJar(outputJar)
def deleteImmediatelyFromJar(outputJar: File,
outputJarContent: JarUtils.OutputJarContent): XClassFileManager =
new DeleteClassFileManagerForJar(outputJar, outputJarContent)

def deleteImmediately(output: Output): XClassFileManager = {
def deleteImmediately(output: Output,
outputJarContent: JarUtils.OutputJarContent): XClassFileManager = {
val outputJar = JarUtils.getOutputJar(output)
outputJar.fold(deleteImmediately)(deleteImmediatelyFromJar)
outputJar.fold(deleteImmediately)(deleteImmediatelyFromJar(_, outputJarContent))
}

/**
Expand All @@ -96,13 +104,19 @@ object ClassFileManager {
def transactional(tempDir0: File, logger: sbt.util.Logger): XClassFileManager =
new TransactionalClassFileManager(tempDir0, logger)

def transactionalForJar(outputJar: File): XClassFileManager = {
new TransactionalClassFileManagerForJar(outputJar)
def transactionalForJar(outputJar: File,
outputJarContent: JarUtils.OutputJarContent): XClassFileManager = {
new TransactionalClassFileManagerForJar(outputJar, outputJarContent)
}

def transactional(output: Output, tempDir: File, logger: sbt.util.Logger): XClassFileManager = {
def transactional(
output: Output,
outputJarContent: JarUtils.OutputJarContent,
tempDir: File,
logger: sbt.util.Logger
): XClassFileManager = {
val outputJar = JarUtils.getOutputJar(output)
outputJar.fold(transactional(tempDir, logger))(transactionalForJar)
outputJar.fold(transactional(tempDir, logger))(transactionalForJar(_, outputJarContent))
}

private final class TransactionalClassFileManager(tempDir0: File, logger: sbt.util.Logger)
Expand Down Expand Up @@ -154,10 +168,13 @@ object ClassFileManager {
}
}

private final class DeleteClassFileManagerForJar(outputJar: File) extends XClassFileManager {
private final class DeleteClassFileManagerForJar(
outputJar: File,
outputJarContent: JarUtils.OutputJarContent
) extends XClassFileManager {
override def delete(classes: Array[File]): Unit = {
val relClasses = classes.map(c => JarUtils.ClassInJar.fromFile(c).toClassFilePath)
JarUtils.OutputJarContent.removeClasses(relClasses.toSet)
outputJarContent.removeClasses(relClasses.toSet)
JarUtils.removeFromJar(outputJar, relClasses)
}
override def generated(classes: Array[File]): Unit = ()
Expand All @@ -175,14 +192,16 @@ object ClassFileManager {
* and potential overwrite is also handled by replacing index entry. For this
* reason the old index with offsets to old files will still be valid.
*/
private final class TransactionalClassFileManagerForJar(outputJar: File)
extends XClassFileManager {
private final class TransactionalClassFileManagerForJar(
outputJar: File,
outputJarContent: JarUtils.OutputJarContent
) extends XClassFileManager {
private val backedUpIndex = Some(outputJar).filter(_.exists()).map(JarUtils.stashIndex)

override def delete(classesInJar: Array[File]): Unit = {
val classes = classesInJar.map(c => JarUtils.ClassInJar.fromFile(c).toClassFilePath)
JarUtils.removeFromJar(outputJar, classes)
JarUtils.OutputJarContent.removeClasses(classes.toSet)
outputJarContent.removeClasses(classes.toSet)
}

override def generated(classes: Array[File]): Unit = ()
Expand Down
26 changes: 16 additions & 10 deletions internal/zinc-core/src/main/scala/sbt/internal/inc/Compile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ object IncrementalCompile {
previous0: CompileAnalysis,
output: Output,
log: Logger,
options: IncOptions): (Boolean, Analysis) = {
options: IncOptions,
outputJarContent: JarUtils.OutputJarContent
): (Boolean, Analysis) = {
val previous = previous0 match { case a: Analysis => a }
val current =
Stamps.initial(Stamper.forLastModified, Stamper.forHash, Stamper.forLastModified)
Expand All @@ -65,7 +67,6 @@ object IncrementalCompile {
val internalSourceToClassNamesMap: File => Set[String] = (f: File) =>
previous.relations.classNames(f)
val externalAPI = getExternalAPI(lookup)
JarUtils.OutputJarContent.reset(output)
try {
Incremental.compile(
sources,
Expand All @@ -78,10 +79,12 @@ object IncrementalCompile {
externalAPI,
current,
output,
options),
options,
outputJarContent),
log,
options,
output
output,
outputJarContent
)
} catch {
case _: xsbti.CompileCancelled =>
Expand Down Expand Up @@ -110,7 +113,8 @@ private object AnalysisCallback {
externalAPI: (File, String) => Option[AnalyzedClass],
current: ReadStamps,
output: Output,
options: IncOptions
options: IncOptions,
outputJarContent: JarUtils.OutputJarContent
) {
def build(): AnalysisCallback = {
new AnalysisCallback(
Expand All @@ -119,7 +123,8 @@ private object AnalysisCallback {
externalAPI,
current,
output,
options
options,
outputJarContent
)
}
}
Expand All @@ -131,7 +136,8 @@ private final class AnalysisCallback(
externalAPI: (File, String) => Option[AnalyzedClass],
stampReader: ReadStamps,
output: Output,
options: IncOptions
options: IncOptions,
outputJarContent: JarUtils.OutputJarContent
) extends xsbti.AnalysisCallback {

private[this] val compilation: Compilation = Compilation(output)
Expand Down Expand Up @@ -307,7 +313,7 @@ private final class AnalysisCallback(
override def enabled(): Boolean = options.enabled

def get: Analysis = {
JarUtils.OutputJarContent.scalacRunCompleted()
outputJarContent.scalacRunCompleted()
addUsedNames(addCompilation(addProductsAndDeps(Analysis.empty)))
}

Expand Down Expand Up @@ -413,13 +419,13 @@ private final class AnalysisCallback(
}

override def dependencyPhaseCompleted(): Unit = {
JarUtils.OutputJarContent.dependencyPhaseCompleted()
outputJarContent.dependencyPhaseCompleted()
}

override def apiPhaseCompleted(): Unit = {}

override def classesInOutputJar(): java.util.Set[String] = {
JarUtils.OutputJarContent.get().asJava
outputJarContent.get().asJava
}

}
Loading

0 comments on commit e9920ce

Please # to comment.