diff --git a/README.md b/README.md index 59b0bc937a..120566971f 100644 --- a/README.md +++ b/README.md @@ -70,3 +70,9 @@ active development. For information on how to contribute, please check the [CONTRIBUTING guide](CONTRIBUTING.md). This software is released under the following [LICENSE](LICENSE). + +## Acknoledgements + +| Logo | Acknoledgement | +| ---- | -------------- | +| ![](https://www.yourkit.com/images/yklogo.png) | We thank [Yourkit](https://www.yourkit.com/) for supporting this open-source project with its full-featured profiler. | diff --git a/bin/bridge213.sh b/bin/bridge213.sh new file mode 100755 index 0000000000..9673d0d521 --- /dev/null +++ b/bin/bridge213.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# This is a hack to validate the compilation of 2.13 compiler bridge without using sbt, +# which was used for bootstrapping the initial compiler bridge. +# In the future when Scala compiler breaks source compatibility, this script might come in handy. + +# $ export SCALA_X_HOME=/usr/local/Cellar/scala@2.13/2.13.0-M2 + +if [[ -z "$SCALA_X_HOME" ]]; then + echo "SCALA_X_HOME is not set!" 1>&2 + echo "Run 'export SCALA_X_HOME=/usr/local/Cellar/scala@2.13/2.13.0-M2' or equivalent." + exit 1 +fi + +mkdir -p target/compiler-bridge/ + +"$SCALA_X_HOME/bin/scalac" \ +-nowarn \ +-classpath $HOME/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-1.0.3.jar:$HOME/.ivy2/cache/org.scala-sbt/util-interface/jars/util-interface-1.0.2.jar \ +-d target/compiler-bridge/ \ +internal/compiler-bridge/src/main/scala/xsbt/API.scala \ +internal/compiler-bridge/src/main/scala/xsbt/DelegatingReporter.scala \ +internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala \ +internal/compiler-bridge/src/main/scala/xsbt/ScaladocInterface.scala \ +internal/compiler-bridge/src/main/scala/xsbt/Analyzer.scala \ +internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala \ +internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleResponse.scala \ +internal/compiler-bridge/src/main/scala/xsbt/CallbackGlobal.scala \ +internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala \ +internal/compiler-bridge/src/main/scala/xsbt/JavaUtils.scala \ +internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala \ +internal/compiler-bridge/src/main/scala/xsbt/ExtractUsedNames.scala \ +internal/compiler-bridge/src/main/scala/xsbt/LocalToNonLocalClass.scala \ +internal/compiler-bridge/src/main/scala/xsbt/Command.scala \ +internal/compiler-bridge/src/main/scala/xsbt/GlobalHelpers.scala \ +internal/compiler-bridge/src/main/scala/xsbt/LocateClassFile.scala \ +internal/compiler-bridge/src/main/scala/xsbt/CompilerInterface.scala \ +internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleFactory.scala \ +internal/compiler-bridge/src/main/scala/xsbt/Log.scala \ +internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala \ +internal/compiler-bridge/src/main/scala/xsbt/Message.scala \ +internal/compiler-bridge/src/main/scala_2.13/xsbt/Compat.scala \ +internal/compiler-bridge/src/main/scala_2.13/xsbt/ConsoleInterface.scala diff --git a/script/deltag.sh b/bin/deltag.sh similarity index 100% rename from script/deltag.sh rename to bin/deltag.sh diff --git a/bin/run-ci.sh b/bin/run-ci.sh index fd0d921ffe..d7611391d6 100755 --- a/bin/run-ci.sh +++ b/bin/run-ci.sh @@ -8,7 +8,8 @@ sbt -Dfile.encoding=UTF-8 \ -J-XX:ReservedCodeCacheSize=256M \ -J-Xmx3046M -J-Xms3046M -J-server \ +mimaReportBinaryIssues \ - scalafmtTest \ + scalafmt::test \ + test:scalafmt::test \ zincRoot/test:compile \ crossTestBridges \ "publishBridgesAndSet $SCALA_VERSION" \ diff --git a/build.sbt b/build.sbt index e643cf314c..1f57e24690 100644 --- a/build.sbt +++ b/build.sbt @@ -1,12 +1,12 @@ import Util._ import Dependencies._ import Scripted._ -//import com.typesafe.tools.mima.core._, ProblemFilters._ def baseVersion = "1.1.0-SNAPSHOT" def internalPath = file("internal") -lazy val compilerBridgeScalaVersions = List(scala212, scala211, scala210) +lazy val compilerBridgeScalaVersions = List(scala212, scala213, scala211, scala210) +lazy val compilerBridgeTestScalaVersions = List(scala212, scala211, scala210) def mimaSettings: Seq[Setting[_]] = Seq( mimaPreviousArtifacts := Set( @@ -157,6 +157,9 @@ lazy val zincRoot: Project = (project in file(".")) homepage := Some(url("https://github.com/sbt/zinc")), developers += Developer("jvican", "Jorge Vicente Cantero", "@jvican", url("https://github.com/jvican")), + scalafmtOnCompile := true, + scalafmtVersion := "1.2.0", + scalafmtOnCompile in Sbt := false, )), minimalSettings, otherRootSettings, @@ -328,10 +331,17 @@ def wrapIn(color: String, content: String): String = { else color + content + scala.Console.RESET } -// Compiler-side interface to compiler that is compiled against the compiler being used either in advance or on the fly. -// Includes API and Analyzer phases that extract source API and relationships. +/** + * Compiler-side interface to compiler that is compiled against the compiler being used either in advance or on the fly. + * Includes API and Analyzer phases that extract source API and relationships. + * As this is essentially implementations of the compiler-interface (per Scala compiler), + * the code here should not be consumed without going through the classloader trick and the interface. + * Due to the hermetic nature of the bridge, there's no necessity to keep binary compatibility across Zinc versions, + * and therefore there's no `mimaSettings` added. + * For the case of Scala 2.13 bridge, we didn't even have the bridge to compare against when Zinc 1.0.0 came out. + */ lazy val compilerBridge: Project = (project in internalPath / "compiler-bridge") - .dependsOn(compilerInterface % "compile;test->test", zincApiInfo % "test->test") + .dependsOn(compilerInterface) .settings( baseSettings, crossScalaVersions := compilerBridgeScalaVersions, @@ -341,17 +351,19 @@ lazy val compilerBridge: Project = (project in internalPath / "compiler-bridge") // precompiledSettings, name := "Compiler Bridge", exportJars := true, - // we need to fork because in unit tests we set usejavacp = true which means - // we are expecting all of our dependencies to be on classpath so Scala compiler - // can use them while constructing its own classpath for compilation - fork in Test := true, - // needed because we fork tests and tests are ran in parallel so we have multiple Scala - // compiler instances that are memory hungry - javaOptions in Test += "-Xmx1G", inBoth(unmanagedSourceDirectories ++= scalaPartialVersion.value.collect { - case (2, y) if y == 10 => new File(scalaSource.value.getPath + "_2.10") - case (2, y) if y >= 11 => new File(scalaSource.value.getPath + "_2.11+") + case (2, y) if y == 10 => new File(scalaSource.value.getPath + "_2.10") + case (2, y) if y == 11 || y == 12 => new File(scalaSource.value.getPath + "_2.11-12") + case (2, y) if y >= 13 => new File(scalaSource.value.getPath + "_2.13") }.toList), + // Use a bootstrap compiler bridge to compile the compiler bridge. + scalaCompilerBridgeSource := { + val old = scalaCompilerBridgeSource.value + scalaVersion.value match { + case x if x startsWith "2.13." => ("org.scala-sbt" % "compiler-bridge_2.13.0-M2" % "1.1.0-M1-bootstrap2" % Compile).sources() + case _ => old + } + }, cleanSbtBridge := { val sbtV = sbtVersion.value val sbtOrg = "org.scala-sbt" @@ -383,7 +395,30 @@ lazy val compilerBridge: Project = (project in internalPath / "compiler-bridge") }, publishLocal := publishLocal.dependsOn(cleanSbtBridge).value, altPublishSettings, - mimaSettings, + ) + +/** + * Tests for the compiler bridge. + * This is split into a separate subproject because testing introduces more dependencies + * (Zinc API Info, which transitively depends on IO). + */ +lazy val compilerBridgeTest = (project in internalPath / "compiler-bridge-test") + .dependsOn(compilerBridge, compilerInterface % "test->test", zincApiInfo % "test->test") + .settings( + name := "Compiler Bridge Test", + baseSettings, + relaxNon212, + // we need to fork because in unit tests we set usejavacp = true which means + // we are expecting all of our dependencies to be on classpath so Scala compiler + // can use them while constructing its own classpath for compilation + fork in Test := true, + // needed because we fork tests and tests are ran in parallel so we have multiple Scala + // compiler instances that are memory hungry + javaOptions in Test += "-Xmx1G", + crossScalaVersions := compilerBridgeTestScalaVersions, + libraryDependencies += scalaCompiler.value, + altPublishSettings, + skip in publish := true, ) val scalaPartialVersion = Def setting (CrossVersion partialVersion scalaVersion.value) @@ -397,7 +432,7 @@ lazy val zincApiInfo = (project in internalPath / "zinc-apiinfo") .configure(addBaseSettingsAndTestDeps) .settings( name := "zinc ApiInfo", - crossScalaVersions := compilerBridgeScalaVersions, + crossScalaVersions := compilerBridgeTestScalaVersions, relaxNon212, mimaSettings, ) @@ -408,7 +443,7 @@ lazy val zincClasspath = (project in internalPath / "zinc-classpath") .configure(addBaseSettingsAndTestDeps) .settings( name := "zinc Classpath", - crossScalaVersions := compilerBridgeScalaVersions, + crossScalaVersions := compilerBridgeTestScalaVersions, relaxNon212, libraryDependencies ++= Seq(scalaCompiler.value, launcherInterface), mimaSettings, @@ -421,7 +456,7 @@ lazy val zincClassfile = (project in internalPath / "zinc-classfile") .configure(addBaseSettingsAndTestDeps) .settings( name := "zinc Classfile", - crossScalaVersions := compilerBridgeScalaVersions, + crossScalaVersions := compilerBridgeTestScalaVersions, relaxNon212, mimaSettings, ) @@ -439,10 +474,10 @@ lazy val zincScripted = (project in internalPath / "zinc-scripted") lazy val crossTestBridges = { Command.command("crossTestBridges") { state => - (compilerBridgeScalaVersions.flatMap { (bridgeVersion: String) => + (compilerBridgeTestScalaVersions.flatMap { (bridgeVersion: String) => // Note the ! here. You need this so compilerInterface gets forced to the scalaVersion s"++ $bridgeVersion!" :: - s"${compilerBridge.id}/test" :: + s"${compilerBridgeTest.id}/test" :: Nil }) ::: (s"++ $scala212!" :: @@ -457,7 +492,6 @@ lazy val publishBridgesAndSet = { s"${compilerInterface.id}/publishLocal" :: compilerBridgeScalaVersions.flatMap { (bridgeVersion: String) => s"++ $bridgeVersion!" :: - s"${zincApiInfo.id}/publishLocal" :: s"${compilerBridge.id}/publishLocal" :: Nil } ::: s"++ $userScalaVersion!" :: @@ -473,7 +507,6 @@ lazy val publishBridgesAndTest = Command.args("publishBridgesAndTest", " s"++ $bridgeVersion" :: - s"${zincApiInfo.id}/publishLocal" :: s"${compilerBridge.id}/publishLocal" :: Nil }) ::: s"++ $version" :: diff --git a/internal/compiler-bridge/src/test/resources/ExtractUsedNamesPerformance.scala.source b/internal/compiler-bridge-test/src/test/resources/ExtractUsedNamesPerformance.scala.source similarity index 100% rename from internal/compiler-bridge/src/test/resources/ExtractUsedNamesPerformance.scala.source rename to internal/compiler-bridge-test/src/test/resources/ExtractUsedNamesPerformance.scala.source diff --git a/internal/compiler-bridge/src/test/scala/xsbt/ClassNameSpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/ClassNameSpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/ClassNameSpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/ClassNameSpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/DependencySpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/DependencySpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/DependencySpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/DependencySpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/ExtractAPISpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/ExtractAPISpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/ExtractAPISpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/ExtractAPISpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/ExtractUsedNamesPerformanceSpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/ExtractUsedNamesPerformanceSpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/ExtractUsedNamesPerformanceSpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/ExtractUsedNamesPerformanceSpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/InteractiveConsoleInterfaceSpecification.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/InteractiveConsoleInterfaceSpecification.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/InteractiveConsoleInterfaceSpecification.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/InteractiveConsoleInterfaceSpecification.scala diff --git a/internal/compiler-bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala b/internal/compiler-bridge-test/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala similarity index 100% rename from internal/compiler-bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala rename to internal/compiler-bridge-test/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala diff --git a/internal/compiler-bridge/src/main/mima-filters/1.0.0.backwards.excludes b/internal/compiler-bridge/src/main/mima-filters/1.0.0.backwards.excludes new file mode 100644 index 0000000000..0adbb561dc --- /dev/null +++ b/internal/compiler-bridge/src/main/mima-filters/1.0.0.backwards.excludes @@ -0,0 +1,6 @@ +# xsbti Java interfaces must be defined in the compiler interface, not the bridge. +# Bridge implementations are compiled per Zinc, so these are safe to change. +ProblemFilters.exclude[MissingClassProblem]("xsbti.InteractiveConsoleFactory") +ProblemFilters.exclude[MissingClassProblem]("xsbti.InteractiveConsoleResult") +ProblemFilters.exclude[MissingClassProblem]("xsbti.InteractiveConsoleInterface") +ProblemFilters.exclude[MissingClassProblem]("xsbti.InteractiveConsoleResponse") diff --git a/internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala b/internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala index ec32db1927..b81e91c1d1 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala @@ -68,15 +68,19 @@ trait ClassName extends Compat { * * If `s` represents a package object `pkg3`, then the returned name will be `pkg1.pkg2.pkg3.package`. * If `s` represents a class `Foo` nested in package object `pkg3` then the returned name is `pkg1.pkg2.pk3.Foo`. + * + * Note that some objects with special access rights are encoded in names + * (like qualified privates `private[qualifier]`). In order to get the right + * original names, we need to use `unexpandedName`. */ protected def classNameAsSeenIn(in: Symbol, s: Symbol): String = enteringPhase(currentRun.picklerPhase.next) { if (in.isRoot || in.isRootPackage || in == NoSymbol || in.isEffectiveRoot) s.simpleName.toString else if (in.isPackageObjectOrClass) - in.owner.fullName + "." + s.name + in.owner.fullName + "." + s.unexpandedName else - in.fullName + "." + s.name + in.fullName + "." + s.unexpandedName } private def pickledName(s: Symbol): Name = diff --git a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala index 6ee53309aa..4148265449 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/Dependency.scala @@ -92,16 +92,21 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with } // Define processor reusing `processDependency` definition - val memberRef = processDependency(DependencyByMemberRef) _ - val inheritance = processDependency(DependencyByInheritance) _ - val localInheritance = processDependency(LocalDependencyByInheritance) _ + val memberRef = processDependency(DependencyByMemberRef, false) _ + val inheritance = processDependency(DependencyByInheritance, true) _ + val localInheritance = processDependency(LocalDependencyByInheritance, true) _ + + @deprecated("Use processDependency that takes allowLocal.", "1.1.0") + def processDependency(context: DependencyContext)(dep: ClassDependency): Unit = + processDependency(context, true)(dep) /* * Handles dependency on given symbol by trying to figure out if represents a term * that is coming from either source code (not necessarily compiled in this compilation * run) or from class file and calls respective callback method. */ - def processDependency(context: DependencyContext)(dep: ClassDependency): Unit = { + def processDependency(context: DependencyContext, allowLocal: Boolean)( + dep: ClassDependency): Unit = { val fromClassName = classNameAsString(dep.from) def binaryDependency(file: File, binaryClassName: String) = @@ -134,11 +139,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with case None => debuglog(Feedback.noOriginFileForExternalSymbol(dep.to)) } - } else if (onSource.file != sourceFile) { - // Dependency is internal -- but from other file / compilation unit + } else if (onSource.file != sourceFile || allowLocal) { + // We cannot ignore dependencies coming from the same source file because + // the dependency info needs to propagate. See source-dependencies/trait-trait-211. val onClassName = classNameAsString(dep.to) callback.classDependency(onClassName, fromClassName, context) - } else () // Comes from the same file, ignore + } } } @@ -228,7 +234,6 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with val depClass = enclOrModuleClass(dep) val dependency = ClassDependency(fromClass, depClass) if (!cache.contains(dependency) && - fromClass.associatedFile != depClass.associatedFile && !depClass.isRefinementClass) { process(dependency) cache.add(dependency) diff --git a/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala b/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala index 2a24052976..07ba61e5e5 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/ExtractAPI.scala @@ -358,7 +358,16 @@ class ExtractAPI[GlobalType <: Global]( * TODO: can we include hashes for parent classes instead? This seems a bit messy. */ private def mkStructureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = { - val ancestorTypes = linearizedAncestorTypes(info) + val ancestorTypes0 = linearizedAncestorTypes(info) + val ancestorTypes = + if (s.isDerivedValueClass) { + val underlying = s.derivedValueClassUnbox.tpe.finalResultType + // The underlying type of a value class should be part of the name hash + // of the value class (see the test `value-class-underlying`), this is accomplished + // by adding the underlying type to the list of parent types. + underlying :: ancestorTypes0 + } else + ancestorTypes0 val decls = info.decls.toList val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls val declSet = decls.toSet diff --git a/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala b/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala index 42f571db27..01dd182e5e 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleHelper.scala @@ -7,14 +7,14 @@ package xsbt -import scala.tools.nsc.interpreter.IR +import Compat._ import xsbti.InteractiveConsoleResult object InteractiveConsoleHelper { - implicit def toConsoleResult(ir: IR.Result): InteractiveConsoleResult = + implicit def toConsoleResult(ir: Results.Result): InteractiveConsoleResult = ir match { - case IR.Success => InteractiveConsoleResult.Success - case IR.Incomplete => InteractiveConsoleResult.Incomplete - case IR.Error => InteractiveConsoleResult.Error + case Results.Success => InteractiveConsoleResult.Success + case Results.Incomplete => InteractiveConsoleResult.Incomplete + case Results.Error => InteractiveConsoleResult.Error } } diff --git a/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala b/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala index 2aa9f5f483..24e6171722 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala +++ b/internal/compiler-bridge/src/main/scala/xsbt/InteractiveConsoleInterface.scala @@ -14,6 +14,7 @@ import scala.tools.nsc.{ GenericRunnerCommand, Settings } import xsbti.Logger +import Compat._ import InteractiveConsoleHelper._ class InteractiveConsoleInterface( @@ -38,9 +39,10 @@ class InteractiveConsoleInterface( val outWriter: StringWriter = new StringWriter val poutWriter: PrintWriter = new PrintWriter(outWriter) - val interpreter: IMain = new IMain(compilerSettings, new PrintWriter(outWriter)) { - def lastReq: Request = prevRequestList.last - } + val interpreter: IMain = + new IMain(compilerSettings, replReporter(compilerSettings, new PrintWriter(outWriter))) { + def lastReq: Request = prevRequestList.last + } def interpret(line: String, synthetic: Boolean): InteractiveConsoleResponse = { clearBuffer() diff --git a/internal/compiler-bridge/src/main/scala_2.10/xsbt/Compat.scala b/internal/compiler-bridge/src/main/scala_2.10/xsbt/Compat.scala index 752ac20d6b..c34db28ae4 100644 --- a/internal/compiler-bridge/src/main/scala_2.10/xsbt/Compat.scala +++ b/internal/compiler-bridge/src/main/scala_2.10/xsbt/Compat.scala @@ -1,5 +1,6 @@ package xsbt +import java.io.PrintWriter import xsbti.compile.Output import scala.reflect.{ internal => sri } import scala.reflect.internal.{ util => sriu } @@ -150,6 +151,12 @@ trait ZincGlobalCompat { } object Compat { + // IR is renamed to Results + val Results = scala.tools.nsc.interpreter.IR + + // IMain in 2.13 accepts ReplReporter + def replReporter(settings: Settings, writer: PrintWriter) = writer + implicit final class TreeOps(val tree: sri.Trees#Tree) extends AnyVal { // Introduced in 2.11 @inline final def hasSymbolField: Boolean = tree.hasSymbol diff --git a/internal/compiler-bridge/src/main/scala/xsbt/ConsoleInterface.scala b/internal/compiler-bridge/src/main/scala_2.10/xsbt/ConsoleInterface.scala similarity index 96% rename from internal/compiler-bridge/src/main/scala/xsbt/ConsoleInterface.scala rename to internal/compiler-bridge/src/main/scala_2.10/xsbt/ConsoleInterface.scala index caff0157b6..531891ab2e 100644 --- a/internal/compiler-bridge/src/main/scala/xsbt/ConsoleInterface.scala +++ b/internal/compiler-bridge/src/main/scala_2.10/xsbt/ConsoleInterface.scala @@ -8,7 +8,7 @@ package xsbt import xsbti.Logger -import scala.tools.nsc.interpreter.{ ILoop, IMain, InteractiveReader } +import scala.tools.nsc.interpreter.{ ILoop, IMain, InteractiveReader, NamedParam } import scala.tools.nsc.reporters.Reporter import scala.tools.nsc.{ GenericRunnerCommand, Settings } @@ -54,7 +54,7 @@ class ConsoleInterface { super.createInterpreter() for ((id, value) <- bindNames zip bindValues) - intp.beQuietDuring(intp.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value)) + intp.quietBind(NamedParam.clazz(id, value)) if (!initialCommands.isEmpty) intp.interpret(initialCommands) diff --git a/internal/compiler-bridge/src/main/scala_2.11+/xsbt/Compat.scala b/internal/compiler-bridge/src/main/scala_2.11-12/xsbt/Compat.scala similarity index 73% rename from internal/compiler-bridge/src/main/scala_2.11+/xsbt/Compat.scala rename to internal/compiler-bridge/src/main/scala_2.11-12/xsbt/Compat.scala index 56a05d9d5c..790ff4e83b 100644 --- a/internal/compiler-bridge/src/main/scala_2.11+/xsbt/Compat.scala +++ b/internal/compiler-bridge/src/main/scala_2.11-12/xsbt/Compat.scala @@ -7,12 +7,19 @@ package xsbt +import java.io.PrintWriter import xsbti.compile.Output import scala.tools.nsc.Settings abstract class Compat -object Compat +object Compat { + // IR is renamed to Results + val Results = scala.tools.nsc.interpreter.IR + + // IMain in 2.13 accepts ReplReporter + def replReporter(settings: Settings, writer: PrintWriter) = writer +} /** Defines compatibility utils for [[ZincCompiler]]. */ trait ZincGlobalCompat { diff --git a/internal/compiler-bridge/src/main/scala_2.11-12/xsbt/ConsoleInterface.scala b/internal/compiler-bridge/src/main/scala_2.11-12/xsbt/ConsoleInterface.scala new file mode 100644 index 0000000000..531891ab2e --- /dev/null +++ b/internal/compiler-bridge/src/main/scala_2.11-12/xsbt/ConsoleInterface.scala @@ -0,0 +1,105 @@ +/* + * 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 xsbt + +import xsbti.Logger +import scala.tools.nsc.interpreter.{ ILoop, IMain, InteractiveReader, NamedParam } +import scala.tools.nsc.reporters.Reporter +import scala.tools.nsc.{ GenericRunnerCommand, Settings } + +class ConsoleInterface { + def commandArguments( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Array[String] = + MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String] + + def run( + args: Array[String], + bootClasspathString: String, + classpathString: String, + initialCommands: String, + cleanupCommands: String, + loader: ClassLoader, + bindNames: Array[String], + bindValues: Array[Any], + log: Logger + ): Unit = { + lazy val interpreterSettings = MakeSettings.sync(args.toList, log) + val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log) + + log.info(Message("Starting scala interpreter...")) + log.info(Message("")) + + val loop = new ILoop { + override def createInterpreter() = { + if (loader ne null) { + in = InteractiveReader.apply() + intp = new IMain(settings) { + override protected def parentClassLoader = + if (loader eq null) super.parentClassLoader else loader + + override protected def newCompiler(settings: Settings, reporter: Reporter) = + super.newCompiler(compilerSettings, reporter) + } + intp.setContextClassLoader() + } else + super.createInterpreter() + + for ((id, value) <- bindNames zip bindValues) + intp.quietBind(NamedParam.clazz(id, value)) + + if (!initialCommands.isEmpty) + intp.interpret(initialCommands) + + () + } + + override def closeInterpreter(): Unit = { + if (!cleanupCommands.isEmpty) + intp.interpret(cleanupCommands) + super.closeInterpreter() + } + } + + loop.process(if (loader eq null) compilerSettings else interpreterSettings) + + () + } +} + +object MakeSettings { + def apply(args: List[String], log: Logger): Settings = { + val command = new GenericRunnerCommand(args, message => log.error(Message(message))) + if (command.ok) + command.settings + else + throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg) + } + + def sync( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Settings = { + val compilerSettings = sync(args.toList, log) + if (!bootClasspathString.isEmpty) + compilerSettings.bootclasspath.value = bootClasspathString + compilerSettings.classpath.value = classpathString + compilerSettings + } + + def sync(options: List[String], log: Logger): Settings = { + val settings = apply(options, log) + settings.Yreplsync.value = true + settings + } +} diff --git a/internal/compiler-bridge/src/main/scala_2.13/xsbt/Compat.scala b/internal/compiler-bridge/src/main/scala_2.13/xsbt/Compat.scala new file mode 100644 index 0000000000..19ca44cd9d --- /dev/null +++ b/internal/compiler-bridge/src/main/scala_2.13/xsbt/Compat.scala @@ -0,0 +1,33 @@ +/* + * 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 xsbt + +import java.io.PrintWriter +import xsbti.compile.Output +import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter.shell.ReplReporterImpl + +abstract class Compat +object Compat { + // IR is renanmed to Results + val Results = scala.tools.nsc.interpreter.Results + + // IMain in 2.13 accepts ReplReporter + def replReporter(settings: Settings, writer: PrintWriter) = + new ReplReporterImpl(settings, writer) +} + +/** Defines compatibility utils for [[ZincCompiler]]. */ +trait ZincGlobalCompat { + protected def superDropRun(): Unit = () +} + +private trait CachedCompilerCompat { self: CachedCompiler0 => + def newCompiler(settings: Settings, reporter: DelegatingReporter, output: Output): ZincCompiler = + new ZincCompiler(settings, reporter, output) +} diff --git a/internal/compiler-bridge/src/main/scala_2.13/xsbt/ConsoleInterface.scala b/internal/compiler-bridge/src/main/scala_2.13/xsbt/ConsoleInterface.scala new file mode 100644 index 0000000000..2081ce0c78 --- /dev/null +++ b/internal/compiler-bridge/src/main/scala_2.13/xsbt/ConsoleInterface.scala @@ -0,0 +1,102 @@ +/* + * 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 xsbt + +import xsbti.Logger +import scala.tools.nsc.interpreter.IMain +import scala.tools.nsc.interpreter.shell.{ ILoop, ShellConfig, ReplReporterImpl } +import scala.tools.nsc.reporters.Reporter +import scala.tools.nsc.{ GenericRunnerCommand, Settings } + +class ConsoleInterface { + def commandArguments( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Array[String] = + MakeSettings.sync(args, bootClasspathString, classpathString, log).recreateArgs.toArray[String] + + def run( + args: Array[String], + bootClasspathString: String, + classpathString: String, + initialCommands: String, + cleanupCommands: String, + loader: ClassLoader, + bindNames: Array[String], + bindValues: Array[Any], + log: Logger + ): Unit = { + lazy val interpreterSettings = MakeSettings.sync(args.toList, log) + val compilerSettings = MakeSettings.sync(args, bootClasspathString, classpathString, log) + + log.info(Message("Starting scala interpreter...")) + log.info(Message("")) + + val loop = new ILoop(ShellConfig(interpreterSettings)) { + override def createInterpreter(interpreterSettings: Settings) = { + if (loader ne null) { + val reporter = new ReplReporterImpl(interpreterSettings) + intp = new IMain(interpreterSettings, reporter) { + override protected def parentClassLoader = + if (loader eq null) super.parentClassLoader + else loader + } + intp.setContextClassLoader() + } else + super.createInterpreter(interpreterSettings) + + for ((id, value) <- bindNames zip bindValues) + intp.beQuietDuring(intp.bind(id, value.asInstanceOf[AnyRef].getClass.getName, value)) + + if (!initialCommands.isEmpty) + intp.interpret(initialCommands) + + () + } + + override def closeInterpreter(): Unit = { + if (!cleanupCommands.isEmpty) + intp.interpret(cleanupCommands) + super.closeInterpreter() + } + } + + loop.run(compilerSettings) + } +} + +object MakeSettings { + def apply(args: List[String], log: Logger): Settings = { + val command = new GenericRunnerCommand(args, message => log.error(Message(message))) + if (command.ok) + command.settings + else + throw new InterfaceCompileFailed(Array(), Array(), command.usageMsg) + } + + def sync( + args: Array[String], + bootClasspathString: String, + classpathString: String, + log: Logger + ): Settings = { + val compilerSettings = sync(args.toList, log) + if (!bootClasspathString.isEmpty) + compilerSettings.bootclasspath.value = bootClasspathString + compilerSettings.classpath.value = classpathString + compilerSettings + } + + def sync(options: List[String], log: Logger): Settings = { + val settings = apply(options, log) + settings.Yreplsync.value = true + settings + } +} diff --git a/internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleFactory.java b/internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java similarity index 100% rename from internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleFactory.java rename to internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleFactory.java diff --git a/internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleInterface.java b/internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java similarity index 100% rename from internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleInterface.java rename to internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleInterface.java diff --git a/internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleResponse.java b/internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleResponse.java similarity index 100% rename from internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleResponse.java rename to internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleResponse.java diff --git a/internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleResult.java b/internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleResult.java similarity index 100% rename from internal/compiler-bridge/src/main/java/xsbti/InteractiveConsoleResult.java rename to internal/compiler-interface/src/main/java/xsbti/InteractiveConsoleResult.java diff --git a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala index f4a08e3085..47ae7a3a35 100644 --- a/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala +++ b/internal/zinc-apiinfo/src/main/scala/sbt/internal/inc/ClassToAPI.scala @@ -244,12 +244,14 @@ object ClassToAPI { accumulate(t).filterNot(_ == null).distinct } - @deprecated("No longer used", "0.13.0") - def parents(c: Class[_]): Seq[api.Type] = types(allSuperTypes(c)) - def types(ts: Seq[Type]): Array[api.Type] = (ts filter (_ ne null) map reference).toArray + def types(ts: Seq[Type]): Array[api.Type] = + ts.filter(_ ne null).map(reference).toArray def upperBounds(ts: Array[Type]): api.Type = api.Structure.of(lzy(types(ts)), lzyEmptyDefArray, lzyEmptyDefArray) + @deprecated("No longer used", "0.13.0") + def parents(c: Class[_]): Seq[api.Type] = types(allSuperTypes(c)) + @deprecated("Use fieldToDef[4] instead", "0.13.9") def fieldToDef(enclPkg: Option[String])(f: Field): api.FieldLike = { val c = f.getDeclaringClass @@ -496,12 +498,18 @@ object ClassToAPI { api.Projection.of(api.Singleton.of(pathFromString(p)), cls) } } + + // sbt/zinc#389: Ignore nulls coming from generic parameter types of lambdas + private[this] def ignoreNulls[T](genericTypes: Array[T]): Array[T] = + genericTypes.filter(_ != null) + def referenceP(t: ParameterizedType): api.Parameterized = { - val targs = t.getActualTypeArguments + val targs = ignoreNulls(t.getActualTypeArguments) val args = if (targs.isEmpty) emptyTypeArray else arrayMap(targs)(t => reference(t): api.Type) val base = reference(t.getRawType) api.Parameterized.of(base, args) } + def reference(t: Type): api.Type = t match { case _: WildcardType => reference("_") @@ -553,8 +561,12 @@ object ClassToAPI { private[this] def exceptionTypes(c: Constructor[_]): Array[Type] = c.getGenericExceptionTypes private[this] def exceptionTypes(m: Method): Array[Type] = m.getGenericExceptionTypes - private[this] def parameterTypes(m: Method): Array[Type] = m.getGenericParameterTypes - private[this] def parameterTypes(c: Constructor[_]): Array[Type] = c.getGenericParameterTypes + + private[this] def parameterTypes(m: Method): Array[Type] = + ignoreNulls(m.getGenericParameterTypes) + + private[this] def parameterTypes(c: Constructor[_]): Array[Type] = + ignoreNulls(c.getGenericParameterTypes) private[this] def typeParameterTypes[T](m: Constructor[T]): Array[TypeVariable[Constructor[T]]] = m.getTypeParameters diff --git a/internal/zinc-compile-core/src/main/java/xsbti/ReporterUtil.java b/internal/zinc-compile-core/src/main/java/xsbti/ReporterUtil.java index 83d1cbbe01..e32e0d5ab5 100644 --- a/internal/zinc-compile-core/src/main/java/xsbti/ReporterUtil.java +++ b/internal/zinc-compile-core/src/main/java/xsbti/ReporterUtil.java @@ -1,3 +1,10 @@ +/* + * 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 xsbti; import sbt.internal.inc.ReporterManager; diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala index aeb2f1dacc..6c73cfa6ae 100644 --- a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/FilteredReporter.scala @@ -1,3 +1,10 @@ +/* + * 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 import java.nio.file.Path diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggedReporter.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggedReporter.scala index 4bca388381..117e178d30 100644 --- a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggedReporter.scala +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/LoggedReporter.scala @@ -101,9 +101,9 @@ class LoggedReporter( sourcePositionMapper: Position => Position = identity[Position] ) extends Reporter { import sbt.util.InterfaceUtil.{ toSupplier => f0 } - val positions = new mutable.HashMap[PositionKey, Severity] - val count = new EnumMap[Severity, Int](classOf[Severity]) - protected val allProblems = new mutable.ListBuffer[Problem] + lazy val positions = new mutable.HashMap[PositionKey, Severity] + lazy val count = new EnumMap[Severity, Int](classOf[Severity]) + protected lazy val allProblems = new mutable.ListBuffer[Problem] reset() def reset(): Unit = { diff --git a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala index b901369f1c..507fdc3552 100644 --- a/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala +++ b/internal/zinc-compile-core/src/main/scala/sbt/internal/inc/ReporterManager.scala @@ -1,3 +1,10 @@ +/* + * 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 import java.io.{ OutputStreamWriter, PrintStream, PrintWriter } diff --git a/internal/zinc-core/src/main/java/xsbti/compile/AnalysisContents.java b/internal/zinc-core/src/main/java/xsbti/compile/AnalysisContents.java index 376e552a2d..283117da91 100644 --- a/internal/zinc-core/src/main/java/xsbti/compile/AnalysisContents.java +++ b/internal/zinc-core/src/main/java/xsbti/compile/AnalysisContents.java @@ -1,3 +1,10 @@ +/* + * 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 xsbti.compile; import sbt.internal.inc.ConcreteAnalysisContents; diff --git a/internal/zinc-core/src/main/java/xsbti/compile/AnalysisStore.java b/internal/zinc-core/src/main/java/xsbti/compile/AnalysisStore.java index 09846f5b47..5a3e2c61fa 100644 --- a/internal/zinc-core/src/main/java/xsbti/compile/AnalysisStore.java +++ b/internal/zinc-core/src/main/java/xsbti/compile/AnalysisStore.java @@ -1,3 +1,10 @@ +/* + * 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 xsbti.compile; import java.util.Optional; diff --git a/internal/zinc-core/src/main/scala/sbt/internal/inc/ConcreteAnalysisContents.scala b/internal/zinc-core/src/main/scala/sbt/internal/inc/ConcreteAnalysisContents.scala index dfb6bbb93c..d11b0462c1 100644 --- a/internal/zinc-core/src/main/scala/sbt/internal/inc/ConcreteAnalysisContents.scala +++ b/internal/zinc-core/src/main/scala/sbt/internal/inc/ConcreteAnalysisContents.scala @@ -1,3 +1,10 @@ +/* + * 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 import xsbti.compile.{ AnalysisContents, CompileAnalysis, MiniSetup } diff --git a/internal/zinc-core/src/main/scala/sbt/internal/inc/Relations.scala b/internal/zinc-core/src/main/scala/sbt/internal/inc/Relations.scala index 16318212f6..6f78eb1524 100644 --- a/internal/zinc-core/src/main/scala/sbt/internal/inc/Relations.scala +++ b/internal/zinc-core/src/main/scala/sbt/internal/inc/Relations.scala @@ -275,6 +275,7 @@ object Relations { case o: ClassDependencies => internal == o.internal && external == o.external case _ => false } + override def toString: String = s"ClassDependencies(internal = $internal, external = $external)" override def hashCode = (internal, external).hashCode } @@ -661,26 +662,23 @@ private class MRelationsNameHashing( override def hashCode = (srcProd :: libraryDep :: libraryClassName :: memberRef :: inheritance :: classes :: Nil).hashCode - override def toString = ( - """ + override def toString: String = { + val internalDepsStr = (internalDependencies.dependencies map { + case (k, vs) => k + " " + relation_s(vs) + }).mkString("\n ", "\n ", "") + val externalDepsStr = (externalDependencies.dependencies map { + case (k, vs) => k + " " + relation_s(vs) + }).mkString("\n ", "\n ", "") + s""" |Relations (with name hashing enabled): - | products: %s - | library deps: %s - | library class names: %s - | class deps: %s - | ext deps: %s - | class names: %s - | used names: %s - | product class names: %s - """.trim.stripMargin.format( - List(srcProd, - libraryDep, - libraryClassName, - internalClassDep, - externalClassDep, - classes, - names, - productClassName) map relation_s: _*) - ) - + | products: ${relation_s(srcProd)} + | library deps: ${relation_s(libraryDep)} + | library class names: ${relation_s(libraryClassName)} + | internalDependencies: $internalDepsStr + | externalDependencies: $externalDepsStr + | class names: ${relation_s(classes)} + | used names: ${relation_s(names)} + | product class names: ${relation_s(productClassName)} + """.trim.stripMargin + } } diff --git a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala index 1c665172c6..dbd18dfc8d 100644 --- a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala +++ b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala @@ -42,9 +42,12 @@ private[sbt] object ZincComponentCompiler { def compilerBridgeId(scalaVersion: String) = { // Defaults to bridge for 2.12 for Scala versions bigger than 2.12.x scalaVersion match { - case sc if (sc startsWith "2.10.") => "compiler-bridge_2.10" - case sc if (sc startsWith "2.11.") => "compiler-bridge_2.11" - case _ => "compiler-bridge_2.12" + case sc if (sc startsWith "2.10.") => "compiler-bridge_2.10" + case sc if (sc startsWith "2.11.") => "compiler-bridge_2.11" + case sc if (sc startsWith "2.12.") => "compiler-bridge_2.12" + case sc if (sc startsWith "2.13.0-M") => "compiler-bridge_2.13.0-M2" + case sc if (sc startsWith "2.13.0-RC") => "compiler-bridge_2.13.0-M2" + case _ => "compiler-bridge_2.13" } } import xsbti.ArtifactInfo.SbtOrganization diff --git a/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala b/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala index 3fbd1a24f6..f3245c1ea4 100644 --- a/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala +++ b/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/ZincComponentCompilerSpec.scala @@ -11,6 +11,7 @@ class ZincComponentCompilerSpec extends BridgeProviderSpecification { val scala2121 = "2.12.1" val scala2122 = "2.12.2" val scala2123 = "2.12.3" + val scala2130M2 = "2.13.0-M2" val logger = ConsoleLogger() it should "compile the bridge for Scala 2.10.5 and 2.10.6" in { @@ -28,4 +29,8 @@ class ZincComponentCompilerSpec extends BridgeProviderSpecification { IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2122) should exist) IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2123) should exist) } + + it should "compile the bridge for Scala 2.13.0-M2" in { + IO.withTemporaryDirectory(t => getCompilerBridge(t, logger, scala2130M2) should exist) + } } diff --git a/internal/zinc-persist/src/main/java/xsbti/compile/FileAnalysisStore.java b/internal/zinc-persist/src/main/java/xsbti/compile/FileAnalysisStore.java index 4164d1dd90..d54f473b4b 100644 --- a/internal/zinc-persist/src/main/java/xsbti/compile/FileAnalysisStore.java +++ b/internal/zinc-persist/src/main/java/xsbti/compile/FileAnalysisStore.java @@ -1,3 +1,10 @@ +/* + * 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 xsbti.compile; import xsbti.compile.analysis.ReadWriteMappers; diff --git a/internal/zinc-persist/src/main/java/xsbti/compile/analysis/RootPaths.java b/internal/zinc-persist/src/main/java/xsbti/compile/analysis/RootPaths.java index a692948c25..21e1a4b608 100644 --- a/internal/zinc-persist/src/main/java/xsbti/compile/analysis/RootPaths.java +++ b/internal/zinc-persist/src/main/java/xsbti/compile/analysis/RootPaths.java @@ -1,3 +1,10 @@ +/* + * 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 xsbti.compile.analysis; import java.io.File; diff --git a/internal/zinc-persist/src/main/scala/sbt/internal/inc/ConcreteRootPaths.scala b/internal/zinc-persist/src/main/scala/sbt/internal/inc/ConcreteRootPaths.scala index 3dae3f1cb5..ae9e486b31 100644 --- a/internal/zinc-persist/src/main/scala/sbt/internal/inc/ConcreteRootPaths.scala +++ b/internal/zinc-persist/src/main/scala/sbt/internal/inc/ConcreteRootPaths.scala @@ -1,3 +1,10 @@ +/* + * 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 import java.io.File diff --git a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/BatchScriptRunner.scala b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/BatchScriptRunner.scala new file mode 100644 index 0000000000..5f36d99a67 --- /dev/null +++ b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/BatchScriptRunner.scala @@ -0,0 +1,64 @@ +package sbt.internal.inc + +import org.scalatest.exceptions.TestFailedException +import sbt.internal.inc +import sbt.internal.scripted._ +import sbt.internal.inc.BatchScriptRunner.States + +/** Defines an alternative script runner that allows batch execution. */ +private[sbt] class BatchScriptRunner extends ScriptRunner { + + /** Defines a method to run batched execution. + * + * @param statements The list of handlers and statements. + * @param states The states of the runner. In case it's empty, inherited apply is called. + */ + def apply(statements: List[(StatementHandler, Statement)], states: States): Unit = { + if (states.isEmpty) super.apply(statements) + else statements.foreach(st => processStatement(st._1, st._2, states)) + } + + def initStates(states: States, handlers: Seq[StatementHandler]): Unit = + handlers.foreach(handler => states(handler) = handler.initialState) + + def cleanUpHandlers(handlers: Seq[StatementHandler], states: States): Unit = { + for (handler <- handlers; state <- states.get(handler)) { + try handler.finish(state.asInstanceOf[handler.State]) + catch { case _: Exception => () } + } + } + + import BatchScriptRunner.PreciseScriptedError + def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = { + val state = states(handler).asInstanceOf[handler.State] + val nextState = + try { Right(handler(statement.command, statement.arguments, state)) } catch { + case e: Exception => Left(e) + } + nextState match { + case Left(err) => + if (statement.successExpected) { + err match { + case t: TestFailed => + val errorMessage = s"${t.getMessage} produced by" + throw new PreciseScriptedError(statement, errorMessage, null) + case _ => throw new PreciseScriptedError(statement, "Command failed", err) + } + } else () + case Right(s) => + if (statement.successExpected) states(handler) = s + else throw new PreciseScriptedError(statement, "Expecting error at", null) + } + } +} + +private[sbt] object BatchScriptRunner { + import scala.collection.mutable + type States = mutable.HashMap[StatementHandler, Any] + + // Should be used instead of sbt.internal.scripted.TestException that doesn't show failed command + final class PreciseScriptedError(st: Statement, msg: String, e: Throwable) + extends RuntimeException(s"$msg: '${st.command} ${st.arguments.mkString(" ")}'", e) { + override def fillInStackTrace = e + } +} diff --git a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncHandler.scala b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncHandler.scala index adc509e53e..8fdf8417ea 100644 --- a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncHandler.scala +++ b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncHandler.scala @@ -57,8 +57,18 @@ final class IncHandler(directory: File, cacheDir: File, scriptedLog: ManagedLogg type IncCommand = (ProjectStructure, List[String], IncInstance) => Unit val compiler = new IncrementalCompilerImpl - def initialState: Option[IncInstance] = None - def finish(state: Option[IncInstance]): Unit = () + + def initialState: Option[IncInstance] = { + initBuildStructure() + None + } + + def finish(state: Option[IncInstance]): Unit = { + // Required so that next projects re-read the project structure + buildStructure.clear() + () + } + val buildStructure: mutable.Map[String, ProjectStructure] = mutable.Map.empty def initBuildStructure(): Unit = { val build = initBuild @@ -70,8 +80,6 @@ final class IncHandler(directory: File, cacheDir: File, scriptedLog: ManagedLogg } } - initBuildStructure() - private final val RootIdentifier = "root" def initBuild: Build = { if ((directory / "build.json").exists) { @@ -108,14 +116,14 @@ final class IncHandler(directory: File, cacheDir: File, scriptedLog: ManagedLogg private final val noLogger = Logger.Null private[this] def onNewIncInstance(p: ProjectStructure): IncInstance = { val scalaVersion = p.scalaVersion - val (compilerBridge, si) = IncHandler.scriptedCompilerCache.get(scalaVersion) match { + val (compilerBridge, si) = IncHandler.getCompilerCacheFor(scalaVersion) match { case Some(alreadyInstantiated) => alreadyInstantiated case None => val compilerBridge = getCompilerBridge(cacheDir, noLogger, scalaVersion) val si = scalaInstance(scalaVersion, cacheDir, noLogger) val toCache = (compilerBridge, si) - IncHandler.scriptedCompilerCache.put(scalaVersion, toCache) + IncHandler.putCompilerCache(scalaVersion, toCache) toCache } val analyzingCompiler = scalaCompiler(si, compilerBridge) @@ -505,7 +513,12 @@ case class ProjectStructure( object IncHandler { type Cached = (File, xsbti.compile.ScalaInstance) - private[internal] final val scriptedCompilerCache = new mutable.WeakHashMap[String, Cached]() + private[this] final val scriptedCompilerCache = new mutable.WeakHashMap[String, Cached]() + def getCompilerCacheFor(scalaVersion: String): Option[Cached] = + synchronized(scriptedCompilerCache.get(scalaVersion)) + def putCompilerCache(scalaVersion: String, cached: Cached): Option[Cached] = + synchronized(scriptedCompilerCache.put(scalaVersion, cached)) + private[internal] final val classLoaderCache = Some( new ClassLoaderCache(new URLClassLoader(Array()))) } diff --git a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncScriptedRunner.scala b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncScriptedRunner.scala index cb9f48810d..0f5cca876b 100644 --- a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncScriptedRunner.scala +++ b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/IncScriptedRunner.scala @@ -2,15 +2,80 @@ package sbt.internal.inc import java.io.File -import sbt.internal.scripted.ScriptedRunnerImpl +import sbt.internal.scripted.{ HandlersProvider, ListTests, ScriptedTest } import sbt.io.IO +import sbt.util.Logger + +import scala.collection.parallel.ParSeq class IncScriptedRunner { def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String]): Unit = { IO.withTemporaryDirectory { tempDir => // Create a global temporary directory to store the bridge et al val handlers = new IncScriptedHandlers(tempDir) - ScriptedRunnerImpl.run(resourceBaseDirectory, bufferLog, tests, handlers); + ScriptedRunnerImpl.run(resourceBaseDirectory, bufferLog, tests, handlers, 4) + } + } +} + +object ScriptedRunnerImpl { + type TestRunner = () => Seq[Option[String]] + + def run( + resourceBaseDirectory: File, + bufferLog: Boolean, + tests: Array[String], + handlersProvider: HandlersProvider, + instances: Int + ): Unit = { + val globalLogger = newLogger + val logsDir = newScriptedLogsDir + val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider, logsDir) + val scriptedTests = get(tests, resourceBaseDirectory, globalLogger) + val scriptedRunners = runner.batchScriptedRunner(scriptedTests, instances) + val parallelRunners = scriptedRunners.toParArray + // Using this deprecated value for 2.11 support + val pool = new scala.concurrent.forkjoin.ForkJoinPool(instances) + parallelRunners.tasksupport = new scala.collection.parallel.ForkJoinTaskSupport(pool) + runAllInParallel(parallelRunners) + globalLogger.info(s"Log files can be found at ${logsDir.getAbsolutePath}") + } + + private val nl = IO.Newline + private val nlt = nl + "\t" + class ScriptedFailure(tests: Seq[String]) extends RuntimeException(tests.mkString(nlt, nlt, nl)) { + // We are not interested in the stack trace here, only the failing tests + override def fillInStackTrace = this + } + + private def reportErrors(errors: Seq[String]): Unit = + if (errors.nonEmpty) throw new ScriptedFailure(errors) else () + + def runAllInParallel(tests: ParSeq[TestRunner]): Unit = { + reportErrors(tests.flatMap(test => test.apply().flatten.toSeq).toList) + } + + def get(tests: Seq[String], baseDirectory: File, log: Logger): Seq[ScriptedTest] = + if (tests.isEmpty) listTests(baseDirectory, log) else parseTests(tests) + + def listTests(baseDirectory: File, log: Logger): Seq[ScriptedTest] = + (new ListTests(baseDirectory, _ => true, log)).listTests + + def parseTests(in: Seq[String]): Seq[ScriptedTest] = for (testString <- in) yield { + testString.split("/").map(_.trim) match { + case Array(group, name) => ScriptedTest(group, name) + case elems => + sys.error(s"Expected two arguments 'group/name', obtained ${elems.mkString("/")}") } } + + private[sbt] def newLogger: Logger = sbt.internal.util.ConsoleLogger() + + private[this] val random = new java.util.Random() + private[sbt] def newScriptedLogsDir: File = { + val randomName = "scripted-logs-" + java.lang.Integer.toHexString(random.nextInt) + val logsDir = new File(IO.temporaryDirectory, randomName) + IO.createDirectory(logsDir) + logsDir + } } diff --git a/internal/zinc-scripted/src/test/scala/sbt/internal/inc/ScriptedTests.scala b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/ScriptedTests.scala new file mode 100644 index 0000000000..1af4282af8 --- /dev/null +++ b/internal/zinc-scripted/src/test/scala/sbt/internal/inc/ScriptedTests.scala @@ -0,0 +1,226 @@ +package sbt.internal.inc + +import java.io.File +import java.util.concurrent.atomic.AtomicInteger + +import sbt.internal.scripted._ +import sbt.io.IO +import sbt.io.FileFilter._ +import sbt.internal.io.Resources +import sbt.internal.util.{ ConsoleAppender, ConsoleOut, ManagedLogger } +import sbt.util.{ Level, LogExchange } + +final class ScriptedTests(resourceBaseDirectory: File, + bufferLog: Boolean, + handlersProvider: HandlersProvider, + logsDir: File) { + import sbt.io.syntax._ + import ScriptedTests._ + + private[this] val batchIdGenerator: AtomicInteger = new AtomicInteger + private[this] val runIdGenerator: AtomicInteger = new AtomicInteger + + final val ScriptFilename = "test" + final val PendingScriptFilename = "pending" + private val testResources = new Resources(resourceBaseDirectory) + + private def createScriptedHandlers( + label: String, + testDir: File, + logger: ManagedLogger + ): Map[Char, StatementHandler] = { + val scriptConfig = new ScriptConfig(label, testDir, logger) + handlersProvider.getHandlers(scriptConfig) + } + + /** Returns a sequence of test runners that have to be applied in the call site. */ + def batchScriptedRunner( + testGroupAndNames: Seq[ScriptedTest], + sbtInstances: Int + ): Seq[TestRunner] = { + // Test group and names may be file filters (like '*') + val groupAndNameDirs = { + for { + ScriptedTest(group, name) <- testGroupAndNames + groupDir <- resourceBaseDirectory.*(group).get + testDir <- groupDir.*(name).get + } yield (groupDir, testDir) + } + + val labelsAndDirs = groupAndNameDirs.map { + case (groupDir, nameDir) => + val groupName = groupDir.getName + val testName = nameDir.getName + val testDirectory = testResources.readOnlyResourceDirectory(groupName, testName) + (groupName, testName) -> testDirectory + } + + if (labelsAndDirs.isEmpty) List() + else { + val batchSeed = labelsAndDirs.size / sbtInstances + val batchSize = if (batchSeed == 0) labelsAndDirs.size else batchSeed + labelsAndDirs + .grouped(batchSize) + .map(batch => () => IO.withTemporaryDirectory(runBatchedTests(batch, _))) + .toList + } + } + + def createScriptedLogFile(loggerName: String): File = { + val name = s"$loggerName-${runIdGenerator.incrementAndGet}.log" + val logFile = logsDir./(name) + logFile.createNewFile() + logFile + } + + import sbt.internal.util.BufferedAppender + case class ScriptedLogger(log: ManagedLogger, logFile: File, buffer: BufferedAppender) + + private val BufferSize = 8192 // copied from IO since it's private + def rebindLogger(logger: ManagedLogger, logFile: File): ScriptedLogger = { + // Create buffered logger to a file that we will afterwards use. + import java.io.{ BufferedWriter, FileWriter } + val name = logger.name + val writer = new BufferedWriter(new FileWriter(logFile), BufferSize) + val fileOut = ConsoleOut.bufferedWriterOut(writer) + val fileAppender = ConsoleAppender(name, fileOut, useFormat = false) + val outAppender = BufferedAppender(ConsoleAppender()) + val appenders = (fileAppender -> Level.Debug) :: (outAppender -> Level.Debug) :: Nil + LogExchange.unbindLoggerAppenders(name) + LogExchange.bindLoggerAppenders(name, appenders) + ScriptedLogger(logger, logFile, outAppender) + } + + private final def createBatchLogger(name: String): ManagedLogger = LogExchange.logger(name) + + /** Defines the batch execution of scripted tests. + * + * Scripted tests are run one after the other one recycling the handlers, under + * the assumption that handlers do not produce side effects that can change scripted + * tests' behaviours. + * + * In batch mode, the test runner performs these operations between executions: + * + * 1. Delete previous test files in the common test directory. + * 2. Copy over next test files to the common test directory. + * 3. Reload the sbt handler. + * + * @param groupedTests The labels and directories of the tests to run. + * @param batchTmpDir The common test directory. + * @param log The logger. + */ + private def runBatchedTests( + groupedTests: Seq[((String, String), File)], + batchTmpDir: File + ): Seq[Option[String]] = { + val runner = new BatchScriptRunner + val batchId = s"initial-batch-${batchIdGenerator.incrementAndGet()}" + val batchLogger = createBatchLogger(batchId) + val handlers = createScriptedHandlers(batchId, batchTmpDir, batchLogger) + val states = new BatchScriptRunner.States + val seqHandlers = handlers.values.toList + runner.initStates(states, seqHandlers) + + def runBatchTests = { + groupedTests.map { + case ((group, name), originalDir) => + val label = s"$group / $name" + val loggerName = s"scripted-$group-$name.log" + val logFile = createScriptedLogFile(loggerName) + val logger = rebindLogger(batchLogger, logFile) + + println(s"Running $label") + // Copy test's contents + IO.copyDirectory(originalDir, batchTmpDir) + + // Reset the state of `IncHandler` between every scripted run + runner.cleanUpHandlers(seqHandlers, states) + runner.initStates(states, seqHandlers) + + // Run the test and delete files (except global that holds local scala jars) + val runTest = () => commonRunTest(label, batchTmpDir, handlers, runner, states, logger) + val result = runOrHandleDisabled(label, batchTmpDir, runTest, logger) + IO.delete(batchTmpDir.*("*" -- "global").get) + result + } + } + + try runBatchTests + finally runner.cleanUpHandlers(seqHandlers, states) + } + + private def runOrHandleDisabled( + label: String, + testDirectory: File, + runTest: () => Option[String], + logger: ScriptedLogger + ): Option[String] = { + val existsDisabled = new File(testDirectory, "disabled").isFile + if (!existsDisabled) runTest() + else { + logger.log.info(s"D $label [DISABLED]") + None + } + } + + import BatchScriptRunner.PreciseScriptedError + private val SuccessMark = s"${Console.GREEN + Console.BOLD}+${Console.RESET}" + private val FailureMark = s"${Console.RED + Console.BOLD}x${Console.RESET}" + private val PendingLabel = "[PENDING]" + private def commonRunTest( + label: String, + testDirectory: File, + handlers: Map[Char, StatementHandler], + runner: BatchScriptRunner, + states: BatchScriptRunner.States, + scriptedLogger: ScriptedLogger + ): Option[String] = { + val ScriptedLogger(logger, _, buffer) = scriptedLogger + if (bufferLog) buffer.record() + + val (file, pending) = { + val normal = new File(testDirectory, ScriptFilename) + val pending = new File(testDirectory, PendingScriptFilename) + if (pending.isFile) (pending, true) else (normal, false) + } + + def testFailed(t: Throwable): Option[String] = { + if (pending) { + logger.trace(t) + buffer.clearBuffer() + logger.error(s"$FailureMark $label $PendingLabel") + None + } else { + logger.error(s"$FailureMark $label") + logger.trace(t) + Some(label) + } + } + + import scala.util.control.Exception.catching + catching(classOf[PreciseScriptedError]) + .withApply(testFailed) + .andFinally(buffer.stopBuffer()) + .apply { + val parser = new TestScriptParser(handlers) + val handlersAndStatements = parser.parse(file) + runner.apply(handlersAndStatements, states) + + // Handle successful tests + if (bufferLog) buffer.clearBuffer() + if (pending) { + logger.info(s"$SuccessMark $label $PendingLabel") + logger.error(s" -> Pending test $label passed. Mark as passing to remove this failure.") + Some(label) + } else { + logger.info(s"$SuccessMark $label") + None + } + } + } +} + +object ScriptedTests { + type TestRunner = () => Seq[Option[String]] + val emptyCallback: File => Unit = _ => () +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 729bca6115..6bca5b05a2 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -5,6 +5,7 @@ object Dependencies { val scala210 = "2.10.6" val scala211 = "2.11.11" val scala212 = "2.12.3" + val scala213 = "2.13.0-M2" private val ioVersion = "1.0.0" private val utilVersion = "1.0.0" diff --git a/project/Header.scala b/project/Header.scala index 35e0c7881d..b6daaa577a 100644 --- a/project/Header.scala +++ b/project/Header.scala @@ -1,27 +1,25 @@ -// import sbt._ -// import Keys._ +import sbt._ +import Keys._ -// import de.heikoseeberger.sbtheader.{ HeaderPlugin, HeaderPattern } -// import HeaderPlugin.autoImport._ +import de.heikoseeberger.sbtheader.HeaderPlugin +import de.heikoseeberger.sbtheader.HeaderPlugin.{autoImport => SbtHeaderKeys} -// object CustomHeaderPlugin extends AutoPlugin { -// override def requires = plugins.JvmPlugin && HeaderPlugin -// override def trigger = allRequirements +object CustomHeaderPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin && HeaderPlugin + override def trigger = allRequirements + import SbtHeaderKeys.{HeaderFileType, HeaderCommentStyle, HeaderLicense} -// override def projectSettings = Seq( -// headers := Map( -// "scala" -> (HeaderPattern.cStyleBlockComment, copyrightText), -// "java" -> (HeaderPattern.cStyleBlockComment, copyrightText) -// ) -// ) - -// val copyrightText = -// """|/* -// | * 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. -// | */ -// | -// |""".stripMargin -// } + override def projectSettings = Seq( + SbtHeaderKeys.headerMappings ++= Map( + HeaderFileType.scala -> HeaderCommentStyle.CStyleBlockComment, + HeaderFileType.java -> HeaderCommentStyle.CStyleBlockComment + ), + SbtHeaderKeys.headerLicense := Some(HeaderLicense.Custom( + """|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. + |""".stripMargin + )) + ) +} diff --git a/project/Scripted.scala b/project/Scripted.scala index a2df1d7bc6..d018b64b69 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -75,6 +75,8 @@ object Scripted { bufferLog: Boolean, prescripted: File => Unit): Unit = { System.err.println(s"About to run tests: ${args.mkString("\n * ", "\n * ", "\n")}") + // Force Log4J to not use a thread context classloader otherwise it throws a CCE + sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" val noJLine = new classpath.FilteredLoader(scriptedSbtInstance.loader, "jline." :: Nil) val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, noJLine) val bridgeClass = Class.forName("sbt.internal.inc.IncScriptedRunner", true, loader) diff --git a/project/build.properties b/project/build.properties index b7dd3cb2ae..9abea1294a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.2 +sbt.version=1.0.3 diff --git a/project/plugins.sbt b/project/plugins.sbt index cc0ea20209..c6926192a9 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,8 +1,8 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.27") addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0-M1") -addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.2.0") -// addSbtPlugin("de.heikoseeberger" % "sbt-header" % "1.7.0") +addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.12") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.3") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0") addSbtPlugin("com.thesamet" % "sbt-protoc" % "0.99.12-rc5") diff --git a/zinc/src/main/scala/sbt/internal/inc/MixedAnalyzingCompiler.scala b/zinc/src/main/scala/sbt/internal/inc/MixedAnalyzingCompiler.scala index 31b55cc7d4..e7e862c775 100644 --- a/zinc/src/main/scala/sbt/internal/inc/MixedAnalyzingCompiler.scala +++ b/zinc/src/main/scala/sbt/internal/inc/MixedAnalyzingCompiler.scala @@ -11,6 +11,7 @@ package inc import java.io.File import java.lang.ref.{ Reference, SoftReference } +import java.nio.file.Files import java.util.Optional import inc.javac.AnalyzingJavaCompiler @@ -20,6 +21,7 @@ import xsbti.compile._ import sbt.io.IO import sbt.util.{ InterfaceUtil, Logger } import sbt.internal.inc.JavaInterfaceUtil.EnrichOption +import sbt.internal.inc.caching.ClasspathCache import xsbti.compile.ClassFileManager /** An instance of an analyzing compiler that can run both javac + scalac. */ @@ -184,7 +186,7 @@ object MixedAnalyzingCompiler { val lookup = incrementalCompilerOptions.externalHooks().getExternalLookup def doHash: Array[FileHash] = - classpath.map(x => FileHash.of(x, Stamper.forHash(x).hashCode))(collection.breakOut) + ClasspathCache.hashClasspath(classpath) val classpathHash = if (lookup.isPresent) { diff --git a/zinc/src/main/scala/sbt/internal/inc/caching/ClasspathCache.scala b/zinc/src/main/scala/sbt/internal/inc/caching/ClasspathCache.scala new file mode 100644 index 0000000000..cda89f31e0 --- /dev/null +++ b/zinc/src/main/scala/sbt/internal/inc/caching/ClasspathCache.scala @@ -0,0 +1,43 @@ +package sbt.internal.inc.caching + +import java.io.File +import java.nio.file.Files +import java.util.concurrent.ConcurrentHashMap +import java.nio.file.attribute.{ BasicFileAttributes, FileTime } + +import xsbti.compile.FileHash +import sbt.internal.inc.{ EmptyStamp, Stamper } + +object ClasspathCache { + // For more safety, store both the time and size + private type JarMetadata = (FileTime, Long) + private[this] val cacheMetadataJar = new ConcurrentHashMap[File, (JarMetadata, FileHash)]() + private[this] final val emptyStampCode = EmptyStamp.hashCode() + private def emptyFileHash(file: File) = FileHash.of(file, emptyStampCode) + private def genFileHash(file: File, metadata: JarMetadata): FileHash = { + val newHash = FileHash.of(file, Stamper.forHash(file).hashCode()) + cacheMetadataJar.put(file, (metadata, newHash)) + newHash + } + + def hashClasspath(classpath: Seq[File]): Array[FileHash] = { + // #433: Cache jars with their metadata to avoid recomputing hashes transitively in other projects + def fromCacheOrHash(file: File): FileHash = { + if (!file.exists()) emptyFileHash(file) + else { + // `readAttributes` needs to be guarded by `file.exists()`, otherwise it fails + val attrs = Files.readAttributes(file.toPath, classOf[BasicFileAttributes]) + if (attrs.isDirectory) emptyFileHash(file) + else { + val currentMetadata = (attrs.lastModifiedTime(), attrs.size()) + Option(cacheMetadataJar.get(file)) match { + case Some((metadata, hashHit)) if metadata == currentMetadata => hashHit + case None => genFileHash(file, currentMetadata) + } + } + } + } + + classpath.toParArray.map(fromCacheOrHash).toArray + } +} diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_210/app/Hello.scala b/zinc/src/sbt-test/bridge-check/bridgecheck_210/app/Hello.scala similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_210/app/Hello.scala rename to zinc/src/sbt-test/bridge-check/bridgecheck_210/app/Hello.scala diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_210/build.json b/zinc/src/sbt-test/bridge-check/bridgecheck_210/build.json similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_210/build.json rename to zinc/src/sbt-test/bridge-check/bridgecheck_210/build.json diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_210/test b/zinc/src/sbt-test/bridge-check/bridgecheck_210/test similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_210/test rename to zinc/src/sbt-test/bridge-check/bridgecheck_210/test diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_211/app/Hello.scala b/zinc/src/sbt-test/bridge-check/bridgecheck_211/app/Hello.scala similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_211/app/Hello.scala rename to zinc/src/sbt-test/bridge-check/bridgecheck_211/app/Hello.scala diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_211/build.json b/zinc/src/sbt-test/bridge-check/bridgecheck_211/build.json similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_211/build.json rename to zinc/src/sbt-test/bridge-check/bridgecheck_211/build.json diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_211/test b/zinc/src/sbt-test/bridge-check/bridgecheck_211/test similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_211/test rename to zinc/src/sbt-test/bridge-check/bridgecheck_211/test diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_212/app/Hello.scala b/zinc/src/sbt-test/bridge-check/bridgecheck_212/app/Hello.scala similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_212/app/Hello.scala rename to zinc/src/sbt-test/bridge-check/bridgecheck_212/app/Hello.scala diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_212/build.json b/zinc/src/sbt-test/bridge-check/bridgecheck_212/build.json similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_212/build.json rename to zinc/src/sbt-test/bridge-check/bridgecheck_212/build.json diff --git a/zinc/src/sbt-test/source-dependencies/bridgecheck_212/test b/zinc/src/sbt-test/bridge-check/bridgecheck_212/test similarity index 100% rename from zinc/src/sbt-test/source-dependencies/bridgecheck_212/test rename to zinc/src/sbt-test/bridge-check/bridgecheck_212/test diff --git a/zinc/src/sbt-test/bridge-check/bridgecheck_213/app/Hello.scala b/zinc/src/sbt-test/bridge-check/bridgecheck_213/app/Hello.scala new file mode 100644 index 0000000000..1fab48bfb0 --- /dev/null +++ b/zinc/src/sbt-test/bridge-check/bridgecheck_213/app/Hello.scala @@ -0,0 +1,5 @@ +package example + +object Hello extends App { + println("hello") +} diff --git a/zinc/src/sbt-test/bridge-check/bridgecheck_213/build.json b/zinc/src/sbt-test/bridge-check/bridgecheck_213/build.json new file mode 100644 index 0000000000..573c72232c --- /dev/null +++ b/zinc/src/sbt-test/bridge-check/bridgecheck_213/build.json @@ -0,0 +1,8 @@ +{ + "projects": [ + { + "name": "app", + "scalaVersion": "2.13.0-M2" + } + ] +} diff --git a/zinc/src/sbt-test/bridge-check/bridgecheck_213/test b/zinc/src/sbt-test/bridge-check/bridgecheck_213/test new file mode 100644 index 0000000000..19aca297fd --- /dev/null +++ b/zinc/src/sbt-test/bridge-check/bridgecheck_213/test @@ -0,0 +1 @@ +> app/compile diff --git a/zinc/src/sbt-test/source-dependencies/constants/pending b/zinc/src/sbt-test/source-dependencies/constants/pending index eebce96e43..7a5ae58795 100644 --- a/zinc/src/sbt-test/source-dependencies/constants/pending +++ b/zinc/src/sbt-test/source-dependencies/constants/pending @@ -1,5 +1,4 @@ # Marked as pending, see https://github.com/sbt/sbt/issues/1543 -# # Tests if source dependencies are tracked properly # for compile-time constants (like final vals in top-level objects) # see https://issues.scala-lang.org/browse/SI-7173 for details diff --git a/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java new file mode 100644 index 0000000000..413bb68d59 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/Example.java @@ -0,0 +1,26 @@ +package typeparameters; + +import java.util.function.Supplier; + +public class Example { + + static void call() { + Supplier> blah = () -> + new BaseBlah() { + @Override + protected O getResponseInternal(I i) { + return null; + } + }; + } + + public static void main(String[] args) { + Example.call(); + } +} + +abstract class BaseBlah { + protected O getResponseInternal(I i) { + return null; + } +} \ No newline at end of file diff --git a/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test new file mode 100644 index 0000000000..73a68203f3 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/java-lambda-typeparams/test @@ -0,0 +1 @@ +> compile \ No newline at end of file diff --git a/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Bar.scala b/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Bar.scala new file mode 100644 index 0000000000..5553bbb07e --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Bar.scala @@ -0,0 +1,6 @@ +package foo + +// This class is used to pad the number of source code. +class Bar { + def bar: Unit = ??? +} diff --git a/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Foo.scala b/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Foo.scala new file mode 100644 index 0000000000..0941e4e3f4 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/patMat-scope/src/main/scala/foo/Foo.scala @@ -0,0 +1,6 @@ +package foo + +// This class is used to pad the number of source code. +class Foo { + def foo: Unit = ??? +} diff --git a/zinc/src/sbt-test/source-dependencies/sealed/incOptions.properties b/zinc/src/sbt-test/source-dependencies/sealed/incOptions.properties new file mode 100644 index 0000000000..26949ab5dd --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/sealed/incOptions.properties @@ -0,0 +1 @@ +scalac.options = -Xfatal-warnings diff --git a/zinc/src/sbt-test/source-dependencies/sealed/test b/zinc/src/sbt-test/source-dependencies/sealed/test index 412e495308..7253703737 100644 --- a/zinc/src/sbt-test/source-dependencies/sealed/test +++ b/zinc/src/sbt-test/source-dependencies/sealed/test @@ -5,5 +5,4 @@ $ copy-file changes/A.scala A.scala # D.scala needs recompiling because the pattern match in D # is no longer exhaustive, which emits a warning -> checkRecompilations 1 A B C E -> checkRecompilations 2 D +-> compile diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/build.json b/zinc/src/sbt-test/source-dependencies/trait-trait-211/build.json new file mode 100644 index 0000000000..e2b0035d3b --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/build.json @@ -0,0 +1,8 @@ +{ + "projects": [ + { + "name": "mirtest", + "scalaVersion": "2.11.8" + } + ] +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/changes/A1.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-211/changes/A1.scala new file mode 100644 index 0000000000..a68e162077 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/changes/A1.scala @@ -0,0 +1,12 @@ +package gg +package table + +trait A { + def transform: Unit = { + // the use site is updated + buildNonemptyObjects(0, 1) + } + + // add extra parameter here + def buildNonemptyObjects(a: Int, b: Int): Unit = () +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/A.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/A.scala new file mode 100644 index 0000000000..5414fa1748 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/A.scala @@ -0,0 +1,10 @@ +package gg +package table + +trait A { + def transform: Unit = { + buildNonemptyObjects(0) + } + + def buildNonemptyObjects(a: Int): Unit = () +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/B.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/B.scala new file mode 100644 index 0000000000..3378a548af --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/B.scala @@ -0,0 +1,9 @@ +package xx + +import gg.table._ + +trait C extends B { +} + +trait B extends A { +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/Hello.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/Hello.scala new file mode 100644 index 0000000000..c416916e23 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/Hello.scala @@ -0,0 +1,8 @@ +package xx + +object Hello extends App { + val consumer = new Foo + consumer.transform +} + +class Foo extends C diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/incOptions.properties b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/incOptions.properties new file mode 100644 index 0000000000..adfc92c361 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/mirtest/incOptions.properties @@ -0,0 +1 @@ +relationsDebug = true diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-211/test b/zinc/src/sbt-test/source-dependencies/trait-trait-211/test new file mode 100644 index 0000000000..fe0b9a28b2 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-211/test @@ -0,0 +1,5 @@ +> mirtest/run + +## After copying the Good implementation, we should be able to run successfully. +$ copy-file changes/A1.scala mirtest/A.scala +> mirtest/run diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/build.json b/zinc/src/sbt-test/source-dependencies/trait-trait-212/build.json new file mode 100644 index 0000000000..c925de7e82 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/build.json @@ -0,0 +1,8 @@ +{ + "projects": [ + { + "name": "mirtest", + "scalaVersion": "2.12.3" + } + ] +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/changes/A1.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-212/changes/A1.scala new file mode 100644 index 0000000000..a68e162077 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/changes/A1.scala @@ -0,0 +1,12 @@ +package gg +package table + +trait A { + def transform: Unit = { + // the use site is updated + buildNonemptyObjects(0, 1) + } + + // add extra parameter here + def buildNonemptyObjects(a: Int, b: Int): Unit = () +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/A.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/A.scala new file mode 100644 index 0000000000..5414fa1748 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/A.scala @@ -0,0 +1,10 @@ +package gg +package table + +trait A { + def transform: Unit = { + buildNonemptyObjects(0) + } + + def buildNonemptyObjects(a: Int): Unit = () +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/B.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/B.scala new file mode 100644 index 0000000000..3378a548af --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/B.scala @@ -0,0 +1,9 @@ +package xx + +import gg.table._ + +trait C extends B { +} + +trait B extends A { +} diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/Hello.scala b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/Hello.scala new file mode 100644 index 0000000000..c416916e23 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/Hello.scala @@ -0,0 +1,8 @@ +package xx + +object Hello extends App { + val consumer = new Foo + consumer.transform +} + +class Foo extends C diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/incOptions.properties b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/incOptions.properties new file mode 100644 index 0000000000..adfc92c361 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/mirtest/incOptions.properties @@ -0,0 +1 @@ +relationsDebug = true diff --git a/zinc/src/sbt-test/source-dependencies/trait-trait-212/test b/zinc/src/sbt-test/source-dependencies/trait-trait-212/test new file mode 100644 index 0000000000..fe0b9a28b2 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/trait-trait-212/test @@ -0,0 +1,5 @@ +> mirtest/run + +## After copying the Good implementation, we should be able to run successfully. +$ copy-file changes/A1.scala mirtest/A.scala +> mirtest/run diff --git a/zinc/src/sbt-test/source-dependencies/transitive-class/A.scala b/zinc/src/sbt-test/source-dependencies/transitive-class/A.scala new file mode 100644 index 0000000000..68c5796a7c --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/transitive-class/A.scala @@ -0,0 +1 @@ +abstract class A diff --git a/zinc/src/sbt-test/source-dependencies/transitive-class/BC.scala b/zinc/src/sbt-test/source-dependencies/transitive-class/BC.scala new file mode 100644 index 0000000000..150a8f195a --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/transitive-class/BC.scala @@ -0,0 +1,2 @@ +class B extends A +class C extends B diff --git a/zinc/src/sbt-test/source-dependencies/transitive-class/D.scala b/zinc/src/sbt-test/source-dependencies/transitive-class/D.scala new file mode 100644 index 0000000000..08ebf2e960 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/transitive-class/D.scala @@ -0,0 +1,4 @@ +class D extends C +object Hello extends App { + new D +} diff --git a/zinc/src/sbt-test/source-dependencies/transitive-class/changes/A.scala b/zinc/src/sbt-test/source-dependencies/transitive-class/changes/A.scala new file mode 100644 index 0000000000..a02e9e5db4 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/transitive-class/changes/A.scala @@ -0,0 +1,3 @@ +abstract class A { + def foo: String = "" +} diff --git a/zinc/src/sbt-test/source-dependencies/transitive-class/test b/zinc/src/sbt-test/source-dependencies/transitive-class/test new file mode 100644 index 0000000000..b4847740f4 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/transitive-class/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/A.scala A.scala +> checkRecompilations 2 A B C D Hello diff --git a/zinc/src/sbt-test/source-dependencies/unexpanded-names/Foo.scala b/zinc/src/sbt-test/source-dependencies/unexpanded-names/Foo.scala new file mode 100644 index 0000000000..1b99e3e20e --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/unexpanded-names/Foo.scala @@ -0,0 +1,19 @@ +class Fooo { + // This one is problematic because of expanded names + private[Fooo] object Bar +} + +package issue127 { + class Foo { + private[Foo] object Bar + class Baz { + private[Baz] object Bazz + } + } + + object Foo { + private[issue127] class Bippy + // This one is problematic because of expanded names + private[issue127] object Bippy + } +} diff --git a/zinc/src/sbt-test/source-dependencies/unexpanded-names/test b/zinc/src/sbt-test/source-dependencies/unexpanded-names/test new file mode 100644 index 0000000000..e3d1046bf1 --- /dev/null +++ b/zinc/src/sbt-test/source-dependencies/unexpanded-names/test @@ -0,0 +1,2 @@ +# See https://github.com/sbt/zinc/issues/127 +> compile \ No newline at end of file diff --git a/zinc/src/sbt-test/source-dependencies/value-class-underlying/C.scala b/zinc/src/sbt-test/source-dependencies/value-class-underlying/C.scala index 0c09ea764f..9a6a975330 100644 --- a/zinc/src/sbt-test/source-dependencies/value-class-underlying/C.scala +++ b/zinc/src/sbt-test/source-dependencies/value-class-underlying/C.scala @@ -1,6 +1,6 @@ object C { def main(args: Array[String]): Unit = { - val x = B.foo - println("x: " + x) // Need to use x in an expression to see if it crashes or not + val duck = B.foo + println("duck: " + duck) // Need to use duck in an expression to see if it crashes or not } } diff --git a/zinc/src/test/scala/sbt/inc/cached/CachedHashingSpec.scala b/zinc/src/test/scala/sbt/inc/cached/CachedHashingSpec.scala new file mode 100644 index 0000000000..f66c99d796 --- /dev/null +++ b/zinc/src/test/scala/sbt/inc/cached/CachedHashingSpec.scala @@ -0,0 +1,65 @@ +/* + * 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.inc.cached + +import java.nio.file.Paths + +import sbt.inc.{ BaseCompilerSpec, SourceFiles } +import sbt.internal.inc.{ Analysis, CompileOutput, MixedAnalyzingCompiler } +import sbt.io.IO + +class CachedHashingSpec extends BaseCompilerSpec { + def timeMs[R](block: => R): Long = { + val t0 = System.nanoTime() + block // call-by-name + val t1 = System.nanoTime() + (t1 - t0) / 1000000 + } + + "zinc" should "cache jar generation" in { + IO.withTemporaryDirectory { tempDir => + val classes = Seq(SourceFiles.Good) + val sources0 = Map(Paths.get("src") -> classes.map(path => Paths.get(path))) + val projectSetup = ProjectSetup(tempDir.toPath(), sources0, Nil) + val compiler = projectSetup.createCompiler() + + import compiler.in.{ setup, options, compilers, previousResult } + import sbt.internal.inc.JavaInterfaceUtil._ + import sbt.io.syntax.{ file, fileToRichFile, singleFileFinder } + + val javac = compilers.javaTools.javac + val scalac = compilers.scalac + val giganticClasspath = file(sys.props("user.home"))./(".ivy2").**("*.jar").get.take(500) + + def genConfig = MixedAnalyzingCompiler.makeConfig( + scalac, + javac, + options.sources, + giganticClasspath, + CompileOutput(options.classesDirectory), + setup.cache, + setup.progress.toOption, + options.scalacOptions, + options.javacOptions, + Analysis.empty, + previousResult.setup.toOption, + setup.perClasspathEntryLookup, + setup.reporter, + options.order, + setup.skip, + setup.incrementalCompilerOptions, + setup.extra.toList.map(_.toScalaTuple) + ) + + val hashingTime = timeMs(genConfig) + val cachedHashingTime = timeMs(genConfig) + assert(cachedHashingTime < (hashingTime * 0.20), + s"Cache jar didn't work: $cachedHashingTime is >= than 20% of $hashingTime.") + } + } +}