mirror of https://github.com/sbt/sbt.git
Further refinements to Scala version handling
- override location of resolved Scala jars when scalaInstance is unmanaged - document current behavior: scalaHome, update, scalaInstance, autoScalaLibrary, managedScalaInstance
This commit is contained in:
parent
ae211ee33e
commit
d156ccfe4e
|
|
@ -271,9 +271,12 @@ object Defaults extends BuildCommon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@deprecated("Use scalaInstanceTask.", "0.13.0")
|
@deprecated("Use scalaInstanceTask.", "0.13.0")
|
||||||
def scalaInstanceSetting = scalaInstanceTask
|
def scalaInstanceSetting = scalaInstanceTask
|
||||||
def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn {
|
def scalaInstanceTask: Initialize[Task[ScalaInstance]] = Def.taskDyn {
|
||||||
|
// if this logic changes, ensure that `unmanagedScalaInstanceOnly` and `update` are changed
|
||||||
|
// appropriately to avoid cycles
|
||||||
scalaHome.value match {
|
scalaHome.value match {
|
||||||
case Some(h) => scalaInstanceFromHome(h)
|
case Some(h) => scalaInstanceFromHome(h)
|
||||||
case None =>
|
case None =>
|
||||||
|
|
@ -285,6 +288,12 @@ object Defaults extends BuildCommon
|
||||||
scalaInstanceFromUpdate
|
scalaInstanceFromUpdate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Returns the ScalaInstance only if it was not constructed via `update`
|
||||||
|
// This is necessary to prevent cycles between `update` and `scalaInstance`
|
||||||
|
private[sbt] def unmanagedScalaInstanceOnly: Initialize[Task[Option[ScalaInstance]]] = Def.taskDyn {
|
||||||
|
if(scalaHome.value.isDefined) Def.task(Some(scalaInstance.value)) else Def.task(None)
|
||||||
|
}
|
||||||
|
|
||||||
private[this] def noToolConfiguration(autoInstance: Boolean): String =
|
private[this] def noToolConfiguration(autoInstance: Boolean): String =
|
||||||
{
|
{
|
||||||
val pre = "Missing Scala tool configuration from the 'update' report. "
|
val pre = "Missing Scala tool configuration from the 'update' report. "
|
||||||
|
|
@ -902,13 +911,8 @@ object Classpaths
|
||||||
ivySbt <<= ivySbt0,
|
ivySbt <<= ivySbt0,
|
||||||
ivyModule := { val is = ivySbt.value; new is.Module(moduleSettings.value) },
|
ivyModule := { val is = ivySbt.value; new is.Module(moduleSettings.value) },
|
||||||
transitiveUpdate <<= transitiveUpdateTask,
|
transitiveUpdate <<= transitiveUpdateTask,
|
||||||
update <<= (ivyModule, thisProjectRef, updateConfiguration, cacheDirectory, transitiveUpdate, executionRoots, resolvedScoped, skip in update, streams) map {
|
update <<= updateTask tag(Tags.Update, Tags.Network),
|
||||||
(module, ref, config, cacheDirectory, reports, roots, resolved, skip, s) =>
|
update := { val report = update.value; ConflictWarning(conflictWarning.value, report, streams.value.log); report },
|
||||||
val depsUpdated = reports.exists(!_.stats.cached)
|
|
||||||
val isRoot = roots contains resolved
|
|
||||||
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),
|
transitiveClassifiers in GlobalScope :== Seq(SourceClassifier, DocClassifier),
|
||||||
classifiersModule in updateClassifiers := GetClassifiersModule(projectID.value, update.value.allModules, ivyConfigurations.in(updateClassifiers).value, transitiveClassifiers.in(updateClassifiers).value),
|
classifiersModule in updateClassifiers := GetClassifiersModule(projectID.value, update.value.allModules, ivyConfigurations.in(updateClassifiers).value, transitiveClassifiers.in(updateClassifiers).value),
|
||||||
updateClassifiers <<= (ivySbt, classifiersModule in updateClassifiers, updateConfiguration, ivyScala, target in LocalRootProject, appConfiguration, streams) map { (is, mod, c, ivyScala, out, app, s) =>
|
updateClassifiers <<= (ivySbt, classifiersModule in updateClassifiers, updateConfiguration, ivyScala, target in LocalRootProject, appConfiguration, streams) map { (is, mod, c, ivyScala, out, app, s) =>
|
||||||
|
|
@ -989,7 +993,17 @@ object Classpaths
|
||||||
}})
|
}})
|
||||||
}
|
}
|
||||||
|
|
||||||
def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration, scalaInstance: Option[ScalaInstance], skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport =
|
def updateTask: Initialize[Task[UpdateReport]] = Def.task {
|
||||||
|
val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached)
|
||||||
|
val isRoot = executionRoots.value contains resolvedScoped.value
|
||||||
|
val log = streams.value.log
|
||||||
|
val si = Defaults.unmanagedScalaInstanceOnly.value.map(si => (si, scalaOrganization.value))
|
||||||
|
val show = Reference.display(thisProjectRef.value)
|
||||||
|
val cache = cacheDirectory.value / "update"
|
||||||
|
cachedUpdate(cache, show, ivyModule.value, updateConfiguration.value, si, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = log)
|
||||||
|
}
|
||||||
|
|
||||||
|
def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration, scalaInstance: Option[(ScalaInstance, String)], skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport =
|
||||||
{
|
{
|
||||||
implicit val updateCache = updateIC
|
implicit val updateCache = updateIC
|
||||||
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||||
|
|
@ -997,7 +1011,7 @@ object Classpaths
|
||||||
log.info("Updating " + label + "...")
|
log.info("Updating " + label + "...")
|
||||||
val r = IvyActions.update(module, config, log)
|
val r = IvyActions.update(module, config, log)
|
||||||
log.info("Done updating.")
|
log.info("Done updating.")
|
||||||
scalaInstance match { case Some(si) => substituteScalaFiles(si, r); case None => r }
|
scalaInstance match { case Some((si,scalaOrg)) => substituteScalaFiles(si, scalaOrg, r); case None => r }
|
||||||
}
|
}
|
||||||
def uptodate(inChanged: Boolean, out: UpdateReport): Boolean =
|
def uptodate(inChanged: Boolean, out: UpdateReport): Boolean =
|
||||||
!force &&
|
!force &&
|
||||||
|
|
@ -1249,15 +1263,24 @@ object Classpaths
|
||||||
)
|
)
|
||||||
@deprecated("Doesn't properly handle non-standard Scala organizations.", "0.13.0")
|
@deprecated("Doesn't properly handle non-standard Scala organizations.", "0.13.0")
|
||||||
def substituteScalaFiles(scalaInstance: ScalaInstance, report: UpdateReport): UpdateReport =
|
def substituteScalaFiles(scalaInstance: ScalaInstance, report: UpdateReport): UpdateReport =
|
||||||
|
substituteScalaFiles(scalaInstance, ScalaArtifacts.Organization, report)
|
||||||
|
def substituteScalaFiles(scalaInstance: ScalaInstance, ScalaOrg: String, report: UpdateReport): UpdateReport =
|
||||||
|
{
|
||||||
|
val scalaJars = scalaInstance.jars
|
||||||
report.substitute { (configuration, module, arts) =>
|
report.substitute { (configuration, module, arts) =>
|
||||||
import ScalaArtifacts._
|
import ScalaArtifacts._
|
||||||
(module.organization, module.name) match
|
(module.organization, module.name) match
|
||||||
{
|
{
|
||||||
case (Organization, LibraryID) => (Artifact(LibraryID), scalaInstance.libraryJar) :: Nil
|
case (ScalaOrg, LibraryID) => (Artifact(LibraryID), scalaInstance.libraryJar) :: Nil
|
||||||
case (Organization, CompilerID) => (Artifact(CompilerID), scalaInstance.compilerJar) :: Nil
|
case (ScalaOrg, CompilerID) => (Artifact(CompilerID), scalaInstance.compilerJar) :: Nil
|
||||||
|
case (ScalaOrg, id) =>
|
||||||
|
val jarName = id + ".jar"
|
||||||
|
val replaceWith = scalaJars.filter(_.getName == jarName).map(f => (Artifact(f.getName), f))
|
||||||
|
if(replaceWith.isEmpty) arts else replaceWith
|
||||||
case _ => arts
|
case _ => arts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// try/catch for supporting earlier launchers
|
// try/catch for supporting earlier launchers
|
||||||
def bootIvyHome(app: xsbti.AppConfiguration): Option[File] =
|
def bootIvyHome(app: xsbti.AppConfiguration): Option[File] =
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
val makeHome = taskKey[Unit]("Populates the 'home/lib' directory with Scala jars from the default ScalaInstance")
|
||||||
|
|
||||||
|
makeHome := {
|
||||||
|
val lib = baseDirectory.value / "home" / "lib"
|
||||||
|
for(jar <- scalaInstance.value.jars)
|
||||||
|
IO.copyFile(jar, lib / jar.getName)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
scalaHome := Some(baseDirectory.value / "home")
|
||||||
|
|
||||||
|
val checkUpdate = taskKey[Unit]("Ensures that resolved Scala artifacts are replaced with ones from the configured Scala home directory")
|
||||||
|
|
||||||
|
checkUpdate := {
|
||||||
|
val report = update.value
|
||||||
|
val lib = (scalaHome.value.get / "lib").getCanonicalFile
|
||||||
|
for(f <- report.allFiles)
|
||||||
|
assert(f.getParentFile == lib, "Artifact not in Scala home directory: " + f.getAbsolutePath)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
> makeHome
|
||||||
|
$ copy-file changes/real-build.sbt build.sbt
|
||||||
|
> reload
|
||||||
|
> checkUpdate
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
autoScalaLibrary := false
|
||||||
|
|
||||||
|
libraryDependencies += "org.scala-lang" % "scala-library" % scalaVersion.value % "test"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class A
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
public class B {}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
$ copy-file changes/B.java B.java
|
||||||
|
> compile
|
||||||
|
|
||||||
|
$ copy-file changes/A.scala A.scala
|
||||||
|
-> compile
|
||||||
|
|
||||||
|
$ delete A.scala
|
||||||
|
$ copy-file changes/A.scala src/test/scala/A.scala
|
||||||
|
> compile
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
=================
|
||||||
|
Configuring Scala
|
||||||
|
=================
|
||||||
|
|
||||||
|
sbt needs to obtain Scala for a project and it can do this automatically or you can configure it explicitly.
|
||||||
|
The Scala version that is configured for a project will compile, run, document, and provide a REPL for the project code.
|
||||||
|
When compiling a project, sbt needs to run the Scala compiler as well as provide the compiler with a classpath, which may include several Scala jars, like the reflection jar.
|
||||||
|
|
||||||
|
Automatically managed Scala
|
||||||
|
===========================
|
||||||
|
|
||||||
|
The most common case is when you want to use a version of Scala that is available in a repository.
|
||||||
|
The only required configuration is the Scala version you want to use.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
scalaVersion := "2.10.0"
|
||||||
|
|
||||||
|
This will retrieve Scala from the repositories configured via the ``resolvers`` setting.
|
||||||
|
It will use this version for building your project: compiling, running, scaladoc, and the REPL.
|
||||||
|
|
||||||
|
Configuring the scala-library dependency
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
By default, the standard Scala library is automatically added as a dependency.
|
||||||
|
If you want to configure it differently than the default or you have a project with only Java sources, set:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
autoScalaLibrary := false
|
||||||
|
|
||||||
|
In order to compile Scala sources, the Scala library needs to be on the classpath.
|
||||||
|
When ``autoScalaLibrary`` is true, the Scala library will be on all classpaths: test, runtime, and compile.
|
||||||
|
Otherwise, you need to add it like any other dependency.
|
||||||
|
For example, the following dependency definition uses Scala only for tests:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
autoScalaLibrary := false
|
||||||
|
|
||||||
|
libraryDependencies += "org.scala-lang" % "scala-library" % scalaVersion.value % "test"
|
||||||
|
|
||||||
|
Configuring additional Scala dependencies
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When using a Scala dependency other than the standard library, add it as a normal managed dependency.
|
||||||
|
For example, to depend on the Scala compiler,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value
|
||||||
|
|
||||||
|
Note that this is necessary regardless of the value of the ``autoScalaLibrary`` setting described in the previous section.
|
||||||
|
|
||||||
|
Configuring Scala tool dependencies
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In order to compile Scala code, run scaladoc, and provide a Scala REPL, sbt needs the ``scala-compiler`` jar.
|
||||||
|
This should not be a normal dependency of the project, so sbt adds a dependency on ``scala-compiler`` in the special, private ``scala-tool`` configuration.
|
||||||
|
It may be desirable to have more control over this in some situations.
|
||||||
|
Disable this automatic behavior with the ``autoScalaInstance`` key:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
managedScalaInstance := false
|
||||||
|
|
||||||
|
This will also disable the automatic dependency on ``scala-library``.
|
||||||
|
If you do not need the Scala compiler for anything (compiling, the REPL, scaladoc, etc...), you can stop here.
|
||||||
|
sbt does not need an instance of Scala for your project in that case.
|
||||||
|
Otherwise, sbt will still need access to the jars for the Scala compiler for compilation and other tasks.
|
||||||
|
You can provide them by either declaring a dependency in the ``scala-tool`` configuration or by explicitly defining ``scalaInstance``.
|
||||||
|
|
||||||
|
In the first case, add the ``scala-tool`` configuration and add a dependency on ``scala-compiler`` in this configuration.
|
||||||
|
The organization is not important, but sbt needs the module name to be ``scala-compiler`` and ``scala-library`` in order to handle those jars appropriately.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
managedScalaInstance := false
|
||||||
|
|
||||||
|
// Add the configuration for the dependencies on Scala tool jars
|
||||||
|
// You can also use a manually constructed configuration like:
|
||||||
|
// config("scala-tool").hide
|
||||||
|
ivyConfigurations += Configurations.ScalaTool
|
||||||
|
|
||||||
|
// Add the usual dependency on the library as well on the compiler in the
|
||||||
|
// 'scala-tool' configuration
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.scala-lang" % "scala-library" % scalaVersion.value,
|
||||||
|
"org.scala-lang" % "scala-compiler" % scalaVersion.value % "scala-tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
In the second case, directly construct a value of type `ScalaInstance <../../api/sbt/ScalaInstance.html>`_, typically using a method in the `companion object <../../api/sbt/ScalaInstance$.html>`_, and assign it to ``scalaInstance``.
|
||||||
|
You will also need to add the ``scala-library`` jar to the classpath to compile and run Scala sources.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
managedScalaInstance := false
|
||||||
|
|
||||||
|
scalaInstance := ...
|
||||||
|
|
||||||
|
unmanagedJars in Compile += scalaInstance.value.libraryJar
|
||||||
|
|
||||||
|
Switching to a local Scala version
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To use a locally built Scala version, configure Scala home as described in the following section.
|
||||||
|
Scala will still be resolved as before, but the jars will come from the configured Scala home directory.
|
||||||
|
|
||||||
|
|
||||||
|
Using Scala from a local directory
|
||||||
|
==================================
|
||||||
|
|
||||||
|
The result of building Scala from source is a Scala home directory ``<base>/build/pack/`` that contains a subdirectory ``lib/`` containing the Scala library, compiler, and other jars.
|
||||||
|
The same directory layout is obtained by downloading and extracting a Scala distribution.
|
||||||
|
Such a Scala home directory may be used as the source for jars by setting ``scalaHome``.
|
||||||
|
For example,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
scalaHome := Some(file("/home/user/scala-2.10/"))
|
||||||
|
|
||||||
|
By default, ``lib/scala-library.jar`` will be added to the unmanaged classpath and ``lib/scala-compiler.jar`` will be used to compile Scala sources and provide a Scala REPL.
|
||||||
|
No managed dependency is recorded on ``scala-library``.
|
||||||
|
This means that Scala will only be resolved from a repository if you explicitly define a dependency on Scala or if Scala is depended on indirectly via a dependency.
|
||||||
|
In these cases, the artifacts for the resolved dependencies will be substituted with jars in the Scala home ``lib/`` directory.
|
||||||
|
|
||||||
|
Mixing with managed dependencies
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
As an example, consider adding a dependency on ``scala-reflect`` when ``scalaHome`` is configured:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
scalaHome := Some(file("/home/user/scala-2.10/"))
|
||||||
|
|
||||||
|
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
|
||||||
|
|
||||||
|
This will be resolved as normal, except that sbt will see if ``/home/user/scala-2.10/lib/scala-reflect.jar`` exists.
|
||||||
|
If it does, that file will be used in place of the artifact from the managed dependency.
|
||||||
|
|
||||||
|
Using unmanaged dependencies only
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Instead of adding managed dependencies on Scala jars, you can directly add them.
|
||||||
|
The ``scalaInstance`` task provides structured access to the Scala distribution.
|
||||||
|
For example, to add all jars in the Scala home ``lib/`` directory,
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
scalaHome := Some(file("/home/user/scala-2.10/"))
|
||||||
|
|
||||||
|
unmanagedJars in Compile ++= scalaInstance.value.jars
|
||||||
|
|
||||||
|
To add only some jars, filter the jars from ``scalaInstance`` before adding them.
|
||||||
|
|
||||||
|
sbt's Scala version
|
||||||
|
===================
|
||||||
|
|
||||||
|
sbt needs Scala jars to run itself since it is written in Scala.
|
||||||
|
sbt uses that same version of Scala to compile the build definitions that you write for your project because they use sbt APIs.
|
||||||
|
This version of Scala is fixed for a specific sbt release and cannot be changed.
|
||||||
|
For sbt |version|, this version is Scala |scalaVersion|.
|
||||||
|
Because this Scala version is needed before sbt runs, the repositories used to retrieve this version are configured in the sbt :doc:`launcher </Detailed-Topics/Launcher>`.
|
||||||
|
|
||||||
|
|
@ -10,6 +10,8 @@ extensions = ['sphinxcontrib.issuetracker', 'sphinx.ext.extlinks', 'howto']
|
||||||
project = 'sbt'
|
project = 'sbt'
|
||||||
version = '0.13'
|
version = '0.13'
|
||||||
release = '0.13.0-SNAPSHOT'
|
release = '0.13.0-SNAPSHOT'
|
||||||
|
scalaVersion = "2.10"
|
||||||
|
scalaRelease = "2.10.0"
|
||||||
|
|
||||||
# General settings
|
# General settings
|
||||||
|
|
||||||
|
|
@ -72,6 +74,8 @@ sbt_native_package_base = 'http://scalasbt.artifactoryonline.com/scalasbt/sbt-na
|
||||||
|
|
||||||
|
|
||||||
rst_epilog = """
|
rst_epilog = """
|
||||||
|
.. |scalaVersion| replace:: %(scalaVersion)s
|
||||||
|
.. |scalaRelease| replace:: %(scalaRelease)s
|
||||||
.. _typesafe-snapshots: %(typesafe_ivy_snapshots)s
|
.. _typesafe-snapshots: %(typesafe_ivy_snapshots)s
|
||||||
.. |typesafe-snapshots| replace:: Typesafe Snapshots
|
.. |typesafe-snapshots| replace:: Typesafe Snapshots
|
||||||
.. _sbt-launch.jar: %(launcher_release_base)s/%(version)s/sbt-launch.jar
|
.. _sbt-launch.jar: %(launcher_release_base)s/%(version)s/sbt-launch.jar
|
||||||
|
|
@ -88,6 +92,8 @@ rst_epilog = """
|
||||||
'launcher_snapshots_base': launcher_snapshots_base,
|
'launcher_snapshots_base': launcher_snapshots_base,
|
||||||
'version': release,
|
'version': release,
|
||||||
'typesafe_ivy_snapshots': typesafe_ivy_snapshots,
|
'typesafe_ivy_snapshots': typesafe_ivy_snapshots,
|
||||||
'sbt_native_package_base': sbt_native_package_base
|
'sbt_native_package_base': sbt_native_package_base,
|
||||||
|
'scalaRelease': scalaRelease,
|
||||||
|
'scalaVersion': scalaVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue