mirror of https://github.com/sbt/sbt.git
Resolve Scala version for projects in the normal `update` task.
1. Scala jars won't be copied to the boot directory, except for those needed to run sbt. 2. Scala SNAPSHOTs behave like normal SNAPSHOTs. In particular, running `update` will properly re-resolve the dynamic revision. 3. Scala jars are resolved using the same repositories and configuration as other dependencies. 4. Classloaders (currently, Scala classloaders) are cached by the timestamps of entries instead of Scala class loaders being cached by version. TODO: Support external dependency configuration
This commit is contained in:
parent
4cc5bece70
commit
e47a357ab7
|
|
@ -347,7 +347,7 @@ object Configurations
|
|||
def default: Seq[Configuration] = defaultMavenConfigurations
|
||||
def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional)
|
||||
def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal)
|
||||
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom)
|
||||
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom, ScalaTool)
|
||||
def names(cs: Seq[Configuration]) = cs.map(_.name)
|
||||
|
||||
lazy val RuntimeInternal = optionalInternal(Runtime)
|
||||
|
|
@ -379,6 +379,7 @@ object Configurations
|
|||
lazy val Optional = config("optional")
|
||||
lazy val Pom = config("pom")
|
||||
|
||||
lazy val ScalaTool = config("scala-tool") hide
|
||||
lazy val CompilerPlugin = config("plugin") hide
|
||||
|
||||
private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true)
|
||||
|
|
|
|||
|
|
@ -35,9 +35,16 @@ object Defaults extends BuildCommon
|
|||
{
|
||||
final val CacheDirectoryName = "cache"
|
||||
|
||||
private[sbt] def scalaToolDependencies(org: String, version: String): Seq[ModuleID] = Seq(
|
||||
scalaToolDependency(org, ScalaArtifacts.CompilerID, version),
|
||||
scalaToolDependency(org, ScalaArtifacts.LibraryID, version)
|
||||
)
|
||||
private[this] def scalaToolDependency(org: String, id: String, version: String): ModuleID =
|
||||
ModuleID(org, id, version, Some(Configurations.ScalaTool.name + "->default,optional(default)") )
|
||||
|
||||
def configSrcSub(key: SettingKey[File]): Initialize[File] = (key in ThisScope.copy(config = Global), configuration) { (src, conf) => src / nameForSrc(conf.name) }
|
||||
def nameForSrc(config: String) = if(config == "compile") "main" else config
|
||||
def prefix(config: String) = if(config == "compile") "" else config + "-"
|
||||
def nameForSrc(config: String) = if(config == Configurations.Compile.name) "main" else config
|
||||
def prefix(config: String) = if(config == Configurations.Compile.name) "" else config + "-"
|
||||
|
||||
def lock(app: xsbti.AppConfiguration): xsbti.GlobalLock = app.provider.scalaProvider.launcher.globalLock
|
||||
|
||||
|
|
@ -199,7 +206,7 @@ object Defaults extends BuildCommon
|
|||
compilersSetting,
|
||||
javacOptions in GlobalScope :== Nil,
|
||||
scalacOptions in GlobalScope :== Nil,
|
||||
scalaInstance <<= scalaInstanceSetting,
|
||||
scalaInstance <<= scalaInstanceTask,
|
||||
scalaVersion in GlobalScope := appConfiguration.value.provider.scalaProvider.version,
|
||||
scalaBinaryVersion in GlobalScope := binaryScalaVersion(scalaVersion.value),
|
||||
crossVersion := (if(crossPaths.value) CrossVersion.binary else CrossVersion.Disabled),
|
||||
|
|
@ -270,13 +277,37 @@ object Defaults extends BuildCommon
|
|||
}
|
||||
}
|
||||
}
|
||||
def scalaInstanceSetting = (appConfiguration, scalaOrganization, scalaVersion, scalaHome) map { (app, org, version, home) =>
|
||||
val launcher = app.provider.scalaProvider.launcher
|
||||
home match {
|
||||
case None => ScalaInstance(org, version, launcher)
|
||||
case Some(h) => ScalaInstance(h, launcher)
|
||||
@deprecated("Use scalaInstanceTask.", "0.13.0")
|
||||
def scalaInstanceSetting = scalaInstanceTask
|
||||
def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn {
|
||||
scalaHome.value match {
|
||||
case Some(h) => scalaInstanceFromHome(h)
|
||||
case None =>
|
||||
val scalaProvider = appConfiguration.value.provider.scalaProvider
|
||||
val version = scalaVersion.value
|
||||
if(version == scalaProvider.version) // use the same class loader as the Scala classes used by sbt
|
||||
Def.task( ScalaInstance(version, scalaProvider) )
|
||||
else
|
||||
scalaInstanceFromUpdate
|
||||
}
|
||||
}
|
||||
def scalaInstanceFromUpdate: Initialize[Task[ScalaInstance]] = Def.task {
|
||||
val toolReport = update.value.configuration(Configurations.ScalaTool.name) getOrElse error("Missing Scala tool configuration.")
|
||||
def files(id: String) =
|
||||
for { m <- toolReport.modules if m.module.name == id;
|
||||
(art, file) <- m.artifacts if art.`type` == Artifact.DefaultType }
|
||||
yield file
|
||||
def file(id: String) = files(id).headOption getOrElse error(s"Missing ${id}.jar")
|
||||
val allFiles = toolReport.modules.flatMap(_.artifacts.map(_._2))
|
||||
val libraryJar = file(ScalaArtifacts.LibraryID)
|
||||
val compilerJar = file(ScalaArtifacts.CompilerID)
|
||||
val otherJars = allFiles.filterNot(x => x == libraryJar || x == compilerJar)
|
||||
ScalaInstance(scalaVersion.value, libraryJar, compilerJar, otherJars : _*)(makeClassLoader(state.value))
|
||||
}
|
||||
def scalaInstanceFromHome(dir: File): Initialize[Task[ScalaInstance]] = Def.task {
|
||||
ScalaInstance(dir)(makeClassLoader(state.value))
|
||||
}
|
||||
private[this] def makeClassLoader(state: State) = state.classLoaderCache.apply _
|
||||
|
||||
lazy val testTasks: Seq[Setting[_]] = testTaskOptions(test) ++ testTaskOptions(testOnly) ++ testTaskOptions(testQuick) ++ Seq(
|
||||
testLoader := TestFramework.createTestLoader(data(fullClasspath.value), scalaInstance.value, IO.createUniqueDirectory(taskTemporaryDirectory.value)),
|
||||
|
|
@ -807,16 +838,20 @@ object Classpaths
|
|||
projectDependencies <<= projectDependenciesTask,
|
||||
dependencyOverrides in GlobalScope :== Set.empty,
|
||||
libraryDependencies in GlobalScope :== Nil,
|
||||
libraryDependencies <++= (autoScalaLibrary, sbtPlugin, scalaVersion) apply autoLibraryDependency,
|
||||
allDependencies <<= (projectDependencies,libraryDependencies,sbtPlugin,sbtDependency) map { (projDeps, libDeps, isPlugin, sbtDep) =>
|
||||
val base = projDeps ++ libDeps
|
||||
if(isPlugin) sbtDep.copy(configurations = Some(Provided.name)) +: base else base
|
||||
libraryDependencies <++= (autoScalaLibrary, sbtPlugin, scalaOrganization, scalaVersion) apply autoLibraryDependency,
|
||||
allDependencies := {
|
||||
val base = projectDependencies.value ++ libraryDependencies.value
|
||||
val pluginAdjust = if(sbtPlugin.value) sbtDependency.value.copy(configurations = Some(Provided.name)) +: base else base
|
||||
if(scalaHome.value.isDefined)
|
||||
pluginAdjust
|
||||
else
|
||||
Defaults.scalaToolDependencies(scalaOrganization.value, scalaVersion.value) ++ pluginAdjust
|
||||
},
|
||||
ivyLoggingLevel in GlobalScope :== UpdateLogging.DownloadOnly,
|
||||
ivyXML in GlobalScope :== NodeSeq.Empty,
|
||||
ivyValidate in GlobalScope :== false,
|
||||
ivyScala <<= ivyScala or (scalaHome, scalaVersion in update, scalaBinaryVersion in update) { (sh,fv,bv) =>
|
||||
Some(new IvyScala(fv, bv, Nil, filterImplicit = true, checkExplicit = true, overrideScalaVersion = sh.isEmpty))
|
||||
Some(new IvyScala(fv, bv, Nil, filterImplicit = false, checkExplicit = true, overrideScalaVersion = sh.isEmpty))
|
||||
},
|
||||
moduleConfigurations in GlobalScope :== Nil,
|
||||
publishTo in GlobalScope :== None,
|
||||
|
|
@ -852,11 +887,11 @@ object Classpaths
|
|||
ivySbt <<= ivySbt0,
|
||||
ivyModule <<= (ivySbt, moduleSettings) map { (ivySbt, settings) => new ivySbt.Module(settings) },
|
||||
transitiveUpdate <<= transitiveUpdateTask,
|
||||
update <<= (ivyModule, thisProjectRef, updateConfiguration, cacheDirectory, scalaInstance, transitiveUpdate, executionRoots, resolvedScoped, skip in update, streams) map {
|
||||
(module, ref, config, cacheDirectory, si, reports, roots, resolved, skip, s) =>
|
||||
update <<= (ivyModule, thisProjectRef, updateConfiguration, cacheDirectory, transitiveUpdate, executionRoots, resolvedScoped, skip in update, streams) map {
|
||||
(module, ref, config, cacheDirectory, reports, roots, resolved, skip, s) =>
|
||||
val depsUpdated = reports.exists(!_.stats.cached)
|
||||
val isRoot = roots contains resolved
|
||||
cachedUpdate(cacheDirectory / "update", Reference.display(ref), module, config, Some(si), skip = skip, force = isRoot, depsUpdated = depsUpdated, log = s.log)
|
||||
cachedUpdate(cacheDirectory / "update", Reference.display(ref), module, config, None, skip = skip, force = isRoot, depsUpdated = depsUpdated, log = s.log)
|
||||
} tag(Tags.Update, Tags.Network),
|
||||
update <<= (conflictWarning, update, streams) map { (config, report, s) => ConflictWarning(config, report, s.log); report },
|
||||
transitiveClassifiers in GlobalScope :== Seq(SourceClassifier, DocClassifier),
|
||||
|
|
@ -1155,11 +1190,18 @@ object Classpaths
|
|||
|
||||
def modifyForPlugin(plugin: Boolean, dep: ModuleID): ModuleID =
|
||||
if(plugin) dep.copy(configurations = Some(Provided.name)) else dep
|
||||
|
||||
@deprecated("Explicitly specify the organization using the other variant.", "0.13.0")
|
||||
def autoLibraryDependency(auto: Boolean, plugin: Boolean, version: String): Seq[ModuleID] =
|
||||
if(auto)
|
||||
modifyForPlugin(plugin, ScalaArtifacts.libraryDependency(version)) :: Nil
|
||||
else
|
||||
Nil
|
||||
def autoLibraryDependency(auto: Boolean, plugin: Boolean, org: String, version: String): Seq[ModuleID] =
|
||||
if(auto)
|
||||
modifyForPlugin(plugin, ModuleID(org, ScalaArtifacts.LibraryID, version)) :: Nil
|
||||
else
|
||||
Nil
|
||||
|
||||
import DependencyFilter._
|
||||
def managedJars(config: Configuration, jarTypes: Set[String], up: UpdateReport): Classpath =
|
||||
|
|
@ -1183,6 +1225,7 @@ object Classpaths
|
|||
if(autoCompilerPlugins.value) options ++ autoPlugins(update.value) else options
|
||||
}
|
||||
)
|
||||
@deprecated("Doesn't properly handle non-standard Scala organizations.", "0.13.0")
|
||||
def substituteScalaFiles(scalaInstance: ScalaInstance, report: UpdateReport): UpdateReport =
|
||||
report.substitute { (configuration, module, arts) =>
|
||||
import ScalaArtifacts._
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@ object StandardMain
|
|||
def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State =
|
||||
{
|
||||
val commands = preCommands ++ configuration.arguments.map(_.trim)
|
||||
State( configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, BuiltinCommands.initialAttributes, initialGlobalLogging, State.Continue )
|
||||
val initAttrs = BuiltinCommands.initialAttributes
|
||||
val s = State( configuration, initialDefinitions, Set.empty, None, commands, State.newHistory, initAttrs, initialGlobalLogging, State.Continue )
|
||||
s.initializeClassLoaderCache
|
||||
}
|
||||
def initialGlobalLogging: GlobalLogging =
|
||||
GlobalLogging.initial((pw, glb) => MainLogging.globalDefault(pw,glb,console), File.createTempFile("sbt",".log"), console)
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@ object BasicKeys
|
|||
val shellPrompt = AttributeKey[State => String]("shell-prompt", "The function that constructs the command prompt from the current build state.", 10000)
|
||||
val watch = AttributeKey[Watched]("watch", "Continuous execution configuration.", 1000)
|
||||
private[sbt] val interactive = AttributeKey[Boolean]("interactive", "True if commands are currently being entered from an interactive environment.", 10)
|
||||
private[sbt] val classLoaderCache = AttributeKey[classpath.ClassLoaderCache]("class-loader-cache", "Caches class loaders based on the classpath entries and last modified times.", 10)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,11 +38,13 @@ trait Identity {
|
|||
override final def toString = super.toString
|
||||
}
|
||||
|
||||
/** StateOps methods to be merged at the next binary incompatible release. */
|
||||
/** StateOps methods to be merged at the next binary incompatible release (0.13.0). */
|
||||
private[sbt] trait NewStateOps
|
||||
{
|
||||
def interactive: Boolean
|
||||
def setInteractive(flag: Boolean): State
|
||||
def classLoaderCache: classpath.ClassLoaderCache
|
||||
def initializeClassLoaderCache: State
|
||||
}
|
||||
|
||||
/** Convenience methods for State transformations and operations. */
|
||||
|
|
@ -161,6 +163,9 @@ object State
|
|||
private[sbt] implicit def newStateOps(s: State): NewStateOps = new NewStateOps {
|
||||
def interactive = s.get(BasicKeys.interactive).getOrElse(false)
|
||||
def setInteractive(i: Boolean) = s.put(BasicKeys.interactive, i)
|
||||
def classLoaderCache: classpath.ClassLoaderCache = s get BasicKeys.classLoaderCache getOrElse newClassLoaderCache
|
||||
def initializeClassLoaderCache = s.put(BasicKeys.classLoaderCache, newClassLoaderCache)
|
||||
private[this] def newClassLoaderCache = new classpath.ClassLoaderCache(s.configuration.provider.scalaProvider.launcher.topLoader)
|
||||
}
|
||||
|
||||
/** Provides operations and transformations on State. */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package sbt.classpath
|
||||
|
||||
import java.lang.ref.{Reference, SoftReference}
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.HashMap
|
||||
|
||||
private[sbt] final class ClassLoaderCache(val commonParent: ClassLoader)
|
||||
{
|
||||
private[this] val delegate = new HashMap[List[File],Reference[CachedClassLoader]]
|
||||
def apply(files: List[File]): ClassLoader =
|
||||
{
|
||||
val tstamps = files.map(_.lastModified)
|
||||
getFromReference(files, tstamps, delegate.get(files))
|
||||
}
|
||||
|
||||
private[this] def getFromReference(files: List[File], stamps: List[Long], existingRef: Reference[CachedClassLoader]) =
|
||||
if(existingRef eq null)
|
||||
newEntry(files, stamps)
|
||||
else
|
||||
get(files, stamps, existingRef.get)
|
||||
|
||||
private[this] def get(files: List[File], stamps: List[Long], existing: CachedClassLoader): ClassLoader =
|
||||
if(existing == null || stamps != existing.timestamps)
|
||||
newEntry(files, stamps)
|
||||
else
|
||||
existing.loader
|
||||
|
||||
private[this] def newEntry(files: List[File], stamps: List[Long]): ClassLoader =
|
||||
{
|
||||
val loader = new URLClassLoader(files.map(_.toURI.toURL).toArray, commonParent)
|
||||
delegate.put(files, new SoftReference(new CachedClassLoader(loader, files, stamps)))
|
||||
loader
|
||||
}
|
||||
}
|
||||
private[sbt] final class CachedClassLoader(val loader: ClassLoader, val files: List[File], val timestamps: List[Long])
|
||||
|
|
@ -63,7 +63,7 @@ object ClasspathUtilities
|
|||
|
||||
def makeLoader[T](classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance, nativeTemp: File): ClassLoader =
|
||||
toLoader(classpath, parent, createClasspathResources(classpath, instance), nativeTemp)
|
||||
|
||||
|
||||
private[sbt] def printSource(c: Class[_]) =
|
||||
println(c.getName + " loader=" +c.getClassLoader + " location=" + IO.classLocationFile(c))
|
||||
|
||||
|
|
|
|||
|
|
@ -46,18 +46,27 @@ object ScalaInstance
|
|||
|
||||
def apply(scalaHome: File, launcher: xsbti.Launcher): ScalaInstance =
|
||||
apply(libraryJar(scalaHome), compilerJar(scalaHome), launcher, extraJars(scalaHome): _*)
|
||||
def apply(scalaHome: File)(classLoader: List[File] => ClassLoader): ScalaInstance =
|
||||
apply(libraryJar(scalaHome), compilerJar(scalaHome), extraJars(scalaHome): _*)(classLoader)
|
||||
|
||||
def apply(version: String, scalaHome: File, launcher: xsbti.Launcher): ScalaInstance =
|
||||
apply(version, libraryJar(scalaHome), compilerJar(scalaHome), launcher, extraJars(scalaHome) : _*)
|
||||
def apply(libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance =
|
||||
apply(libraryJar, compilerJar, extraJars : _*)( scalaLoader(launcher) )
|
||||
def apply(libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance =
|
||||
{
|
||||
val loader = scalaLoader(launcher, libraryJar :: compilerJar :: extraJars.toList)
|
||||
val loader = classLoader(libraryJar :: compilerJar :: extraJars.toList)
|
||||
val version = actualVersion(loader)(" (library jar " + libraryJar.getAbsolutePath + ")")
|
||||
new ScalaInstance(version, loader, libraryJar, compilerJar, extraJars, None)
|
||||
}
|
||||
def apply(version: String, libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance =
|
||||
apply(version, None, libraryJar, compilerJar, launcher, extraJars : _*)
|
||||
def apply(version: String, libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance =
|
||||
apply(version, None, libraryJar, compilerJar, extraJars : _*)(classLoader)
|
||||
def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance =
|
||||
new ScalaInstance(version, scalaLoader(launcher, libraryJar :: compilerJar :: extraJars.toList), libraryJar, compilerJar, extraJars, explicitActual)
|
||||
apply(version, explicitActual, libraryJar, compilerJar, extraJars : _*)( scalaLoader(launcher) )
|
||||
def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance =
|
||||
new ScalaInstance(version, classLoader(libraryJar :: compilerJar :: extraJars.toList), libraryJar, compilerJar, extraJars, explicitActual)
|
||||
|
||||
def extraJars(scalaHome: File): Seq[File] =
|
||||
optScalaJar(scalaHome, "jline.jar") ++
|
||||
|
|
@ -94,9 +103,8 @@ object ScalaInstance
|
|||
}
|
||||
finally stream.close()
|
||||
}
|
||||
|
||||
import java.net.{URL, URLClassLoader}
|
||||
private def scalaLoader(launcher: xsbti.Launcher, jars: Seq[File]): ClassLoader =
|
||||
private def scalaLoader(launcher: xsbti.Launcher): Seq[File] => ClassLoader = jars =>
|
||||
new URLClassLoader(jars.map(_.toURI.toURL).toArray[URL], launcher.topLoader)
|
||||
}
|
||||
class InvalidScalaInstance(message: String, cause: Throwable) extends RuntimeException(message, cause)
|
||||
Loading…
Reference in New Issue