Further Scala handling refinements. fixes #690.

* No longer override the Scala version to force it to be scalaVersion.  Custom configurations might use an independent version.
* Only substitute the jars from scalaHome when the major.minor versions line up for the substituted artifact
* Allow the Scala version to use for dependency resolution to be specified when using ++ to change Scala home: ++ version=/path/to/scala/home
This commit is contained in:
Mark Harrah 2013-03-05 17:21:53 -05:00
parent b3cbdf2bf2
commit ada663106d
4 changed files with 47 additions and 20 deletions

View File

@ -283,6 +283,16 @@ s"""$SwitchCommand <scala-version> [<command>]
Sets the `scalaVersion` of all projects to <scala-version> and reloads the build.
If <command> is provided, it is then executed.
$SwitchCommand [<scala-version>=]<scala-home> [<command>]
Uses the Scala installation at <scala-home> by configuring the scalaHome setting for
all projects.
If <scala-version> is specified, it is used as the value of the scalaVersion setting.
This is important when using managed dependencies. This version will determine the
cross-version used as well as transitive dependencies.
If <command> is provided, it is then executed.
See also `help $CrossCommand`
"""
}

View File

@ -32,24 +32,33 @@ object Cross
}
def spacedFirst(name: String) = opOrIDSpaced(name) ~ any.+
lazy val switchVersion = Command.arb(requireSession(switchParser), switchHelp) { case (state, (version, command)) =>
lazy val switchVersion = Command.arb(requireSession(switchParser), switchHelp) { case (state, (arg, command)) =>
val x = Project.extract(state)
import x._
val home = IO.resolve(x.currentProject.base, new File(version))
val (resolveVersion, homePath) = arg.split("=") match {
case Array(v, h) => (v, h)
case _ => ("", arg)
}
val home = IO.resolve(x.currentProject.base, new File(homePath))
val (add, exclude) =
if(home.exists) {
val instance = ScalaInstance(home)(state.classLoaderCache.apply _)
state.log.info("Setting Scala home to " + home + " with actual version " + instance.actualVersion)
val version = if(resolveVersion.isEmpty) instance.actualVersion else resolveVersion
state.log.info("\tand using " + version + " for resolving dependencies.")
val settings = Seq(
scalaVersion in GlobalScope :== instance.actualVersion,
scalaVersion in GlobalScope :== version,
scalaHome in GlobalScope :== Some(home),
scalaInstance in GlobalScope :== instance
)
(settings, excludeKeys(Set(scalaVersion.key, scalaHome.key, scalaInstance.key)))
} else if(!resolveVersion.isEmpty) {
error("Scala home directory did not exist: " + home)
} else {
state.log.info("Setting version to " + version)
state.log.info("Setting version to " + arg)
val settings = Seq(
scalaVersion in GlobalScope :== version,
scalaVersion in GlobalScope :== arg,
scalaHome in GlobalScope :== None
)
(settings, excludeKeys(Set(scalaVersion.key, scalaHome.key)))

View File

@ -11,7 +11,7 @@ package sbt
import Def.{Initialize, ScopedKey, Setting, SettingsDefinition}
import Artifact.{DocClassifier, SourceClassifier}
import Configurations.{Compile, CompilerPlugin, IntegrationTest, names, Provided, Runtime, Test}
import CrossVersion.{binarySbtVersion, binaryScalaVersion}
import CrossVersion.{binarySbtVersion, binaryScalaVersion, partialVersion}
import complete._
import std.TaskExtra._
import inc.{FileValueCache, Locate}
@ -919,7 +919,7 @@ object Classpaths
ivyXML in GlobalScope :== NodeSeq.Empty,
ivyValidate in GlobalScope :== false,
ivyScala <<= ivyScala or (scalaHome, scalaVersion in update, scalaBinaryVersion in update, scalaOrganization) { (sh,fv,bv,so) =>
Some(new IvyScala(fv, bv, Nil, filterImplicit = false, checkExplicit = true, overrideScalaVersion = sh.isEmpty, scalaOrganization = so))
Some(new IvyScala(fv, bv, Nil, filterImplicit = false, checkExplicit = true, overrideScalaVersion = false, scalaOrganization = so))
},
moduleConfigurations in GlobalScope :== Nil,
publishTo in GlobalScope :== None,
@ -1046,15 +1046,23 @@ object Classpaths
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
val isRoot = executionRoots.value contains resolvedScoped.value
val s = streams.value
val subScalaJars: Seq[File] = Defaults.unmanagedScalaInstanceOnly.value match {
case Some(si) => si.jars
case None =>
val scalaProvider = appConfiguration.value.provider.scalaProvider
// substitute the Scala jars from the provider so that when the provider's loader is used,
// the jars on the classpath match the jars used by the loader
if(scalaProvider.version == scalaVersion.value) scalaProvider.jars else Nil
val scalaProvider = appConfiguration.value.provider.scalaProvider
// Only substitute unmanaged jars for managed jars when the major.minor parts of the versions the same for:
// the resolved Scala version and the scalaHome version: compatible (weakly- no qualifier checked)
// the resolved Scala version and the declared scalaVersion: assume the user intended scalaHome to override anything with scalaVersion
def subUnmanaged(subVersion: String, jars: Seq[File]) = (sv: String) =>
(partialVersion(sv), partialVersion(subVersion), partialVersion(scalaVersion.value)) match {
case (Some(res), Some(sh), _) if res == sh => jars
case (Some(res), _, Some(decl)) if res == decl => jars
case _ => Nil
}
val subScalaJars: String => Seq[File] = Defaults.unmanagedScalaInstanceOnly.value match {
case Some(si) => subUnmanaged(si.version, si.jars)
case None => sv => if(scalaProvider.version == sv) scalaProvider.jars else Nil
}
val transform: UpdateReport => UpdateReport = if(subScalaJars.isEmpty) idFun else r => substituteScalaFiles(subScalaJars, scalaOrganization.value, r)
val transform: UpdateReport => UpdateReport = r => substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
val show = Reference.display(thisProjectRef.value)
cachedUpdate(s.cacheDirectory, show, ivyModule.value, updateConfiguration.value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log)
}
@ -1322,16 +1330,16 @@ object Classpaths
def substituteScalaFiles(scalaInstance: ScalaInstance, report: UpdateReport): UpdateReport =
substituteScalaFiles(scalaInstance, ScalaArtifacts.Organization, report)
@deprecated("Directly provide the jar files.", "0.13.0")
@deprecated("Directly provide the jar files per Scala version.", "0.13.0")
def substituteScalaFiles(scalaInstance: ScalaInstance, scalaOrg: String, report: UpdateReport): UpdateReport =
substituteScalaFiles(scalaInstance.jars, scalaOrg, report)
substituteScalaFiles(scalaOrg, report)(const(scalaInstance.jars))
def substituteScalaFiles(scalaJars: Seq[File], scalaOrg: String, report: UpdateReport): UpdateReport =
def substituteScalaFiles(scalaOrg: String, report: UpdateReport)(scalaJars: String => Seq[File]): UpdateReport =
report.substitute { (configuration, module, arts) =>
import ScalaArtifacts._
if(module.organization == scalaOrg) {
val jarName = module.name + ".jar"
val replaceWith = scalaJars.filter(_.getName == jarName).map(f => (Artifact(f.getName.stripSuffix(".jar")), f))
val replaceWith = scalaJars(module.revision).filter(_.getName == jarName).map(f => (Artifact(f.getName.stripSuffix(".jar")), f))
if(replaceWith.isEmpty) arts else replaceWith
} else
arts

View File

@ -23,7 +23,7 @@ Features
- Support defining Projects in .sbt files: vals of type Project are added to the Build. Details below.
- New syntax for settings, tasks, and input tasks. Details below.
- Automatically link to external API scaladocs of dependencies by setting ``autoAPIMappings := true``. This requires at least Scala 2.10.1 and for dependencies to define ``apiURL`` for their scaladoc location. Mappings may be manually added to the ``apiMappings`` task as well.
- Support setting Scala home directory temporary using the switch command: ``++ /path/to/scala/home``.
- Support setting Scala home directory temporary using the switch command: ``++ scala-version=/path/to/scala/home``. The scala-version part is optional, but is used as the version for any managed dependencies.
- ``export`` command
* For tasks, prints the contents of the 'export' stream. By convention, this should be the equivalent command line(s) representation. ``compile``, ``doc``, and ``console`` show the approximate command lines for their execution. Classpath tasks print the classpath string suitable for passing as an option.