mirror of https://github.com/sbt/sbt.git
Put `util-interface` on classpath when compiling compiler bridge
Also, write tests to make sure that we remain able to compile the compiler bridge for different versions of Scala. Fixes sbt/incrementalcompiler#32
This commit is contained in:
parent
f2ee845280
commit
c62f8f4683
|
|
@ -47,8 +47,8 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: Componen
|
|||
import ComponentCompiler._
|
||||
|
||||
private val sbtOrg = xsbti.ArtifactInfo.SbtOrganization
|
||||
private val xsbtiInterfaceModuleName = "compiler-interface"
|
||||
private val xsbtiInterfaceID = s"interface-$incrementalVersion"
|
||||
// private val xsbtiInterfaceModuleName = "compiler-interface"
|
||||
// private val xsbtiInterfaceID = s"interface-$incrementalVersion"
|
||||
private val sbtOrgTemp = JsonUtil.sbtOrgTemp
|
||||
private val modulePrefixTemp = "temp-module-"
|
||||
private val ivySbt: IvySbt = new IvySbt(ivyConfiguration)
|
||||
|
|
@ -69,41 +69,26 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: Componen
|
|||
IO.withTemporaryDirectory { binaryDirectory =>
|
||||
|
||||
val targetJar = new File(binaryDirectory, s"$binID.jar")
|
||||
val xsbtiJars = manager.files(xsbtiInterfaceID)(new IfMissing.Define(true, getXsbtiInterface()))
|
||||
|
||||
buffered bufferQuietly {
|
||||
|
||||
IO.withTemporaryDirectory { retrieveDirectory =>
|
||||
(update(getModule(sourcesModule), retrieveDirectory)(_.getName endsWith "-sources.jar")) match {
|
||||
case Some(sources) =>
|
||||
|
||||
update(getModule(sourcesModule), retrieveDirectory) match {
|
||||
case Seq() =>
|
||||
throw new InvalidComponent(s"Couldn't retrieve source module: $sourcesModule")
|
||||
|
||||
case allArtifacts =>
|
||||
val (sources, xsbtiJars) = allArtifacts partition (_.getName endsWith "-sources.jar")
|
||||
AnalyzingCompiler.compileSources(sources, targetJar, xsbtiJars, sourcesModule.name, compiler, log)
|
||||
manager.define(binID, Seq(targetJar))
|
||||
|
||||
case None =>
|
||||
throw new InvalidComponent(s"Couldn't retrieve source module: $sourcesModule")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private def getXsbtiInterface(): Unit =
|
||||
buffered bufferQuietly {
|
||||
|
||||
IO withTemporaryDirectory { retrieveDirectory =>
|
||||
val module = ModuleID(sbtOrg, xsbtiInterfaceModuleName, incrementalVersion, Some("component"))
|
||||
val jarName = s"$xsbtiInterfaceModuleName-$incrementalVersion.jar"
|
||||
(update(getModule(module), retrieveDirectory)(_.getName == jarName)) match {
|
||||
case Some(interface) =>
|
||||
manager.define(xsbtiInterfaceID, interface)
|
||||
|
||||
case None =>
|
||||
throw new InvalidComponent(s"Couldn't retrieve xsbti interface module: $xsbtiInterfaceModuleName")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a dummy module that depends on `moduleID`.
|
||||
* Note: Sbt's implementation of Ivy requires us to do this, because only the dependencies
|
||||
|
|
@ -139,7 +124,7 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: Componen
|
|||
s"unknown"
|
||||
}
|
||||
|
||||
private def update(module: ivySbt.Module, retrieveDirectory: File)(predicate: File => Boolean): Option[Seq[File]] = {
|
||||
private def update(module: ivySbt.Module, retrieveDirectory: File): Seq[File] = {
|
||||
|
||||
val retrieveConfiguration = new RetrieveConfiguration(retrieveDirectory, Resolver.defaultRetrievePattern, false)
|
||||
val updateConfiguration = new UpdateConfiguration(Some(retrieveConfiguration), true, UpdateLogging.DownloadOnly)
|
||||
|
|
@ -148,7 +133,7 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: Componen
|
|||
IvyActions.updateEither(module, updateConfiguration, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, buffered) match {
|
||||
case Left(unresolvedWarning) =>
|
||||
buffered.debug(s"Couldn't retrieve module ${dependenciesNames(module)}.")
|
||||
None
|
||||
Nil
|
||||
|
||||
case Right(updateReport) =>
|
||||
val allFiles =
|
||||
|
|
@ -161,10 +146,7 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: Componen
|
|||
buffered.debug(s"Files retrieved for ${dependenciesNames(module)}:")
|
||||
buffered.debug(allFiles mkString ", ")
|
||||
|
||||
allFiles filter predicate match {
|
||||
case Seq() => None
|
||||
case files => Some(files)
|
||||
}
|
||||
allFiles
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
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.{ ComponentManager, IvySbt, BaseIvySpecification }
|
||||
import sbt.io.IO
|
||||
import sbt.io.Path._
|
||||
import sbt.librarymanagement.{ ModuleID, UpdateOptions, Resolver }
|
||||
import sbt.util.Logger
|
||||
import xsbti.{ ComponentProvider, GlobalLock }
|
||||
|
||||
abstract class BridgeProviderSpecification extends BaseIvySpecification {
|
||||
|
||||
override def resolvers: Seq[Resolver] = Seq(Resolver.mavenLocal, Resolver.jcenterRepo)
|
||||
val ivyConfiguration = mkIvyConfiguration(UpdateOptions())
|
||||
val ivySbt = new IvySbt(ivyConfiguration)
|
||||
|
||||
val home = new File(sys.props("user.home"))
|
||||
val ivyCache = home / ".ivy2" / "cache"
|
||||
|
||||
def getCompilerBridge(tempDir: File, log: Logger, scalaVersion: String): File = {
|
||||
val instance = scalaInstanceFromFile(scalaVersion)
|
||||
val bridgeId = compilerBridgeId(scalaVersion)
|
||||
val sourceModule = ModuleID(xsbti.ArtifactInfo.SbtOrganization, bridgeId, ComponentCompiler.incrementalVersion, Some("component")).sources()
|
||||
|
||||
val raw = new RawCompiler(instance, ClasspathOptions.auto, log)
|
||||
val manager = new ComponentManager(lock, provider(tempDir), None, log)
|
||||
val componentCompiler = new IvyComponentCompiler(raw, manager, ivyConfiguration, sourceModule, log)
|
||||
|
||||
val bridge = componentCompiler.apply()
|
||||
val target = tempDir / s"target-bridge-$scalaVersion.jar"
|
||||
IO.copyFile(bridge, target)
|
||||
target
|
||||
}
|
||||
|
||||
def scalaInstanceFromFile(scalaVersion: String): ScalaInstance =
|
||||
scalaInstance(
|
||||
ivyCache / "org.scala-lang" / "scala-compiler" / "jars" / s"scala-compiler-$scalaVersion.jar",
|
||||
ivyCache / "org.scala-lang" / "scala-library" / "jars" / s"scala-library-$scalaVersion.jar",
|
||||
Seq(ivyCache / "org.scala-lang" / "scala-reflect" / "jars" / s"scala-reflect-$scalaVersion.jar")
|
||||
)
|
||||
|
||||
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 compilerBridgeId(scalaVersion: String) =
|
||||
scalaVersion match {
|
||||
case sc if sc startsWith "2.11" => "compiler-bridge_2.11"
|
||||
case _ => "compiler-bridge_2.10"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
private val lock: GlobalLock = new GlobalLock {
|
||||
override def apply[T](file: File, callable: Callable[T]): T = callable.call()
|
||||
}
|
||||
|
||||
private def provider(targetDir: File): ComponentProvider = new ComponentProvider {
|
||||
|
||||
override def lockFile(): File = targetDir / "lock"
|
||||
|
||||
override def defineComponent(componentID: String, files: Array[File]): Unit =
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package sbt.internal.inc
|
||||
|
||||
import sbt.io.IO
|
||||
import sbt.util.Logger
|
||||
|
||||
class IvyComponentCompilerSpec extends BridgeProviderSpecification {
|
||||
|
||||
val scala282 = "2.8.2"
|
||||
val scala292 = "2.9.2"
|
||||
val scala210 = "2.10.5"
|
||||
val scala211 = "2.11.7"
|
||||
|
||||
"IvyComponentCompiler" should "compile the bridge for Scala 2.8" in pendingUntilFixed {
|
||||
IO.withTemporaryDirectory { tempDir =>
|
||||
getCompilerBridge(tempDir, Logger.Null, scala282) should exist
|
||||
}
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.9" in pendingUntilFixed {
|
||||
IO.withTemporaryDirectory { tempDir =>
|
||||
getCompilerBridge(tempDir, Logger.Null, scala292) should exist
|
||||
}
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.10" in pendingUntilFixed {
|
||||
IO.withTemporaryDirectory { tempDir =>
|
||||
getCompilerBridge(tempDir, Logger.Null, scala210) should exist
|
||||
}
|
||||
}
|
||||
|
||||
it should "compile the bridge for Scala 2.11" in {
|
||||
IO.withTemporaryDirectory { tempDir =>
|
||||
getCompilerBridge(tempDir, Logger.Null, scala211) should exist
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue