diff --git a/main/src/main/scala/sbt/CommandStrings.scala b/main/src/main/scala/sbt/CommandStrings.scala index 0c3795ed1..55a640cd8 100644 --- a/main/src/main/scala/sbt/CommandStrings.scala +++ b/main/src/main/scala/sbt/CommandStrings.scala @@ -283,6 +283,16 @@ s"""$SwitchCommand [] Sets the `scalaVersion` of all projects to and reloads the build. If is provided, it is then executed. +$SwitchCommand [=] [] + Uses the Scala installation at by configuring the scalaHome setting for + all projects. + + If 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 is provided, it is then executed. + See also `help $CrossCommand` """ } diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 787e6baad..c39019062 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -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))) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index c3678c280..ac3e9629e 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -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 diff --git a/src/sphinx/Community/ChangeSummary_0.13.0.rst b/src/sphinx/Community/ChangeSummary_0.13.0.rst index 69b11f4b6..a9db33501 100644 --- a/src/sphinx/Community/ChangeSummary_0.13.0.rst +++ b/src/sphinx/Community/ChangeSummary_0.13.0.rst @@ -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.