diff --git a/internal/incrementalcompiler-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala b/internal/incrementalcompiler-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala index 81acc448f..9846e2d2a 100644 --- a/internal/incrementalcompiler-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala +++ b/internal/incrementalcompiler-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala @@ -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 } } diff --git a/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala b/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala new file mode 100644 index 000000000..f31478b9e --- /dev/null +++ b/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala @@ -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 + } + +} diff --git a/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/IvyComponentCompilerSpec.scala b/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/IvyComponentCompilerSpec.scala new file mode 100644 index 000000000..0043a083b --- /dev/null +++ b/internal/incrementalcompiler-ivy-integration/src/test/scala/sbt/internal/inc/IvyComponentCompilerSpec.scala @@ -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 + } + } + +}