Specify the compiler bridge module in a setting

In order to restore reproducibility of builds, we no longer cascade over
the possibly available versions of the compiler bridge sources (a
specific version of the bridge sources may not be available one day, but
exist on the next day), but rather let the build definition configure
which module to use.

Fixes sbt/sbt#2196
This commit is contained in:
Martin Duhem 2015-09-04 11:17:27 +02:00
parent 58f5ceb6f2
commit dcad8a5527
5 changed files with 33 additions and 55 deletions

View File

@ -26,13 +26,13 @@ object ComponentCompiler {
} }
} }
def interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, bootDirectory: File): CompilerInterfaceProvider = new CompilerInterfaceProvider { def interfaceProvider(manager: ComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID, bootDirectory: File): CompilerInterfaceProvider = new CompilerInterfaceProvider {
def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File = def apply(scalaInstance: xsbti.compile.ScalaInstance, log: Logger): File =
{ {
// this is the instance used to compile the interface component // this is the instance used to compile the interface component
val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, ivyConfiguration, bootDirectory, log) val componentCompiler = new IvyComponentCompiler(new RawCompiler(scalaInstance, ClasspathOptions.auto, log), manager, ivyConfiguration, sourcesModule, bootDirectory, log)
log.debug("Getting " + compilerInterfaceID + " from component compiler for Scala " + scalaInstance.version) log.debug("Getting " + sourcesModule + " from component compiler for Scala " + scalaInstance.version)
componentCompiler(compilerInterfaceID) componentCompiler()
} }
} }
} }
@ -80,12 +80,12 @@ class ComponentCompiler(compiler: RawCompiler, manager: ComponentManager) {
} }
/** /**
* Component compiler which is able to find the most specific version available of * Component compiler which is able to to retrieve the compiler bridge sources
* the compiler interface sources using Ivy. * `sourceModule` using Ivy.
* The compiled classes are cached using the provided component manager according * The compiled classes are cached using the provided component manager according
* to the actualVersion field of the RawCompiler. * to the actualVersion field of the RawCompiler.
*/ */
private[compiler] class IvyComponentCompiler(compiler: RawCompiler, manager: ComponentManager, ivyConfiguration: IvyConfiguration, bootDirectory: File, log: Logger) { private[compiler] class IvyComponentCompiler(compiler: RawCompiler, manager: ComponentManager, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID, bootDirectory: File, log: Logger) {
import ComponentCompiler._ import ComponentCompiler._
private val sbtOrg = xsbti.ArtifactInfo.SbtOrganization private val sbtOrg = xsbti.ArtifactInfo.SbtOrganization
@ -94,11 +94,11 @@ private[compiler] class IvyComponentCompiler(compiler: RawCompiler, manager: Com
private val ivySbt: IvySbt = new IvySbt(ivyConfiguration) private val ivySbt: IvySbt = new IvySbt(ivyConfiguration)
private val sbtVersion = ComponentManager.version private val sbtVersion = ComponentManager.version
private val buffered = new BufferedLogger(FullLogger(log)) private val buffered = new BufferedLogger(FullLogger(log))
private val retrieveDirectory = new File(s"$bootDirectory/scala-${compiler.scalaInstance.version}/$sbtOrg/sbt/$sbtVersion/compiler-interface-srcs") private val retrieveDirectory = new File(s"$bootDirectory/scala-${compiler.scalaInstance.version}/$sbtOrg/sbt/$sbtVersion/compiler-interface-srcs/${sourcesModule.name}/${sourcesModule.revision}")
def apply(id: String): File = { def apply(): File = {
val binID = binaryID(id) val binID = binaryID(sourcesModule.name)
manager.file(binID)(new IfMissing.Define(true, compileAndInstall(id, binID))) manager.file(binID)(new IfMissing.Define(true, compileAndInstall(binID)))
} }
private def binaryID(id: String): String = { private def binaryID(id: String): String = {
@ -106,53 +106,32 @@ private[compiler] class IvyComponentCompiler(compiler: RawCompiler, manager: Com
base + "__" + javaVersion base + "__" + javaVersion
} }
private def compileAndInstall(id: String, binID: String): Unit = { private def compileAndInstall(binID: String): Unit =
def interfaceSources(moduleVersions: Vector[VersionNumber]): Iterable[File] =
moduleVersions match {
case Vector() =>
def getAndDefineDefaultSources() =
update(getModule(id))(_.getName endsWith "-sources.jar") map { sourcesJar =>
manager.define(id, sourcesJar)
sourcesJar
} getOrElse (throw new InvalidComponent(s"Couldn't retrieve default sources: module '$id'"))
buffered.debug(s"Fetching default sources: module '$id'")
manager.files(id)(new IfMissing.Fallback(getAndDefineDefaultSources()))
case version +: rest =>
val moduleName = s"${id}_$version"
def getAndDefineVersionSpecificSources() =
update(getModule(moduleName))(_.getName endsWith "-sources.jar") map { sourcesJar =>
manager.define(moduleName, sourcesJar)
sourcesJar
} getOrElse interfaceSources(rest)
buffered.debug(s"Fetching version-specific sources: module '$moduleName'")
manager.files(moduleName)(new IfMissing.Fallback(getAndDefineVersionSpecificSources()))
}
IO.withTemporaryDirectory { binaryDirectory => IO.withTemporaryDirectory { binaryDirectory =>
val targetJar = new File(binaryDirectory, s"$binID.jar") val targetJar = new File(binaryDirectory, s"$binID.jar")
val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail) val xsbtiJars = manager.files(xsbtiID)(IfMissing.Fail)
val sourceModuleVersions = VersionNumber(compiler.scalaInstance.actualVersion).cascadingVersions buffered bufferQuietly {
val sources = buffered bufferQuietly interfaceSources(sourceModuleVersions) (update(getModule(sourcesModule))(_.getName endsWith "-sources.jar")) match {
AnalyzingCompiler.compileSources(sources, targetJar, xsbtiJars, id, compiler, log) case Some(sources) =>
AnalyzingCompiler.compileSources(sources, targetJar, xsbtiJars, sourcesModule.name, compiler, log)
manager.define(binID, Seq(targetJar)) manager.define(binID, Seq(targetJar))
case None =>
throw new InvalidComponent(s"Couldn't retrieve source module: $sourcesModule")
}
}
} }
}
/** /**
* Returns a dummy module that depends on "org.scala-sbt" % `id` % `sbtVersion`. * Returns a dummy module that depends on `moduleID`.
* Note: Sbt's implementation of Ivy requires us to do this, because only the dependencies * Note: Sbt's implementation of Ivy requires us to do this, because only the dependencies
* of the specified module will be downloaded. * of the specified module will be downloaded.
*/ */
private def getModule(id: String): ivySbt.Module = { private def getModule(moduleID: ModuleID): ivySbt.Module = {
val sha1 = Hash.toHex(Hash(id)) val sha1 = Hash.toHex(Hash(moduleID.name))
val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, sbtVersion, Some("component")) val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, moduleID.revision, moduleID.configurations)
val moduleID = ModuleID(sbtOrg, id, sbtVersion, Some("component")).sources()
getModule(dummyID, Seq(moduleID)) getModule(dummyID, Seq(moduleID))
} }

View File

@ -39,8 +39,6 @@ class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentPr
d() d()
if (d.cache) cache(id) if (d.cache) cache(id)
getOrElse(notFound) getOrElse(notFound)
case f: IfMissing.Fallback =>
f()
} }
lockLocalCache { getOrElse(fromGlobal) } lockLocalCache { getOrElse(fromGlobal) }
@ -75,7 +73,6 @@ sealed trait IfMissing extends NotNull
object IfMissing { object IfMissing {
object Fail extends IfMissing object Fail extends IfMissing
final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define } final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define }
final class Fallback(f: => Iterable[File]) extends IfMissing { def apply() = f }
} }
object ComponentManager { object ComponentManager {
lazy val (version, timestamp) = lazy val (version, timestamp) =

View File

@ -69,7 +69,7 @@ object Compiler {
} }
compilers(instance, cpOptions, CheaterJavaTool(javac2, javac)) compilers(instance, cpOptions, CheaterJavaTool(javac2, javac))
} }
def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File], ivyConfiguration: IvyConfiguration)(implicit app: AppConfiguration, log: Logger): Compilers = def compilers(instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File], ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID)(implicit app: AppConfiguration, log: Logger): Compilers =
{ {
val javac = val javac =
AggressiveCompile.directOrFork(instance, cpOptions, javaHome) AggressiveCompile.directOrFork(instance, cpOptions, javaHome)
@ -81,7 +81,7 @@ object Compiler {
javac.compile(contract, sources, classpath, outputDirectory, options)(log) javac.compile(contract, sources, classpath, outputDirectory, options)(log)
def onArgs(f: Seq[String] => Unit): JavaTool = CheaterJavaTool(newJavac, delegate.onArgs(f)) def onArgs(f: Seq[String] => Unit): JavaTool = CheaterJavaTool(newJavac, delegate.onArgs(f))
} }
val scalac = scalaCompiler(instance, cpOptions, ivyConfiguration) val scalac = scalaCompiler(instance, cpOptions, ivyConfiguration, sourcesModule)
new Compilers(scalac, CheaterJavaTool(javac2, javac)) new Compilers(scalac, CheaterJavaTool(javac2, javac))
} }
@deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8") @deprecated("Deprecated in favor of new sbt.compiler.javac package.", "0.13.8")
@ -104,12 +104,12 @@ object Compiler {
val provider = ComponentCompiler.interfaceProvider(componentManager) val provider = ComponentCompiler.interfaceProvider(componentManager)
new AnalyzingCompiler(instance, provider, cpOptions) new AnalyzingCompiler(instance, provider, cpOptions)
} }
def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions, ivyConfiguration: IvyConfiguration)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler = def scalaCompiler(instance: ScalaInstance, cpOptions: ClasspathOptions, ivyConfiguration: IvyConfiguration, sourcesModule: ModuleID)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler =
{ {
val launcher = app.provider.scalaProvider.launcher val launcher = app.provider.scalaProvider.launcher
val componentManager = new ComponentManager(launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log) val componentManager = new ComponentManager(launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log)
val bootDirectory = launcher.bootDirectory val bootDirectory = launcher.bootDirectory
val provider = ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, bootDirectory) val provider = ComponentCompiler.interfaceProvider(componentManager, ivyConfiguration, sourcesModule, bootDirectory)
new AnalyzingCompiler(instance, provider, cpOptions) new AnalyzingCompiler(instance, provider, cpOptions)
} }

View File

@ -232,7 +232,8 @@ object Defaults extends BuildCommon {
clean := { clean := {
val _ = clean.value val _ = clean.value
IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log) IvyActions.cleanCachedResolutionCache(ivyModule.value, streams.value.log)
} },
scalaCompilerBridgeSource := ModuleID(xsbti.ArtifactInfo.SbtOrganization, "compiler-interface", sbtVersion.value, Some("component")).sources()
) )
// must be a val: duplication detected by object identity // must be a val: duplication detected by object identity
private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq( private[this] lazy val compileBaseGlobal: Seq[Setting[_]] = globalDefaults(Seq(
@ -261,7 +262,7 @@ object Defaults extends BuildCommon {
if (plugin) scalaBase / ("sbt-" + sbtv) else scalaBase if (plugin) scalaBase / ("sbt-" + sbtv) else scalaBase
} }
def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value, ivyConfiguration.value)(appConfiguration.value, streams.value.log) def compilersSetting = compilers := Compiler.compilers(scalaInstance.value, classpathOptions.value, javaHome.value, ivyConfiguration.value, scalaCompilerBridgeSource.value)(appConfiguration.value, streams.value.log)
lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq( lazy val configTasks = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ configGlobal ++ compileAnalysisSettings ++ Seq(
compile <<= compileTask, compile <<= compileTask,

View File

@ -137,6 +137,7 @@ object Keys {
val sbtPlugin = SettingKey[Boolean]("sbt-plugin", "If true, enables adding sbt as a dependency and auto-generation of the plugin descriptor file.", BMinusSetting) val sbtPlugin = SettingKey[Boolean]("sbt-plugin", "If true, enables adding sbt as a dependency and auto-generation of the plugin descriptor file.", BMinusSetting)
val printWarnings = TaskKey[Unit]("print-warnings", "Shows warnings from compilation, including ones that weren't printed initially.", BPlusTask) val printWarnings = TaskKey[Unit]("print-warnings", "Shows warnings from compilation, including ones that weren't printed initially.", BPlusTask)
val fileInputOptions = SettingKey[Seq[String]]("file-input-options", "Options that take file input, which may invalidate the cache.", CSetting) val fileInputOptions = SettingKey[Seq[String]]("file-input-options", "Options that take file input, which may invalidate the cache.", CSetting)
val scalaCompilerBridgeSource = SettingKey[ModuleID]("scala-compiler-bridge-source", "Configures the module ID of the sources of the compiler bridge.", CSetting)
val clean = TaskKey[Unit]("clean", "Deletes files produced by the build, such as generated sources, compiled classes, and task caches.", APlusTask) val clean = TaskKey[Unit]("clean", "Deletes files produced by the build, such as generated sources, compiled classes, and task caches.", APlusTask)
val console = TaskKey[Unit]("console", "Starts the Scala interpreter with the project classes on the classpath.", APlusTask) val console = TaskKey[Unit]("console", "Starts the Scala interpreter with the project classes on the classpath.", APlusTask)