diff --git a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala index 936bf21b5..2888cfb14 100644 --- a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala +++ b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ComponentCompiler.scala @@ -11,8 +11,27 @@ package inc import java.io.File import sbt.io.{ Hash, IO } -import sbt.internal.librarymanagement.{ IvyConfiguration, JsonUtil, IvySbt, InlineConfiguration, RetrieveConfiguration, IvyActions, UnresolvedWarningConfiguration, LogicalClock, UnresolvedWarning } -import sbt.librarymanagement.{ Configurations, ModuleID, ModuleInfo, Resolver, UpdateConfiguration, UpdateLogging, UpdateOptions, ArtifactTypeFilter } +import sbt.internal.librarymanagement.{ + IvyConfiguration, + JsonUtil, + IvySbt, + InlineConfiguration, + RetrieveConfiguration, + IvyActions, + UnresolvedWarningConfiguration, + LogicalClock, + UnresolvedWarning +} +import sbt.librarymanagement.{ + Configurations, + ModuleID, + ModuleInfo, + Resolver, + UpdateConfiguration, + UpdateLogging, + UpdateOptions, + ArtifactTypeFilter +} import sbt.librarymanagement.syntax._ import sbt.util.Logger import sbt.internal.util.{ BufferedLogger, FullLogger } @@ -23,19 +42,28 @@ private[sbt] object ComponentCompiler { val binSeparator = "-bin_" val javaVersion = System.getProperty("java.class.version") - def interfaceProvider(manager: ZincComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID): CompilerBridgeProvider = new CompilerBridgeProvider { - def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = - { + def interfaceProvider(manager: ZincComponentManager, + ivyConfiguration: IvyConfiguration, + sourcesModule: ModuleID): CompilerBridgeProvider = + new CompilerBridgeProvider { + def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = { // this is the instance used to compile the interface component - val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptionsUtil.auto, log), manager, ivyConfiguration, sourcesModule, log) - log.debug("Getting " + sourcesModule + " from component compiler for Scala " + scalaInstance.version) + val componentCompiler = new IvyComponentCompiler( + new RawCompiler(scalaInstance, ClasspathOptionsUtil.auto, log), + manager, + ivyConfiguration, + sourcesModule, + log) + log.debug( + "Getting " + sourcesModule + " from component compiler for Scala " + scalaInstance.version) componentCompiler() } - } + } lazy val incrementalVersion = { val properties = new java.util.Properties - val propertiesStream = getClass.getResource("/incrementalcompiler.version.properties").openStream + val propertiesStream = + getClass.getResource("/incrementalcompiler.version.properties").openStream try { properties.load(propertiesStream) } finally { propertiesStream.close() } properties.getProperty("version") } @@ -47,7 +75,11 @@ private[sbt] object ComponentCompiler { * The compiled classes are cached using the provided component manager according * to the actualVersion field of the RawCompiler. */ -private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: ZincComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID, log: Logger) { +private[inc] class IvyComponentCompiler(compiler: RawCompiler, + manager: ZincComponentManager, + ivyConfiguration: IvyConfiguration, + sourcesModule: ModuleID, + log: Logger) { import ComponentCompiler._ // private val xsbtiInterfaceModuleName = "compiler-interface" // private val xsbtiInterfaceID = s"interface-$incrementalVersion" @@ -58,7 +90,8 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: ZincComp def apply(): File = { // binID is of the form "org.example-compilerbridge-1.0.0-bin_2.11.7__50.0" - val binID = binaryID(s"${sourcesModule.organization}-${sourcesModule.name}-${sourcesModule.revision}") + val binID = binaryID( + s"${sourcesModule.organization}-${sourcesModule.name}-${sourcesModule.revision}") manager.file(binID)(IfMissing.define(true, compileAndInstall(binID))) } @@ -69,22 +102,26 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: ZincComp private def compileAndInstall(binID: String): Unit = IO.withTemporaryDirectory { binaryDirectory => - val targetJar = new File(binaryDirectory, s"$binID.jar") buffered bufferQuietly { IO.withTemporaryDirectory { retrieveDirectory => - update(getModule(sourcesModule), retrieveDirectory) match { case Left(uw) => import sbt.util.ShowLines._ - throw new InvalidComponent(s"Couldn't retrieve source module: $sourcesModule\n" + - uw.lines.mkString("\n")) + throw new InvalidComponent( + s"Couldn't retrieve source module: $sourcesModule\n" + + uw.lines.mkString("\n")) case Right(allArtifacts) => val (sources, xsbtiJars) = allArtifacts partition (_.getName endsWith "-sources.jar") - AnalyzingCompiler.compileSources(sources, targetJar, xsbtiJars, sourcesModule.name, compiler, log) + AnalyzingCompiler.compileSources(sources, + targetJar, + xsbtiJars, + sourcesModule.name, + compiler, + log) manager.define(binID, Seq(targetJar)) } } @@ -99,11 +136,14 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: ZincComp */ private def getModule(moduleID: ModuleID): ivySbt.Module = { val sha1 = Hash.toHex(Hash(moduleID.name)) - val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, moduleID.revision).withConfigurations(moduleID.configurations) + val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, moduleID.revision) + .withConfigurations(moduleID.configurations) getModule(dummyID, Vector(moduleID)) } - private def getModule(moduleID: ModuleID, deps: Vector[ModuleID], uo: UpdateOptions = UpdateOptions()): ivySbt.Module = { + private def getModule(moduleID: ModuleID, + deps: Vector[ModuleID], + uo: UpdateOptions = UpdateOptions()): ivySbt.Module = { val moduleSetting = InlineConfiguration( validate = false, ivyScala = None, @@ -127,15 +167,24 @@ private[inc] class IvyComponentCompiler(compiler: RawCompiler, manager: ZincComp s"unknown" } - private def update(module: ivySbt.Module, retrieveDirectory: File): Either[UnresolvedWarning, Vector[File]] = { - val retrieveConfiguration = RetrieveConfiguration(retrieveDirectory, Resolver.defaultRetrievePattern, false, None) + private def update(module: ivySbt.Module, + retrieveDirectory: File): Either[UnresolvedWarning, Vector[File]] = { + val retrieveConfiguration = + RetrieveConfiguration(retrieveDirectory, Resolver.defaultRetrievePattern, false, None) val updateConfiguration = UpdateConfiguration( Some(retrieveConfiguration), - missingOk = false, UpdateLogging.DownloadOnly, ArtifactTypeFilter.forbid(Set("doc")) + missingOk = false, + UpdateLogging.DownloadOnly, + ArtifactTypeFilter.forbid(Set("doc")) ) buffered.info(s"Attempting to fetch ${dependenciesNames(module)}. This operation may fail.") - IvyActions.updateEither(module, updateConfiguration, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, buffered) match { + IvyActions.updateEither(module, + updateConfiguration, + UnresolvedWarningConfiguration(), + LogicalClock.unknown, + None, + buffered) match { case Left(unresolvedWarning) => buffered.debug(s"Couldn't retrieve module ${dependenciesNames(module)}.") Left(unresolvedWarning) diff --git a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentManager.scala b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentManager.scala index e0cc8f418..4bd983159 100644 --- a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentManager.scala +++ b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentManager.scala @@ -23,82 +23,89 @@ import sbt.io.IO * This is used for compiled source jars so that the compilation need not be repeated for other projects on the same * machine. */ -class ZincComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, - secondaryCacheDir: Option[File], val log: Logger) { +class ZincComponentManager(globalLock: xsbti.GlobalLock, + provider: xsbti.ComponentProvider, + secondaryCacheDir: Option[File], + val log: Logger) { + /** Get all of the files for component 'id', throwing an exception if no files exist for the component. */ - def files(id: String)(ifMissing: IfMissing): Iterable[File] = - { - def fromSecondary = - lockSecondaryCache { - update(id) - getOrElse(createAndCache) - } getOrElse notFound - def getOrElse(orElse: => Iterable[File]): Iterable[File] = - { - val existing = provider.component(id) - if (existing.isEmpty) orElse - else existing - } - def notFound = invalid("Could not find required component '" + id + "'") - def createAndCache = - ifMissing match { - case IfMissing.Fail => notFound - case d: IfMissing.Define => - d.run() // this is expected to have called define. - if (d.useSecondaryCache) { - cacheToSecondaryCache(id) - } - getOrElse(notFound) - } - lockLocalCache { getOrElse(fromSecondary) } + def files(id: String)(ifMissing: IfMissing): Iterable[File] = { + def fromSecondary = + lockSecondaryCache { + update(id) + getOrElse(createAndCache) + } getOrElse notFound + def getOrElse(orElse: => Iterable[File]): Iterable[File] = { + val existing = provider.component(id) + if (existing.isEmpty) orElse + else existing } + def notFound = invalid("Could not find required component '" + id + "'") + def createAndCache = + ifMissing match { + case IfMissing.Fail => notFound + case d: IfMissing.Define => + d.run() // this is expected to have called define. + if (d.useSecondaryCache) { + cacheToSecondaryCache(id) + } + getOrElse(notFound) + } + lockLocalCache { getOrElse(fromSecondary) } + } + /** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */ private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)(action) + /** This is used to ensure atomic access to components in the global Ivy cache.*/ private def lockSecondaryCache[T](action: => T): Option[T] = secondaryCacheDir map { dir => val lockFile = new File(dir, ".sbt.cache.lock") lock(lockFile)(action) } - private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action }) + private def lock[T](file: File)(action: => T): T = + globalLock(file, new Callable[T] { def call = action }) + /** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */ def file(id: String)(ifMissing: IfMissing): File = files(id)(ifMissing).toList match { case x :: Nil => x - case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", ")) + case xs => + invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", ")) } private def invalid(msg: String) = throw new InvalidComponent(msg) - def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(id, files.toSeq.toArray) } + def define(id: String, files: Iterable[File]) = lockLocalCache { + provider.defineComponent(id, files.toSeq.toArray) + } + /** Retrieve the file for component 'id' from the secondary cache. */ - private def update(id: String): Unit = - { - secondaryCacheDir map { dir => - val file = seondaryCacheFile(id, dir) - if (file.exists) { - define(id, Seq(file)) - } + private def update(id: String): Unit = { + secondaryCacheDir map { dir => + val file = seondaryCacheFile(id, dir) + if (file.exists) { + define(id, Seq(file)) } - () } + () + } + /** Install the files for component 'id' to the secondary cache. */ - private def cacheToSecondaryCache(id: String): Unit = - { - val fromPrimaryCache = file(id)(IfMissing.fail) - secondaryCacheDir match { - case Some(dir) => - val file = seondaryCacheFile(id, dir) - IO.copyFile(fromPrimaryCache, file) - case _ => () - } - () + private def cacheToSecondaryCache(id: String): Unit = { + val fromPrimaryCache = file(id)(IfMissing.fail) + secondaryCacheDir match { + case Some(dir) => + val file = seondaryCacheFile(id, dir) + IO.copyFile(fromPrimaryCache, file) + case _ => () } + () + } private val sbtOrg = xsbti.ArtifactInfo.SbtOrganization - private def seondaryCacheFile(id: String, dir: File): File = - { - val fileName = id + "-" + ZincComponentManager.stampedVersion + ".jar" - new File(new File(dir, sbtOrg), fileName) - } + private def seondaryCacheFile(id: String, dir: File): File = { + val fileName = id + "-" + ZincComponentManager.stampedVersion + ".jar" + new File(new File(dir, sbtOrg), fileName) + } } class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) { def this(msg: String) = this(msg, null) @@ -106,6 +113,7 @@ class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(m sealed trait IfMissing object IfMissing { def fail: IfMissing = Fail + /** f is expected to call ZincComponentManager.define. */ def define(useSecondaryCache: Boolean, f: => Unit): IfMissing = new Define(useSecondaryCache, f) object Fail extends IfMissing @@ -114,15 +122,15 @@ object IfMissing { } } object ZincComponentManager { - lazy val (version, timestamp) = - { - val properties = new java.util.Properties - val propertiesStream = versionResource.openStream - try { properties.load(propertiesStream) } finally { propertiesStream.close() } - (properties.getProperty("version"), properties.getProperty("timestamp")) - } + lazy val (version, timestamp) = { + val properties = new java.util.Properties + val propertiesStream = versionResource.openStream + try { properties.load(propertiesStream) } finally { propertiesStream.close() } + (properties.getProperty("version"), properties.getProperty("timestamp")) + } lazy val stampedVersion = version + "_" + timestamp import java.net.URL - private def versionResource: URL = getClass.getResource("/incrementalcompiler.version.properties") + private def versionResource: URL = + getClass.getResource("/incrementalcompiler.version.properties") } diff --git a/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala b/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala index 5b20a0eb4..c966a4390 100644 --- a/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala +++ b/internal/zinc-ivy-integration/src/test/scala/sbt/internal/inc/BridgeProviderSpecification.scala @@ -9,7 +9,14 @@ import sbt.internal.inc.classpath.ClasspathUtilities import sbt.internal.librarymanagement.JsonUtil import sbt.io.IO import sbt.io.syntax._ -import sbt.librarymanagement.{ ModuleID, UpdateOptions, Resolver, Patterns, FileRepository, DefaultMavenRepository } +import sbt.librarymanagement.{ + ModuleID, + UpdateOptions, + Resolver, + Patterns, + FileRepository, + DefaultMavenRepository +} import sbt.util.Logger import xsbti.{ ComponentProvider, GlobalLock } @@ -19,29 +26,34 @@ import xsbti.{ ComponentProvider, GlobalLock } abstract class BridgeProviderSpecification extends BaseIvySpecification { // log.setLevel(Level.Warn) - def realLocal: Resolver = - { - val pList = Vector(s"$${user.home}/.ivy2/local/${Resolver.localBasePattern}") - FileRepository("local", Resolver.defaultFileConfiguration, Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false)) - } + def realLocal: Resolver = { + val pList = Vector(s"$${user.home}/.ivy2/local/${Resolver.localBasePattern}") + FileRepository( + "local", + Resolver.defaultFileConfiguration, + Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false)) + } override def resolvers: Vector[Resolver] = Vector(realLocal, DefaultMavenRepository) private val ivyConfiguration = mkIvyConfiguration(UpdateOptions()) - def secondaryCacheDirectory: File = - { - val target = file("target").getAbsoluteFile - target / "zinc-components" - } + def secondaryCacheDirectory: File = { + val target = file("target").getAbsoluteFile + target / "zinc-components" + } def secondaryCacheOpt: Option[File] = Some(secondaryCacheDirectory) def getCompilerBridge(targetDir: File, log: Logger, scalaVersion: String): File = { val instance = scalaInstance(scalaVersion) val bridgeId = compilerBridgeId(scalaVersion) - val sourceModule = ModuleID(xsbti.ArtifactInfo.SbtOrganization, bridgeId, ComponentCompiler.incrementalVersion).withConfigurations(Some("component")).sources() + val sourceModule = ModuleID( + xsbti.ArtifactInfo.SbtOrganization, + bridgeId, + ComponentCompiler.incrementalVersion).withConfigurations(Some("component")).sources() val raw = new RawCompiler(instance, ClasspathOptionsUtil.auto, log) val manager = new ZincComponentManager(lock, provider(targetDir), secondaryCacheOpt, log) - val componentCompiler = new IvyComponentCompiler(raw, manager, ivyConfiguration, sourceModule, log) + val componentCompiler = + new IvyComponentCompiler(raw, manager, ivyConfiguration, sourceModule, log) val bridge = componentCompiler.apply() val target = targetDir / s"target-bridge-$scalaVersion.jar" @@ -51,9 +63,14 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification { 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")) + 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) } @@ -68,18 +85,27 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification { 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 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 = { + 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) + new ScalaInstance(version.getOrElse("unknown"), + loader, + scalaLibrary, + scalaCompiler, + allJars, + version) } def compilerBridgeId(scalaVersion: String) = @@ -89,14 +115,17 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification { case _ => "compiler-bridge_2.12" } - def scalaLoader(jars: Seq[File]) = new URLClassLoader(sbt.io.Path.toURLs(jars), ClasspathUtilities.rootLoader) + 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] = { + def propertyFromResource(resource: String, + property: String, + classLoader: ClassLoader): Option[String] = { val props = propertiesFromResource(resource, classLoader) Option(props.getProperty(property)) } @@ -107,9 +136,9 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification { 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() } + try { props.load(stream) } catch { case _: Exception => } finally { + if (stream ne null) stream.close() + } props } @@ -122,7 +151,9 @@ abstract class BridgeProviderSpecification extends BaseIvySpecification { 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) } + files foreach { f => + IO.copyFile(f, targetDir / componentID / f.getName) + } override def addToComponent(componentID: String, files: Array[File]): Boolean = { defineComponent(componentID, files)