Add `getScalaInstance` to `CompilerBridgeProvider`

This commit is contained in:
jvican 2017-05-19 14:42:43 +02:00
parent d48f65a8a7
commit 62a5037d23
4 changed files with 142 additions and 69 deletions

View File

@ -0,0 +1,23 @@
/*
* 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
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) {
def this(msg: String) = this(msg, null)
}
final class MissingScalaJar(msg: String, cause: Throwable) extends RuntimeException(msg, cause) {
def this(msg: String) = this(msg, null)
}
object MissingScalaJar {
def missingTemplate(missing: String): String =
s"The $missing could not be found in your cache nor downloaded from the Internet."
def compiler: MissingScalaJar = new MissingScalaJar(missingTemplate("Scala compiler"))
def library: MissingScalaJar = new MissingScalaJar(missingTemplate("Scala library"))
}

View File

@ -1,12 +0,0 @@
/*
* 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
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) {
def this(msg: String) = this(msg, null)
}

View File

@ -17,4 +17,13 @@ private[inc] object ResourceLoader {
try { properties.load(propertiesStream) } finally { propertiesStream.close() }
properties
}
def getSafePropertiesFor(resource: String, classLoader: ClassLoader): Properties = {
val properties = new Properties
val propertiesStream = classLoader.getResourceAsStream(resource)
try { properties.load(propertiesStream) } catch { case _: Exception => } finally {
if (propertiesStream ne null) propertiesStream.close()
}
properties
}
}

View File

@ -11,32 +11,17 @@ package inc
import java.io.File
import sbt.internal.inc.classpath.ClasspathUtilities
import sbt.io.{ Hash, IO }
import sbt.internal.librarymanagement.{
InlineConfiguration,
IvyActions,
IvyConfiguration,
IvySbt,
JsonUtil,
LogicalClock,
RetrieveConfiguration,
UnresolvedWarning,
UnresolvedWarningConfiguration
}
import sbt.librarymanagement.{
ArtifactTypeFilter,
Configurations,
ModuleID,
ModuleInfo,
Resolver,
UpdateConfiguration,
UpdateLogging,
UpdateOptions
}
import sbt.internal.librarymanagement._
import sbt.internal.util.FullLogger
import sbt.librarymanagement._
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")
@ -59,11 +44,60 @@ private[sbt] object ZincComponentCompiler {
override def getCompiledBridge(scalaInstance: xsbti.compile.ScalaInstance,
logger: xsbti.Logger): File = {
val autoClasspath = ClasspathOptionsUtil.auto
val bridgeCompiler = new RawCompiler(scalaInstance, autoClasspath, logger)
val ivyComponent =
new ZincComponentCompiler(bridgeCompiler, manager, ivyConfiguration, bridgeModule, logger)
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}"))
ivyComponent.getCompiledBridgeJar
zinc.getCompiledBridgeJar
}
private final case class ScalaArtifacts(compiler: File, library: File, others: Vector[File])
private def getScalaArtifacts(scalaVersion: String, logger: xsbti.Logger): ScalaArtifacts = {
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 dummyModule = ModuleID(JsonUtil.sbtOrgTemp, s"tmp-scala-$scalaVersion", scalaVersion)
val scalaLibrary = ModuleID(ScalaOrganization, ScalaLibraryID, scalaVersion)
val scalaCompiler = ModuleID(ScalaOrganization, ScalaCompilerID, scalaVersion)
val dependencies = Vector(scalaLibrary, scalaCompiler).map(_.withConfigurations(CompileConf))
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)
}
}
}
override def getScalaInstance(scalaVersion: String,
logger: xsbti.Logger): xsbti.compile.ScalaInstance = {
import sbt.io.Path.toURLs
val scalaArtifacts = getScalaArtifacts(scalaVersion, logger)
val scalaCompiler = scalaArtifacts.compiler
val scalaLibrary = scalaArtifacts.library
val jarsToLoad = (scalaCompiler +: scalaLibrary +: scalaArtifacts.others).toArray
val loader = new URLClassLoader(toURLs(jarsToLoad), ClasspathUtilities.rootLoader)
val properties = ResourceLoader.getSafePropertiesFor("compiler.properties", loader)
val loaderVersion = Option(properties.getProperty("version.number"))
val scalaV = loaderVersion.getOrElse("unknown")
new ScalaInstance(scalaV, loader, scalaLibrary, scalaCompiler, jarsToLoad, loaderVersion)
}
}
@ -117,6 +151,19 @@ private[inc] class ZincComponentCompiler(
s"$id$binSeparator${scalaVersion}__$javaClassVersion"
}
/**
* Returns an ivy module that will wrap and download a given `moduleID`.
*
* @param moduleID The `moduleID` that needs to be wrapped in a dummy module to be downloaded.
*/
private[inc] def wrapDependencyInModule(moduleID: ModuleID): IvySbt#Module = {
import ZincComponentCompiler.{ sbtOrgTemp, modulePrefixTemp }
val sha1 = Hash.toHex(Hash(moduleID.name))
val dummyID = ModuleID(sbtOrgTemp, s"$modulePrefixTemp$sha1", moduleID.revision)
.withConfigurations(moduleID.configurations)
ZincIvyActions.getModule(ivySbt, dummyID, Vector(moduleID))
}
/**
* Resolves the compiler bridge sources, compiles them and installs the sbt component
* in the local filesystem to make sure that it's reused the next time is required.
@ -130,7 +177,7 @@ private[inc] class ZincComponentCompiler(
val target = new File(binaryDirectory, s"$compilerBridgeId.jar")
buffered bufferQuietly {
IO.withTemporaryDirectory { retrieveDirectory =>
update(ivyModuleForBridge, retrieveDirectory) match {
ZincIvyActions.update(ivyModuleForBridge, retrieveDirectory, buffered) match {
case Left(uw) =>
val mod = bridgeSources.toString
val unresolvedLines = unresolvedWarningLines.showLines(uw)
@ -149,34 +196,39 @@ private[inc] class ZincComponentCompiler(
}
}
/**
* Returns an ivy module that will wrap and download a given `moduleID`.
*
* @param moduleID The `moduleID` that needs to be wrapped in a dummy module to be downloaded.
*/
private def wrapDependencyInModule(moduleID: ModuleID): ivySbt.Module = {
def getModule(moduleID: ModuleID, deps: Vector[ModuleID], uo: UpdateOptions): ivySbt.Module = {
val moduleInfo = ModuleInfo(moduleID.name)
val componentIvySettings = InlineConfiguration(
validate = false,
ivyScala = None,
module = moduleID,
moduleInfo = moduleInfo,
dependencies = deps
).withConfigurations(Vector(Configurations.Component))
new ivySbt.Module(componentIvySettings)
}
}
import ZincComponentCompiler.{ sbtOrgTemp, modulePrefixTemp }
val sha1 = Hash.toHex(Hash(moduleID.name))
val dummyID = ModuleID(sbtOrgTemp, s"$modulePrefixTemp$sha1", moduleID.revision)
.withConfigurations(moduleID.configurations)
val defaultUpdateOptions = UpdateOptions()
getModule(dummyID, Vector(moduleID), defaultUpdateOptions)
object ZincIvyActions {
type IvyModule = IvySbt#Module
/** Define the default configurations for a Zinc module wrapper. */
private final val DefaultConfigurations: Vector[Configuration] =
Vector(Configurations.Component, Configurations.Compile)
private final val DefaultUpdateOptions = UpdateOptions()
/**
* Create a module from its dummy wrapper and its dependencies.
*
* @param wrapper The ModuleID wrapper that will download the dependencies.
* @param deps The dependencies to be downloaded.
* @return A fully-fledged ivy module to be used for [[IvyActions]].
*/
private[inc] def getModule(ivySbt: IvySbt,
wrapper: ModuleID,
deps: Vector[ModuleID]): IvyModule = {
val moduleInfo = ModuleInfo(wrapper.name)
val componentIvySettings = InlineConfiguration(
validate = false,
ivyScala = None,
module = wrapper,
moduleInfo = moduleInfo,
dependencies = deps
).withConfigurations(DefaultConfigurations)
new ivySbt.Module(componentIvySettings)
}
// The implementation of this method is linked to `wrapDependencyInModule`
private def prettyPrintDependency(module: ivySbt.Module): String = {
private def prettyPrintDependency(module: IvyModule): String = {
module.moduleSettings match {
case ic: InlineConfiguration =>
// Pretty print the module as `ModuleIDExtra.toStringImpl` does.
@ -196,22 +248,23 @@ private[inc] class ZincComponentCompiler(
UpdateConfiguration(Some(retrieve), missingOk = false, logLevel, noDocs)
}
private def update(module: ivySbt.Module,
retrieveDirectory: File): Either[UnresolvedWarning, Vector[File]] = {
private[inc] def update(module: IvyModule,
retrieveDirectory: File,
logger: Logger): Either[UnresolvedWarning, Vector[File]] = {
import IvyActions.updateEither
val updateConfiguration = defaultUpdateConfiguration(retrieveDirectory)
val dependencies = prettyPrintDependency(module)
buffered.info(s"Attempting to fetch $dependencies. This operation may fail.")
logger.info(s"Attempting to fetch $dependencies. This operation may fail.")
val clockForCache = LogicalClock.unknown
updateEither(module, updateConfiguration, warningConf, clockForCache, None, buffered) match {
updateEither(module, updateConfiguration, warningConf, clockForCache, None, logger) match {
case Left(unresolvedWarning) =>
buffered.debug(s"Couldn't retrieve module ${prettyPrintDependency(module)}.")
logger.debug(s"Couldn't retrieve module ${prettyPrintDependency(module)}.")
Left(unresolvedWarning)
case Right(updateReport) =>
val allFiles = updateReport.allFiles
buffered.debug(s"Files retrieved for ${prettyPrintDependency(module)}:")
buffered.debug(allFiles mkString ", ")
logger.debug(s"Files retrieved for ${prettyPrintDependency(module)}:")
logger.debug(allFiles mkString ", ")
Right(allFiles)
}
}