Simplify layering strategies

The ShareRuntimeDependenciesLayerWithTestDependencies strategy doesn't
really work with resources, so it makes sense to get rid of it. Without
the share layer, there is no point in having separate
RuntimeDependencies and TestDependencies layers so I consolidated them
to Dependencies.

If we really care about binary/source compatibility for the 1.3.0-RCx
series, I can restore the traits and objects and set them private[sbt].
I think it was kind of a bug that they existed at all given the issue
with resources so it makes sense to just remove them.
This commit is contained in:
Ethan Atkins 2019-05-25 18:44:58 -07:00
parent 468334f142
commit b6cdd60cf8
8 changed files with 26 additions and 121 deletions

View File

@ -89,36 +89,12 @@ object ClassLoaderLayeringStrategy {
case object ScalaLibrary extends ScalaLibrary
/**
* Add a layer on top of the ScalaLibrary layer for the runtime jar dependencies.
* Add a layer on top of the ScalaLibrary layer for all of the task jar dependencies.
*/
sealed trait RuntimeDependencies extends ScalaLibrary
sealed trait AllLibraryJars extends ScalaLibrary
/**
* Add a layer on top of the ScalaLibrary layer for the runtime jar dependencies.
* Add a layer on top of the ScalaLibrary layer for all of the jar dependencies.
*/
case object RuntimeDependencies extends ScalaLibrary with RuntimeDependencies
/**
* Add a layer on top of the ScalaLibrary layer for the test jar dependencies.
*/
sealed trait TestDependencies extends ScalaLibrary
/**
* Add a layer on top of the ScalaInstance layer for the test jar dependencies.
*/
case object TestDependencies extends ScalaLibrary with TestDependencies
/**
* Add the TestDependencies layer on top of the RuntimeDependencies layer on top of the
* ScalaLibrary layer. This differs from TestDependencies in that it will not reload the
* runtime classpath. The drawback to using this is that if the test dependencies evict
* classes provided in the runtime layer, then tests can fail. In order for sharing the runtime
* layer to work, it is necessary to set [[Keys.bgCopyClasspath]] to false. Otherwise the
* runtime and test classpaths are completely different.
*/
case object ShareRuntimeDependenciesLayerWithTestDependencies
extends ScalaLibrary
with RuntimeDependencies
with TestDependencies
object AllLibraryJars extends AllLibraryJars
}

View File

@ -168,8 +168,7 @@ object Defaults extends BuildCommon {
private[sbt] lazy val globalJvmCore: Seq[Setting[_]] =
Seq(
compilerCache := state.value get Keys.stateCompilerCache getOrElse CompilerCache.fresh,
classLoaderLayeringStrategy :== ClassLoaderLayeringStrategy.RuntimeDependencies,
classLoaderLayeringStrategy in Test :== ClassLoaderLayeringStrategy.TestDependencies,
classLoaderLayeringStrategy :== ClassLoaderLayeringStrategy.AllLibraryJars,
sourcesInBase :== true,
autoAPIMappings := false,
apiMappings := Map.empty,
@ -1059,7 +1058,7 @@ object Defaults extends BuildCommon {
cp,
forkedParallelExecution = false,
javaOptions = Nil,
strategy = ClassLoaderLayeringStrategy.TestDependencies,
strategy = ClassLoaderLayeringStrategy.AllLibraryJars,
projectId = "",
)
}
@ -1082,7 +1081,7 @@ object Defaults extends BuildCommon {
cp,
forkedParallelExecution,
javaOptions = Nil,
strategy = ClassLoaderLayeringStrategy.TestDependencies,
strategy = ClassLoaderLayeringStrategy.AllLibraryJars,
projectId = "",
)
}
@ -1863,18 +1862,7 @@ object Defaults extends BuildCommon {
Classpaths.compilerPluginConfig ++ deprecationSettings
lazy val compileSettings: Seq[Setting[_]] =
configSettings ++ (mainBgRunMainTask +: mainBgRunTask) ++
Classpaths.addUnmanagedLibrary ++
Vector(
bgCopyClasspath in bgRun := {
val old = (bgCopyClasspath in bgRun).value
old && (Test / classLoaderLayeringStrategy).value != ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
},
bgCopyClasspath in bgRunMain := {
val old = (bgCopyClasspath in bgRunMain).value
old && (Test / classLoaderLayeringStrategy).value != ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
},
)
configSettings ++ (mainBgRunMainTask +: mainBgRunTask) ++ Classpaths.addUnmanagedLibrary
lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks

View File

@ -13,14 +13,12 @@ import java.net.URLClassLoader
import sbt.ClassLoaderLayeringStrategy._
import sbt.Keys._
import sbt.SlashSyntax0._
import sbt.internal.classpath.ClassLoaderCache
import sbt.internal.inc.ScalaInstance
import sbt.internal.inc.classpath.ClasspathUtilities
import sbt.internal.util.Attributed
import sbt.internal.util.Attributed.data
import sbt.io.IO
import sbt.librarymanagement.Configurations.Runtime
private[sbt] object ClassLoaders {
private[this] val interfaceLoader = classOf[sbt.testing.Framework].getClassLoader
@ -36,8 +34,6 @@ private[sbt] object ClassLoaders {
strategy = classLoaderLayeringStrategy.value,
si = si,
fullCP = fullCP,
rawRuntimeDependencies =
dependencyJars(Runtime / dependencyClasspath).value.filterNot(exclude),
allDependencies = dependencyJars(dependencyClasspath).value.filterNot(exclude),
cache = extendedClassLoaderCache.value,
resources = ClasspathUtilities.createClasspathResources(fullCP, si),
@ -72,7 +68,6 @@ private[sbt] object ClassLoaders {
s.log.warn(s"$showJavaOptions will be ignored, $showFork is set to false")
}
val exclude = dependencyJars(exportedProducts).value.toSet ++ instance.allJars
val runtimeDeps = dependencyJars(Runtime / dependencyClasspath).value.filterNot(exclude)
val allDeps = dependencyJars(dependencyClasspath).value.filterNot(exclude)
val newLoader =
(classpath: Seq[File]) => {
@ -80,7 +75,6 @@ private[sbt] object ClassLoaders {
strategy = classLoaderLayeringStrategy.value: @sbtUnchecked,
si = instance,
fullCP = classpath,
rawRuntimeDependencies = runtimeDeps,
allDependencies = allDeps,
cache = extendedClassLoaderCache.value: @sbtUnchecked,
resources = ClasspathUtilities.createClasspathResources(classpath, instance),
@ -103,10 +97,9 @@ private[sbt] object ClassLoaders {
* Create a layered classloader. There are up to five layers:
* 1) the scala instance class loader
* 2) the resource layer
* 3) the runtime dependencies
* 4) the test dependencies
* 5) the rest of the classpath
* The first two layers may be optionally cached to reduce memory usage and improve
* 3) the dependency jars
* 4) the rest of the classpath
* The first three layers may be optionally cached to reduce memory usage and improve
* start up latency. Because there may be mutually incompatible libraries in the runtime
* and test dependencies, it's important to be able to configure which layers are used.
*/
@ -114,36 +107,20 @@ private[sbt] object ClassLoaders {
strategy: ClassLoaderLayeringStrategy,
si: ScalaInstance,
fullCP: Seq[File],
rawRuntimeDependencies: Seq[File],
allDependencies: Seq[File],
cache: ClassLoaderCache,
resources: Map[String, String],
tmp: File,
scope: Scope
): ClassLoader = {
val isTest = scope.config.toOption.map(_.name) == Option("test")
val raw = strategy match {
case Flat => flatLoader(fullCP, interfaceLoader)
case _ =>
val (layerDependencies, layerTestDependencies) = strategy match {
case ShareRuntimeDependenciesLayerWithTestDependencies if isTest => (true, true)
case ScalaLibrary => (false, false)
case RuntimeDependencies => (true, false)
case TestDependencies if isTest => (false, true)
case badStrategy =>
val msg = s"Layering strategy $badStrategy is not valid for the classloader in " +
s"$scope. Valid options are: ClassLoaderLayeringStrategy.{ " +
"Flat, ScalaInstance, RuntimeDependencies }"
throw new IllegalArgumentException(msg)
val layerDependencies = strategy match {
case _: AllLibraryJars => true
case _ => false
}
val allDependenciesSet = allDependencies.toSet
// The raw declarations are to avoid having to make a dynamic task. The
// allDependencies and allTestDependencies create a mutually exclusive list of jar
// dependencies for layers 2 and 3. Note that in the Runtime or Compile configs, it
// should always be the case that allTestDependencies == Nil
val allTestDependencies = if (layerTestDependencies) allDependenciesSet else Set.empty[File]
val allRuntimeDependencies = (if (layerDependencies) rawRuntimeDependencies else Nil).toSet
val scalaLibraryLayer = layer(si.libraryJar :: Nil, interfaceLoader, cache, resources, tmp)
// layer 2 (resources)
@ -152,24 +129,16 @@ private[sbt] object ClassLoaders {
else scalaLibraryLayer
// layer 3 (optional if in the test config and the runtime layer is not shared)
val runtimeDependencySet = allDependenciesSet intersect allRuntimeDependencies
val runtimeDependencies = rawRuntimeDependencies.filter(runtimeDependencySet)
lazy val runtimeLayer =
if (layerDependencies)
layer(runtimeDependencies, resourceLayer, cache, resources, tmp)
val dependencyLayer =
if (layerDependencies) layer(allDependencies, resourceLayer, cache, resources, tmp)
else resourceLayer
// layer 4 (optional if testDependencies are empty)
val testDependencySet = allTestDependencies diff runtimeDependencySet
val testDependencies = allDependencies.filter(testDependencySet)
val testLayer = layer(testDependencies, runtimeLayer, cache, resources, tmp)
// layer 5
// layer 4
val dynamicClasspath =
fullCP.filterNot(testDependencySet ++ runtimeDependencies + si.libraryJar)
fullCP.filterNot(allDependenciesSet + si.libraryJar)
if (dynamicClasspath.nonEmpty)
new LayeredClassLoader(dynamicClasspath, testLayer, resources, tmp)
else testLayer
new LayeredClassLoader(dynamicClasspath, dependencyLayer, resources, tmp)
else dependencyLayer
}
ClasspathUtilities.filterByClasspath(fullCP, raw)
}

View File

@ -1,4 +1,4 @@
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
> run
> run

View File

@ -1,15 +1,3 @@
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
> run
# This fails because the runtime layer includes an old version of the foo-lib library that doesn't
# have the sbt.foo.Foo.y method defined.
> test
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.TestDependencies
> run
> test
> test

View File

@ -6,6 +6,6 @@ publishTo := Some(Resolver.file("test-resolver", file("").getCanonicalFile / "iv
version := "0.1.0"
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Dependencies
classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Dependencies
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars

View File

@ -8,14 +8,10 @@
> run
> set Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.RuntimeDependencies
> set Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
> run
> set Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.TestDependencies
-> run
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
> Test / runMain sbt.scripted.TestAkkaTest
@ -24,14 +20,6 @@
> Test / runMain sbt.scripted.TestAkkaTest
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.RuntimeDependencies
> Test / runMain sbt.scripted.TestAkkaTest
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
> Test / runMain sbt.scripted.TestAkkaTest
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.TestDependencies
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
> Test / runMain sbt.scripted.TestAkkaTest

View File

@ -1,9 +1,5 @@
> test
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies
> test
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
> test
@ -12,7 +8,7 @@
> test
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.TestDependencies
> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
$ copy-file changes/bad.scala src/test/scala/sbt/ScalatestTest.scala