Skip to content

Commit

Permalink
Fixes in exportable cache mappers.
Browse files Browse the repository at this point in the history
Add cache verifier. Add classpath mappers. Add mapper for whole MiniSetup after setup os loaded.
Fixes small problems with dependencies phase (e.g. reduce numbers of NoSymbol checked)  and do treat refinement class as top-level class (since it does not have runtime representation.
  • Loading branch information
romanowski committed Feb 8, 2017
1 parent 9c52239 commit 52aa327
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 62 deletions.
4 changes: 2 additions & 2 deletions internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
case _ => newOne()
}
}
private def addClassDependency(deps: HashSet[ClassDependency], fromClass: Symbol, dep: Symbol): Unit = {
private def addClassDependency(deps: HashSet[ClassDependency], fromClass: Symbol, dep: Symbol): Unit = if (dep != NoSymbol) {
assert(fromClass.isClass, Feedback.expectedClassSymbol(fromClass))
val depClass = enclOrModuleClass(dep)
if (fromClass.associatedFile != depClass.associatedFile) {
if (fromClass.associatedFile != depClass.associatedFile && !depClass.isRefinementClass) {
deps += ClassDependency(fromClass, depClass)
()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ trait GlobalHelpers {
case null =>
case tpe =>
val sym = tpe.typeSymbolDirect
if (!sym.hasPackageFlag) op(sym)
if (sym != NoSymbol && !sym.hasPackageFlag) op(sym)
}).traverse(tpe)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ object MiniSetupUtil {
def sameOrder = a.order == b.order
def sameNameHasher = a.nameHashing == b.nameHashing
def sameExtra = equivPairs.equiv(a.extra, b.extra)
// sameOutput &&
// sameOptions &&
sameCompiler &&
sameOutput &&
sameOptions &&
sameCompiler &&
sameOrder && // equivOrder.equiv(a.order, b.order)
sameNameHasher &&
sameExtra
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,39 +57,6 @@ class TestAnalysisCallback(
def hashFile(f: File): Array[Byte] = Stamp.hash(f).asInstanceOf[Hash].value

def get: TestAnalysis = {

val p = (products foldLeft Relation.empty[File, File]) {
case (rel, (source, module)) => rel + (source -> module)
}

val bin = (binaryDependencies foldLeft Relation.empty[String, File]) {
case (rel, (binary, _, sourceClassName, _)) => rel + (sourceClassName -> binary)
}

val di = Relation.empty[File, File]
val de = Relation.empty[File, String]

val pii = Relation.empty[File, File]
val pie = Relation.empty[File, String]

val mri = (classDependencies.filter(_._3 == DependencyByMemberRef) foldLeft Relation.empty[String, String]) {
case (rel, (dependsOnClassName, sourceClassName, _)) => rel + (sourceClassName -> dependsOnClassName)
}
val mre = Relation.empty[File, String]

val ii = (classDependencies.filter(_._3 == DependencyByInheritance) foldLeft Relation.empty[String, String]) {
case (rel, (dependsOnClassName, sourceClassName, _)) => rel + (sourceClassName -> dependsOnClassName)
}
val ie = Relation.empty[File, String]

val cn = Relation.empty[File, String]

val bcn = Relation.empty[String, String] ++ classNames.values.flatten

val un = (usedNames foldLeft Relation.empty[String, String]) {
case (rel, (sourceClassName, names)) => rel ++ (names map (n => (sourceClassName, n)))
}

val relations = Relations.empty

val analyzedApis = classNames.values.flatMap(_.map(_._1)).map(analyzeClass)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ package sbt.internal.inc
import java.io.File
import java.nio.file.Path

import xsbti.compile.{ MiniOptions, MiniSetup }

import scala.util.Try

case class Mapper[V](read: String => V, write: V => String)
Expand Down Expand Up @@ -54,21 +56,46 @@ object Mapper {
)
}

/** Maps Analysis and Minisetup on read/write to store. Used for caching purposes */
trait AnalysisMappers {
val outputDirMapper: Mapper[File] = Mapper.forFile
val sourceDirMapper: Mapper[File] = Mapper.forFile
val scalacOptions: Mapper[String] = Mapper.forString
val javacOptions: Mapper[String] = Mapper.forString

val sourceMapper: Mapper[File] = Mapper.forFile
val productMapper: Mapper[File] = Mapper.forFile
val binaryMapper: Mapper[File] = Mapper.forFile

val binaryStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp
val productStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp
val sourceStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp
val outputDirMapper: Mapper[File]
val sourceDirMapper: Mapper[File]
val scalacOptions: Mapper[String]
val javacOptions: Mapper[String]

val sourceMapper: Mapper[File]
val productMapper: Mapper[File]
val binaryMapper: Mapper[File]

val binaryStampMapper: ContextAwareMapper[File, Stamp]
val productStampMapper: ContextAwareMapper[File, Stamp]
val sourceStampMapper: ContextAwareMapper[File, Stamp]

val classpathMapper: Mapper[File]

/** Function is called on to map MiniSetup that is imported from cache */
def mapOptionsFromCache(fromCache: MiniSetup): MiniSetup
}

trait AnalysisMappersAdapter extends AnalysisMappers {
override val outputDirMapper: Mapper[File] = Mapper.forFile
override val sourceDirMapper: Mapper[File] = Mapper.forFile
override val scalacOptions: Mapper[String] = Mapper.forString
override val javacOptions: Mapper[String] = Mapper.forString

override val sourceMapper: Mapper[File] = Mapper.forFile
override val productMapper: Mapper[File] = Mapper.forFile
override val binaryMapper: Mapper[File] = Mapper.forFile

override val binaryStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp
override val productStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp
override val sourceStampMapper: ContextAwareMapper[File, Stamp] = Mapper.forStamp

override val classpathMapper: Mapper[File] = Mapper.forFile

override def mapOptionsFromCache(fromCache: MiniSetup): MiniSetup = fromCache
}

object AnalysisMappers {
val default = new AnalysisMappers {}
val default: AnalysisMappers = new AnalysisMappersAdapter {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,10 @@ class TextAnalysisFormat(override val mappers: AnalysisMappers) extends FormatCo
case None => throw new ReadException("No output mode specified")
}

new MiniSetup(output, new MiniOptions(classpathHash.toArray, compileOptions.toArray, javacOptions.toArray), compilerVersion,
val original = new MiniSetup(output, new MiniOptions(classpathHash.toArray, compileOptions.toArray, javacOptions.toArray), compilerVersion,
xsbti.compile.CompileOrder.valueOf(compileOrder), nameHashing, skipApiStoring, extra.toArray)

mappers.mapOptionsFromCache(original)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* This software is released under the terms written in LICENSE.
*/

package sbt.internal.inc.cached

import java.io.File

import sbt.internal.inc.{ AnalysisMappers, ContextAwareMapper, Mapper, Stamp }
import xsbti.compile.MiniSetup

trait VerficationResults
object NoVerification extends VerficationResults

/**
* Class used to verify data that will be exported using Exportable cache.
* It is intended to create warnings if cache is not correctly mapped.
*/
abstract class CacheVerifier {

def verifingMappers(from: AnalysisMappers): AnalysisMappers = new AnalysisMappers {
override val outputDirMapper: Mapper[File] = analyzed("outputDirMapper", from.outputDirMapper)
override val sourceDirMapper: Mapper[File] = analyzed("sourceDirMapper", from.sourceDirMapper)
override val scalacOptions: Mapper[String] = analyzed("scalacOptions", from.scalacOptions)
override val javacOptions: Mapper[String] = analyzed("javacOptions", from.javacOptions)
override val sourceMapper: Mapper[File] = analyzed("sourceMapper", from.sourceMapper)
override val productMapper: Mapper[File] = analyzed("productMapper", from.productMapper)
override val binaryMapper: Mapper[File] = analyzed("binaryMapper", from.binaryMapper)
override val binaryStampMapper: ContextAwareMapper[File, Stamp] =
analyzed("binaryStampMapper", from.binaryStampMapper)
override val productStampMapper: ContextAwareMapper[File, Stamp] =
analyzed("productStampMapper", from.productStampMapper)
override val sourceStampMapper: ContextAwareMapper[File, Stamp] =
analyzed("sourceStampMapper", from.sourceStampMapper)
override val classpathMapper: Mapper[File] = analyzed("classpathMapper", from.classpathMapper)

override def mapOptionsFromCache(fromCache: MiniSetup): MiniSetup = fromCache
}

def results: VerficationResults

protected def analyzeValue(category: String, serializedValue: String, deserializedValue: Any): Unit

private def analyzed[T](category: String, original: Mapper[T]): Mapper[T] = {
def write(v: T): String = {
val result = original.write(v)
analyzeValue(category, result, v)
result
}
Mapper(original.read, write)
}

private def analyzed[C, T](category: String, original: ContextAwareMapper[C, T]): ContextAwareMapper[C, T] = {
def write(c: C, v: T): String = {
val result = original.write(c, v)
analyzeValue(category, result, v)
result
}

ContextAwareMapper(original.read, write)
}
}

object NoopVerifier extends CacheVerifier {
override protected def analyzeValue(category: String, serializedValue: String, deserializedValue: Any): Unit = ()

override def verifingMappers(from: AnalysisMappers): AnalysisMappers = from
override def results: VerficationResults = NoVerification
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ case class ProjectRebasedCache(remoteRoot: Path, cacheLocation: Path) extends Co
}
}

private def createMapper(from: Path, to: Path): AnalysisMappers = new AnalysisMappers {
private def createMapper(from: Path, to: Path): AnalysisMappers = new AnalysisMappersAdapter {
private def justRebase = Mapper.rebaseFile(from, to)

override val outputDirMapper: Mapper[File] = justRebase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import sbt.internal.inc._
import sbt.io.{ PathFinder, IO }
import xsbti.compile.{ CompileAnalysis, MiniSetup, SingleOutput }

class ExportableCacheMapper(root: Path) extends AnalysisMappers {
class ExportableCacheMapper(root: Path) extends AnalysisMappersAdapter {
private def justRelativize = Mapper.relativizeFile(root)

override val outputDirMapper: Mapper[File] = justRelativize
override val sourceDirMapper: Mapper[File] = justRelativize
override val sourceMapper: Mapper[File] = justRelativize
override val productMapper: Mapper[File] = justRelativize
override val binaryMapper: Mapper[File] = justRelativize
override val classpathMapper: Mapper[File] = justRelativize

override val binaryStampMapper: ContextAwareMapper[File, Stamp] =
Mapper.updateModificationDateFileMapper(binaryMapper)
Expand All @@ -32,6 +33,8 @@ class ExportableCache(val cacheLocation: Path, cleanOutputMode: CleanOutputMode

protected def createMapper(projectLocation: File) = new ExportableCacheMapper(projectLocation.toPath)

protected def cacheVerifier(): CacheVerifier = NoopVerifier

private def outputDir(setup: MiniSetup): File = setup.output() match {
case single: SingleOutput =>
single.outputDirectory()
Expand Down Expand Up @@ -84,9 +87,11 @@ class ExportableCache(val cacheLocation: Path, cleanOutputMode: CleanOutputMode
analysis.copy(stamps = newStamps)
}

def exportCache(projectLocation: File, currentAnalysisStore: AnalysisStore): Unit = {
for ((currentAnalysis: Analysis, currentSetup) <- currentAnalysisStore.get()) {
val remoteStore = FileBasedStore(analysisFile.toFile, createMapper(projectLocation))
def exportCache(projectLocation: File, currentAnalysisStore: AnalysisStore): Option[VerficationResults] = {
for ((currentAnalysis: Analysis, currentSetup) <- currentAnalysisStore.get()) yield {
val verifier = cacheVerifier()
val mapper = verifier.verifingMappers(createMapper(projectLocation))
val remoteStore = FileBasedStore(analysisFile.toFile, mapper)
val out = outputDir(currentSetup).toPath

def files(f: File): List[File] = f :: (if (f.isDirectory) IO.listFiles(f).toList.flatMap(files(_)) else Nil)
Expand All @@ -100,6 +105,7 @@ class ExportableCache(val cacheLocation: Path, cleanOutputMode: CleanOutputMode
IO.zip(entries, classesZipFile.toFile)

remoteStore.set(currentAnalysis, currentSetup)
verifier.results
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import sbt.internal.inc._
import scala.util.{ Random, Try }

object MappedTextAnalysisFormatTest extends Properties("MappedTextAnalysisFormat") with BaseTextAnalysisFormatTest {
object TestMapper extends AnalysisMappers {
object TestMapper extends AnalysisMappersAdapter {
override val sourceMapper: Mapper[File] = mapped(Mapper.forFile)
override val productMapper: Mapper[File] = mapped(Mapper.forFile)
override val binaryMapper: Mapper[File] = mapped(Mapper.forFile)
Expand Down
2 changes: 1 addition & 1 deletion zinc/src/test/scala/sbt/inc/BaseCompilerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class BaseCompilerSpec extends BridgeProviderSpecification {
private val sourcesPrefix = Paths.get("sources")
private val binPrefix = Paths.get("bin")

val allSources = for {
val allSources: Iterable[File] = for {
(sourcePath, sourceFiles) <- sources
sourceRoot = baseLocation.resolve(sourcePath)
sourceFile <- sourceFiles
Expand Down
36 changes: 34 additions & 2 deletions zinc/src/test/scala/sbt/inc/cached/ExportedCacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,49 @@ import java.nio.file.Path
import sbt.internal.inc.cached._
import sbt.io.IO
import xsbti.compile.{ MiniSetup, CompileAnalysis }
import org.scalatest._

class ExportedCacheSpec extends CommonCachedCompilation("Exported Cache") {

var cacheLocation: Path = _

class TestVerifierResults extends VerficationResults {
val categories = Set.newBuilder[String]
val values = Set.newBuilder[String]
}

class TestVerifier extends CacheVerifier {
val currentResults = new TestVerifierResults

override protected def analyzeValue(category: String, serializedValue: String, deserializedValue: Any): Unit = {
currentResults.categories += category
currentResults.values += deserializedValue.toString
()
}

override def results: VerficationResults = currentResults
}

override protected def beforeAll(): Unit = {
super.beforeAll()
cacheLocation = remoteProject.baseLocation.resolveSibling("cache")
val remoteCache = new ExportableCache(cacheLocation)
val remoteCache = new ExportableCache(cacheLocation) {
override protected def cacheVerifier(): CacheVerifier = new TestVerifier
}

remoteCache.exportCache(remoteProject.baseLocation.toFile, remoteAnalysisStore)
remoteCache.exportCache(remoteProject.baseLocation.toFile, remoteAnalysisStore) match {
case Some(results: TestVerifierResults) =>
results.categories.result() should not be empty
val values = results.values.result()
values should not be empty
remoteProject.allSources.map(_.toString).foreach {
source => values should contain(source)
}
case Some(other) =>
fail(s"Bad verification results: $other (of class ${other.getClass.getName}")
case _ =>
fail(s"No cache verification results.")
}
}

override def remoteCacheProvider(): CacheProvider = new CacheProvider {
Expand Down

0 comments on commit 52aa327

Please sign in to comment.