mirror of https://github.com/sbt/sbt.git
Test `getScalaInstance` & `getCompiledBridge`
* Remove unused code in `BridgeProviderSpecification` and test the real code in the default provider. * Don't use temporary directory to download the Scala jars, retrive them in a directory specified by the user. * Use `java.net.ClassLoader` instead of `ScalaClassLoader`. * Use the `ScalaInstance` interface, not the implementation. Remove any reference to the implementation.
This commit is contained in:
parent
ae40c096e3
commit
f4c38c8355
|
|
@ -10,6 +10,7 @@ package internal
|
|||
package inc
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
import sbt.internal.inc.classpath.ClasspathUtilities
|
||||
import sbt.io.{ Hash, IO }
|
||||
|
|
@ -20,8 +21,6 @@ import sbt.librarymanagement.syntax._
|
|||
import sbt.util.{ InterfaceUtil, Logger }
|
||||
import xsbti.compile.CompilerBridgeProvider
|
||||
|
||||
import scala.reflect.internal.util.ScalaClassLoader.URLClassLoader
|
||||
|
||||
private[sbt] object ZincComponentCompiler {
|
||||
final val binSeparator = "-bin_"
|
||||
final val javaClassVersion = System.getProperty("java.class.version")
|
||||
|
|
@ -31,23 +30,57 @@ private[sbt] object ZincComponentCompiler {
|
|||
|
||||
private final val ZincVersionPropertyFile = "/incrementalcompiler.version.properties"
|
||||
private final val ZincVersionProperty = "version"
|
||||
final lazy val incrementalVersion: String = {
|
||||
private final lazy val incrementalVersion: String = {
|
||||
val cl = this.getClass.getClassLoader
|
||||
ResourceLoader.getPropertiesFor(ZincVersionPropertyFile, cl).getProperty(ZincVersionProperty)
|
||||
}
|
||||
|
||||
private class ZincCompilerBridgeProvider(manager: ZincComponentManager,
|
||||
ivyConfiguration: IvyConfiguration,
|
||||
bridgeModule: ModuleID)
|
||||
extends CompilerBridgeProvider {
|
||||
/** Defines the internal implementation of a bridge provider. */
|
||||
private class ZincCompilerBridgeProvider(
|
||||
manager: ZincComponentManager,
|
||||
ivyConfiguration: IvyConfiguration,
|
||||
scalaJarsTarget: File
|
||||
) extends CompilerBridgeProvider {
|
||||
|
||||
private val CompileConf = Some(Configurations.Compile.name)
|
||||
private def getDefaultBridgeModule(scalaVersion: String): ModuleID = {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
import xsbti.ArtifactInfo.SbtOrganization
|
||||
val bridgeId = compilerBridgeId(scalaVersion)
|
||||
ModuleID(SbtOrganization, bridgeId, incrementalVersion)
|
||||
.withConfigurations(CompileConf)
|
||||
.sources()
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a richer interface for Scala users that want to pass in an explicit module id.
|
||||
*
|
||||
* Note that this method cannot be defined in [[CompilerBridgeProvider]] because [[ModuleID]]
|
||||
* is a Scala-defined class to which the compiler bridge cannot depend on.
|
||||
*/
|
||||
def getCompiledBridge(bridgeSources: ModuleID,
|
||||
scalaInstance: xsbti.compile.ScalaInstance,
|
||||
logger: xsbti.Logger): File = {
|
||||
val autoClasspath = ClasspathOptionsUtil.auto
|
||||
val raw = new RawCompiler(scalaInstance, autoClasspath, logger)
|
||||
val zinc = new ZincComponentCompiler(raw, manager, ivyConfiguration, bridgeSources, logger)
|
||||
logger.debug(InterfaceUtil.f0(s"Getting $bridgeSources for Scala ${scalaInstance.version}"))
|
||||
zinc.getCompiledBridgeJar
|
||||
}
|
||||
|
||||
override def getCompiledBridge(scalaInstance: xsbti.compile.ScalaInstance,
|
||||
logger: xsbti.Logger): File = {
|
||||
val autoClasspath = ClasspathOptionsUtil.auto
|
||||
val raw = new RawCompiler(scalaInstance, autoClasspath, logger)
|
||||
val zinc = new ZincComponentCompiler(raw, manager, ivyConfiguration, bridgeModule, logger)
|
||||
logger.debug(InterfaceUtil.f0(s"Getting $bridgeModule for Scala ${scalaInstance.version}"))
|
||||
zinc.getCompiledBridgeJar
|
||||
val scalaVersion = scalaInstance.actualVersion()
|
||||
val bridgeSources = getDefaultBridgeModule(scalaVersion)
|
||||
getCompiledBridge(bridgeSources, scalaInstance, logger)
|
||||
}
|
||||
|
||||
private final case class ScalaArtifacts(compiler: File, library: File, others: Vector[File])
|
||||
|
|
@ -56,10 +89,9 @@ private[sbt] object ZincComponentCompiler {
|
|||
def isPrefixedWith(artifact: File, prefix: String) = artifact.getName.startsWith(prefix)
|
||||
|
||||
import xsbti.ArtifactInfo._
|
||||
import Configurations.Compile
|
||||
import UnresolvedWarning.unresolvedWarningLines
|
||||
val fullLogger = new FullLogger(logger)
|
||||
val CompileConf = Some(Compile.name)
|
||||
val CompileConf = Some(Configurations.Compile.name)
|
||||
val dummyModule = ModuleID(JsonUtil.sbtOrgTemp, s"tmp-scala-$scalaVersion", scalaVersion)
|
||||
val scalaLibrary = ModuleID(ScalaOrganization, ScalaLibraryID, scalaVersion)
|
||||
val scalaCompiler = ModuleID(ScalaOrganization, ScalaCompilerID, scalaVersion)
|
||||
|
|
@ -67,22 +99,20 @@ private[sbt] object ZincComponentCompiler {
|
|||
val wrapper = dummyModule.withConfigurations(CompileConf)
|
||||
val ivySbt: IvySbt = new IvySbt(ivyConfiguration)
|
||||
val ivyModule = ZincIvyActions.getModule(ivySbt, wrapper, dependencies)
|
||||
IO.withTemporaryDirectory { retrieveDirectory =>
|
||||
ZincIvyActions.update(ivyModule, retrieveDirectory, fullLogger) match {
|
||||
case Left(uw) =>
|
||||
val unresolvedLines = unresolvedWarningLines.showLines(uw)
|
||||
val unretrievedMessage = s"The Scala compiler and library could not be retrieved."
|
||||
throw new InvalidComponent(s"$unretrievedMessage\n$unresolvedLines")
|
||||
case Right(allArtifacts) =>
|
||||
val isScalaCompiler = (f: File) => isPrefixedWith(f, "scala-compiler-")
|
||||
val isScalaLibrary = (f: File) => isPrefixedWith(f, "scala-library-")
|
||||
val maybeScalaCompiler = allArtifacts.find(isScalaCompiler)
|
||||
val maybeScalaLibrary = allArtifacts.find(isScalaLibrary)
|
||||
val others = allArtifacts.filterNot(a => isScalaCompiler(a) || isScalaLibrary(a))
|
||||
val scalaCompiler = maybeScalaCompiler.getOrElse(throw MissingScalaJar.compiler)
|
||||
val scalaLibrary = maybeScalaLibrary.getOrElse(throw MissingScalaJar.library)
|
||||
ScalaArtifacts(scalaCompiler, scalaLibrary, others)
|
||||
}
|
||||
ZincIvyActions.update(ivyModule, scalaJarsTarget, noSource = true, fullLogger) match {
|
||||
case Left(uw) =>
|
||||
val unresolvedLines = unresolvedWarningLines.showLines(uw)
|
||||
val unretrievedMessage = s"The Scala compiler and library could not be retrieved."
|
||||
throw new InvalidComponent(s"$unretrievedMessage\n$unresolvedLines")
|
||||
case Right(allArtifacts) =>
|
||||
val isScalaCompiler = (f: File) => isPrefixedWith(f, "scala-compiler-")
|
||||
val isScalaLibrary = (f: File) => isPrefixedWith(f, "scala-library-")
|
||||
val maybeScalaCompiler = allArtifacts.find(isScalaCompiler)
|
||||
val maybeScalaLibrary = allArtifacts.find(isScalaLibrary)
|
||||
val others = allArtifacts.filterNot(a => isScalaCompiler(a) || isScalaLibrary(a))
|
||||
val scalaCompiler = maybeScalaCompiler.getOrElse(throw MissingScalaJar.compiler)
|
||||
val scalaLibrary = maybeScalaLibrary.getOrElse(throw MissingScalaJar.library)
|
||||
ScalaArtifacts(scalaCompiler, scalaLibrary, others)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +123,7 @@ private[sbt] object ZincComponentCompiler {
|
|||
val scalaCompiler = scalaArtifacts.compiler
|
||||
val scalaLibrary = scalaArtifacts.library
|
||||
val jarsToLoad = (scalaCompiler +: scalaLibrary +: scalaArtifacts.others).toArray
|
||||
assert(jarsToLoad.forall(_.exists), "One or more jar(s) in the Scala instance do not exist.")
|
||||
val loader = new URLClassLoader(toURLs(jarsToLoad), ClasspathUtilities.rootLoader)
|
||||
val properties = ResourceLoader.getSafePropertiesFor("compiler.properties", loader)
|
||||
val loaderVersion = Option(properties.getProperty("version.number"))
|
||||
|
|
@ -103,8 +134,8 @@ private[sbt] object ZincComponentCompiler {
|
|||
|
||||
def interfaceProvider(manager: ZincComponentManager,
|
||||
ivyConfiguration: IvyConfiguration,
|
||||
sourcesModule: ModuleID): CompilerBridgeProvider =
|
||||
new ZincCompilerBridgeProvider(manager, ivyConfiguration, sourcesModule)
|
||||
scalaJarsTarget: File): CompilerBridgeProvider =
|
||||
new ZincCompilerBridgeProvider(manager, ivyConfiguration, scalaJarsTarget)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +208,7 @@ private[inc] class ZincComponentCompiler(
|
|||
val target = new File(binaryDirectory, s"$compilerBridgeId.jar")
|
||||
buffered bufferQuietly {
|
||||
IO.withTemporaryDirectory { retrieveDirectory =>
|
||||
ZincIvyActions.update(ivyModuleForBridge, retrieveDirectory, buffered) match {
|
||||
ZincIvyActions.update(ivyModuleForBridge, retrieveDirectory, false, buffered) match {
|
||||
case Left(uw) =>
|
||||
val mod = bridgeSources.toString
|
||||
val unresolvedLines = unresolvedWarningLines.showLines(uw)
|
||||
|
|
@ -232,33 +263,34 @@ object ZincIvyActions {
|
|||
module.moduleSettings match {
|
||||
case ic: InlineConfiguration =>
|
||||
// Pretty print the module as `ModuleIDExtra.toStringImpl` does.
|
||||
val dependency =
|
||||
ic.dependencies.map(m => s"${m.organization}:${m.name}:${m.revision}").headOption
|
||||
dependency.getOrElse(sys.error("Fatal: more than one dependency in dummy bridge module."))
|
||||
ic.dependencies.map(m => s"${m.organization}:${m.name}:${m.revision}").mkString(", ")
|
||||
case _ => sys.error("Fatal: configuration to download was not inline.")
|
||||
}
|
||||
}
|
||||
|
||||
private final val warningConf = UnresolvedWarningConfiguration()
|
||||
private final val defaultRetrievePattern = Resolver.defaultRetrievePattern
|
||||
private def defaultUpdateConfiguration(retrieveDirectory: File): UpdateConfiguration = {
|
||||
val retrieve = RetrieveConfiguration(retrieveDirectory, defaultRetrievePattern, false, None)
|
||||
private def defaultUpdateConfiguration(targetDir: File, noSource: Boolean): UpdateConfiguration = {
|
||||
val retrieve = RetrieveConfiguration(targetDir, defaultRetrievePattern, false, None)
|
||||
val logLevel = UpdateLogging.DownloadOnly
|
||||
val noDocs = ArtifactTypeFilter.forbid(Set("doc"))
|
||||
UpdateConfiguration(Some(retrieve), missingOk = false, logLevel, noDocs)
|
||||
val defaultExcluded = Set("doc")
|
||||
val finalExcluded = if (noSource) defaultExcluded + "src" else defaultExcluded
|
||||
val artifactFilter = ArtifactTypeFilter.forbid(finalExcluded)
|
||||
UpdateConfiguration(Some(retrieve), missingOk = false, logLevel, artifactFilter)
|
||||
}
|
||||
|
||||
private[inc] def update(module: IvyModule,
|
||||
retrieveDirectory: File,
|
||||
noSource: Boolean = false,
|
||||
logger: Logger): Either[UnresolvedWarning, Vector[File]] = {
|
||||
import IvyActions.updateEither
|
||||
val updateConfiguration = defaultUpdateConfiguration(retrieveDirectory)
|
||||
val updateConfiguration = defaultUpdateConfiguration(retrieveDirectory, noSource)
|
||||
val dependencies = prettyPrintDependency(module)
|
||||
logger.info(s"Attempting to fetch $dependencies. This operation may fail.")
|
||||
logger.info(s"Attempting to fetch $dependencies.")
|
||||
val clockForCache = LogicalClock.unknown
|
||||
updateEither(module, updateConfiguration, warningConf, clockForCache, None, logger) match {
|
||||
case Left(unresolvedWarning) =>
|
||||
logger.debug(s"Couldn't retrieve module ${prettyPrintDependency(module)}.")
|
||||
logger.debug(s"Couldn't retrieve module(s) ${prettyPrintDependency(module)}.")
|
||||
Left(unresolvedWarning)
|
||||
|
||||
case Right(updateReport) =>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
package sbt.internal.inc
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.Properties
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
import sbt.internal.inc.classpath.ClasspathUtilities
|
||||
import sbt.internal.librarymanagement.JsonUtil
|
||||
import sbt.io.IO
|
||||
import sbt.io.syntax._
|
||||
import sbt.librarymanagement.{
|
||||
DefaultMavenRepository,
|
||||
FileRepository,
|
||||
ModuleID,
|
||||
Patterns,
|
||||
Resolver,
|
||||
UpdateOptions
|
||||
|
|
@ -25,8 +20,6 @@ import xsbti.{ ComponentProvider, GlobalLock }
|
|||
* Base class for test suites that must be able to fetch and compile the compiler bridge.
|
||||
*/
|
||||
abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
||||
// log.setLevel(Level.Warn)
|
||||
|
||||
def realLocal: Resolver = {
|
||||
val pList = Vector(s"$${user.home}/.ivy2/local/${Resolver.localBasePattern}")
|
||||
FileRepository(
|
||||
|
|
@ -34,6 +27,7 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
|||
Resolver.defaultFileConfiguration,
|
||||
Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false))
|
||||
}
|
||||
|
||||
override def resolvers: Vector[Resolver] = Vector(realLocal, DefaultMavenRepository)
|
||||
private val ivyConfiguration = mkIvyConfiguration(UpdateOptions())
|
||||
|
||||
|
|
@ -41,29 +35,15 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
|||
val target = file("target").getAbsoluteFile
|
||||
target / "zinc-components"
|
||||
}
|
||||
def secondaryCacheOpt: Option[File] = Some(secondaryCacheDirectory)
|
||||
|
||||
def compilerBridgeId(scalaVersion: String) = {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
def getZincProvider(targetDir: File, scalaVersion: String, log: Logger): CompilerBridgeProvider = {
|
||||
import xsbti.ArtifactInfo.SbtOrganization
|
||||
import ZincComponentCompiler.incrementalVersion
|
||||
val bridgeId = compilerBridgeId(scalaVersion)
|
||||
val sourceModule = ModuleID(SbtOrganization, bridgeId, incrementalVersion)
|
||||
.withConfigurations(Some("component"))
|
||||
.sources()
|
||||
val manager = new ZincComponentManager(lock, compProvider(targetDir), secondaryCacheOpt, log)
|
||||
ZincComponentCompiler.interfaceProvider(manager, ivyConfiguration, sourceModule)
|
||||
def getZincProvider(targetDir: File, log: Logger): CompilerBridgeProvider = {
|
||||
val secondaryCache = Some(secondaryCacheDirectory)
|
||||
val manager = new ZincComponentManager(lock, compProvider(targetDir), secondaryCache, log)
|
||||
ZincComponentCompiler.interfaceProvider(manager, ivyConfiguration, currentManaged)
|
||||
}
|
||||
|
||||
def getCompilerBridge(targetDir: File, log: Logger, scalaVersion: String): File = {
|
||||
val provider = getZincProvider(targetDir, scalaVersion, log)
|
||||
val provider = getZincProvider(targetDir, log)
|
||||
val scalaInstance = provider.getScalaInstance(scalaVersion, log)
|
||||
val bridge = provider.getCompiledBridge(scalaInstance, log)
|
||||
val target = targetDir / s"target-bridge-$scalaVersion.jar"
|
||||
|
|
@ -71,78 +51,11 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
|||
target
|
||||
}
|
||||
|
||||
def scalaInstance(scalaVersion: String): ScalaInstance = {
|
||||
val scalaModule = {
|
||||
val dummyModule = ModuleID(JsonUtil.sbtOrgTemp, "tmp-scala-" + scalaVersion, scalaVersion)
|
||||
.withConfigurations(Some("compile"))
|
||||
val scalaLibrary = ModuleID(xsbti.ArtifactInfo.ScalaOrganization,
|
||||
xsbti.ArtifactInfo.ScalaLibraryID,
|
||||
scalaVersion).withConfigurations(Some("compile"))
|
||||
val scalaCompiler = ModuleID(xsbti.ArtifactInfo.ScalaOrganization,
|
||||
xsbti.ArtifactInfo.ScalaCompilerID,
|
||||
scalaVersion).withConfigurations(Some("compile"))
|
||||
|
||||
module(dummyModule, Vector(scalaLibrary, scalaCompiler), None)
|
||||
}
|
||||
|
||||
val allArtifacts =
|
||||
for {
|
||||
conf <- ivyUpdate(scalaModule).configurations
|
||||
m <- conf.modules
|
||||
(_, f) <- m.artifacts
|
||||
} yield f
|
||||
|
||||
def isCompiler(f: File) = f.getName startsWith "scala-compiler-"
|
||||
def isLibrary(f: File) = f.getName startsWith "scala-library-"
|
||||
|
||||
val scalaCompilerJar = allArtifacts find isCompiler getOrElse (throw new RuntimeException(
|
||||
"Not found: scala-compiler"))
|
||||
val scalaLibraryJar = allArtifacts find isLibrary getOrElse (throw new RuntimeException(
|
||||
"Not found: scala-library"))
|
||||
val others = allArtifacts filterNot (a => isCompiler(a) || isLibrary(a))
|
||||
|
||||
scalaInstance(scalaCompilerJar, scalaLibraryJar, others)
|
||||
}
|
||||
|
||||
def scalaInstance(scalaCompiler: File,
|
||||
scalaLibrary: File,
|
||||
scalaExtra: Seq[File]): ScalaInstance = {
|
||||
val loader = scalaLoader(scalaLibrary +: scalaCompiler +: scalaExtra)
|
||||
val version = scalaVersion(loader)
|
||||
val allJars = (scalaLibrary +: scalaCompiler +: scalaExtra).toArray
|
||||
new ScalaInstance(version.getOrElse("unknown"),
|
||||
loader,
|
||||
scalaLibrary,
|
||||
scalaCompiler,
|
||||
allJars,
|
||||
version)
|
||||
}
|
||||
|
||||
def scalaLoader(jars: Seq[File]) =
|
||||
new URLClassLoader(sbt.io.Path.toURLs(jars), ClasspathUtilities.rootLoader)
|
||||
def scalaVersion(scalaLoader: ClassLoader): Option[String] =
|
||||
propertyFromResource("compiler.properties", "version.number", scalaLoader)
|
||||
|
||||
/**
|
||||
* Get a property from a properties file resource in the classloader.
|
||||
*/
|
||||
def propertyFromResource(resource: String,
|
||||
property: String,
|
||||
classLoader: ClassLoader): Option[String] = {
|
||||
val props = propertiesFromResource(resource, classLoader)
|
||||
Option(props.getProperty(property))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all properties from a properties file resource in the classloader.
|
||||
*/
|
||||
def propertiesFromResource(resource: String, classLoader: ClassLoader): Properties = {
|
||||
val props = new Properties
|
||||
val stream = classLoader.getResourceAsStream(resource)
|
||||
try { props.load(stream) } catch { case _: Exception => } finally {
|
||||
if (stream ne null) stream.close()
|
||||
}
|
||||
props
|
||||
def scalaInstance(scalaVersion: String,
|
||||
targetDir: File,
|
||||
logger: Logger): xsbti.compile.ScalaInstance = {
|
||||
val provider = getZincProvider(targetDir, logger)
|
||||
provider.getScalaInstance(scalaVersion, logger)
|
||||
}
|
||||
|
||||
private val lock: GlobalLock = new GlobalLock {
|
||||
|
|
@ -150,23 +63,16 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
|||
}
|
||||
|
||||
private def compProvider(targetDir: File): ComponentProvider = new ComponentProvider {
|
||||
|
||||
override def lockFile(): File = targetDir / "lock"
|
||||
|
||||
override def componentLocation(id: String): File =
|
||||
throw new UnsupportedOperationException
|
||||
override def component(componentID: String): Array[File] =
|
||||
IO.listFiles(targetDir / componentID)
|
||||
override def defineComponent(componentID: String, files: Array[File]): Unit =
|
||||
files foreach { f =>
|
||||
IO.copyFile(f, targetDir / componentID / f.getName)
|
||||
}
|
||||
|
||||
files.foreach(f => IO.copyFile(f, targetDir / componentID / f.getName))
|
||||
override def addToComponent(componentID: String, files: Array[File]): Boolean = {
|
||||
defineComponent(componentID, files)
|
||||
true
|
||||
}
|
||||
|
||||
override def component(componentID: String): Array[File] =
|
||||
IO.listFiles(targetDir / componentID)
|
||||
|
||||
override def componentLocation(id: String): File = throw new UnsupportedOperationException
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue