diff --git a/main/src/main/scala/sbt/LayeringStrategy.scala b/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala similarity index 69% rename from main/src/main/scala/sbt/LayeringStrategy.scala rename to main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala index 88d8b801f..4dee12197 100644 --- a/main/src/main/scala/sbt/LayeringStrategy.scala +++ b/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala @@ -8,10 +8,10 @@ package sbt /** - * Represents a ClassLoader layering strategy. By providing an instance of [[LayeringStrategy]], + * Represents a ClassLoader layering strategy. By providing an instance of [[ClassLoaderLayeringStrategy]], * users can configure the strategy that they want to use in various sbt tasks, most importantly * [[Keys.run]] and [[Keys.test]]. This setting is only relevant if fork := false in the task for - * which we obtain a LayeringStrategy. + * which we obtain a ClassLoaderLayeringStrategy. * * ClassLoaders can be composed of multiple ClassLoaders * to form a graph for loading a class. The different portions of the graph may be cached and @@ -62,54 +62,66 @@ package sbt * In general, this should only happen if the user explicitly overrides the thread context * ClassLoader or uses reflection to manipulate classes loaded by different loaders. */ -sealed trait LayeringStrategy +sealed trait ClassLoaderLayeringStrategy /** - * Provides instances of [[LayeringStrategy]] that can be used to define the ClassLoader used by + * Provides instances of [[ClassLoaderLayeringStrategy]] that can be used to define the ClassLoader used by * [[Keys.run]], [[Keys.test]] or any other task that runs java code inside of the sbt jvm. */ -object LayeringStrategy { +object ClassLoaderLayeringStrategy { /** - * Use the default LayeringStrategy for this task. + * Use the default ClassLoaderLayeringStrategy for this task. */ - case object Default extends LayeringStrategy + case object Default extends ClassLoaderLayeringStrategy /** * Include all of the dependencies in the loader. The base loader will be the Application * ClassLoader. All classes apart from system classes will be reloaded with each run. */ - case object Flat extends LayeringStrategy + case object Flat extends ClassLoaderLayeringStrategy /** - * Add a layer for the runtime jar dependencies. + * Add a layer for the scala instance class loader. */ - sealed trait RuntimeLayer extends LayeringStrategy - - /** - * Add a layer for the runtime jar dependencies. - */ - case object RuntimeDependencies extends RuntimeLayer - - /** - * Add a layer for the test jar dependencies. - */ - sealed trait TestLayer extends LayeringStrategy - - /** - * Add a layer for the test jar dependencies. - */ - case object TestDependencies extends TestLayer - - /** - * Add a layer for the test jar dependencies as well as a layer for the runtime jar dependencies. - */ - case object Full extends RuntimeLayer with TestLayer + sealed trait ScalaInstance extends ClassLoaderLayeringStrategy /** * This should indicate that we use a two layer ClassLoader where the top layer is the scala * instance and all of the dependencies and project class paths are included in the search path * of the second layer. */ - case object ScalaInstance extends LayeringStrategy + case object ScalaInstance extends ScalaInstance + + /** + * Add a layer on top of the ScalaInstance layer for the runtime jar dependencies. + */ + sealed trait RuntimeDependencies extends ScalaInstance + + /** + * Add a layer on top of the ScalaInstance layer for the runtime jar dependencies. + */ + case object RuntimeDependencies extends ScalaInstance with RuntimeDependencies + + /** + * Add a layer on top of the ScalaInstance layer for the test jar dependencies. + */ + sealed trait TestDependencies extends ScalaInstance + + /** + * Add a layer on top of the ScalaInstance layer for the test jar dependencies. + */ + case object TestDependencies extends ScalaInstance with TestDependencies + + /** + * Add the TestDependencies layer on top of the RuntimeDependencies layer on top of the + * ScalaInstance 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. + */ + case object ShareRuntimeDependenciesLayerWithTestDependencies + extends ScalaInstance + with RuntimeDependencies + with TestDependencies + } diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 54755ebee..cc8ce3fda 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -141,7 +141,7 @@ object Defaults extends BuildCommon { defaultTestTasks(test) ++ defaultTestTasks(testOnly) ++ defaultTestTasks(testQuick) ++ Seq( excludeFilter :== HiddenFileFilter, classLoaderCache := ClassLoaderCache(4), - layeringStrategy := LayeringStrategy.Default + classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Default ) ++ TaskRepository .proxy(GlobalScope / classLoaderCache, ClassLoaderCache(4)) ++ globalIvyCore ++ globalJvmCore ) ++ globalSbtCore @@ -813,7 +813,7 @@ object Defaults extends BuildCommon { (fullClasspath in test).value, testForkedParallel.value, (javaOptions in test).value, - (layeringStrategy).value + (classLoaderLayeringStrategy).value ) } ).value, @@ -980,7 +980,7 @@ object Defaults extends BuildCommon { fullClasspath.value, testForkedParallel.value, javaOptions.value, - layeringStrategy.value + classLoaderLayeringStrategy.value ) val taskName = display.show(resolvedScoped.value) val trl = testResultLogger.value @@ -1024,7 +1024,7 @@ object Defaults extends BuildCommon { cp, forkedParallelExecution = false, javaOptions = Nil, - strategy = LayeringStrategy.Default + strategy = ClassLoaderLayeringStrategy.Default ) } @@ -1046,7 +1046,7 @@ object Defaults extends BuildCommon { cp, forkedParallelExecution, javaOptions = Nil, - strategy = LayeringStrategy.Default + strategy = ClassLoaderLayeringStrategy.Default ) } @@ -1059,7 +1059,7 @@ object Defaults extends BuildCommon { cp: Classpath, forkedParallelExecution: Boolean, javaOptions: Seq[String], - strategy: LayeringStrategy, + strategy: ClassLoaderLayeringStrategy, ): Initialize[Task[Tests.Output]] = { val runners = createTestRunners(frameworks, loader, config) val groupTasks = groups map { @@ -1091,16 +1091,18 @@ object Defaults extends BuildCommon { case (suite, e) => e.throwables .collectFirst { - case t if t.isInstanceOf[NoClassDefFoundError] && strategy != LayeringStrategy.Flat => + case t + if t + .isInstanceOf[NoClassDefFoundError] && strategy != ClassLoaderLayeringStrategy.Flat => t } .foreach { t => s.log.error( - s"Test suite $suite failed with $t. This may be due to the LayeringStrategy" + s"Test suite $suite failed with $t. This may be due to the ClassLoaderLayeringStrategy" + s" ($strategy) used by your task. This issue may be resolved by changing the" - + " LayeringStrategy in your configuration (generally Test or IntegrationTest)," - + "e.g.:\nTest / layeringStrategy := LayeringStrategy.Flat\n" - + "See LayeringStrategy.scala for the full list of options." + + " ClassLoaderLayeringStrategy in your configuration (generally Test or IntegrationTest)," + + "e.g.:\nTest / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat\n" + + "See ClassLoaderLayeringStrategy.scala for the full list of options." ) } } diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 65e619504..6696e9171 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -264,7 +264,7 @@ object Keys { val bgRunMain = inputKey[JobHandle]("Start a provided main class as a background job") val fgRunMain = inputKey[Unit]("Start a provided main class as a foreground job") val bgCopyClasspath = settingKey[Boolean]("Copies classpath on bgRun to prevent conflict.") - val layeringStrategy = settingKey[LayeringStrategy]("Creates the classloader layering strategy for the particular configuration.") + val classLoaderLayeringStrategy = settingKey[ClassLoaderLayeringStrategy]("Creates the classloader layering strategy for the particular configuration.") // Test Keys val testLoader = taskKey[ClassLoader]("Provides the class loader used for testing.").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/ClassLoaders.scala b/main/src/main/scala/sbt/internal/ClassLoaders.scala index cba184ad0..ac55174e1 100644 --- a/main/src/main/scala/sbt/internal/ClassLoaders.scala +++ b/main/src/main/scala/sbt/internal/ClassLoaders.scala @@ -35,15 +35,15 @@ private[sbt] object ClassLoaders { val si = scalaInstance.value val rawCP = data(fullClasspath.value) val fullCP = if (si.isManagedVersion) rawCP else si.allJars.toSeq ++ rawCP - val strategy = layeringStrategy.value + val strategy = classLoaderLayeringStrategy.value val runtimeCache = (Runtime / classLoaderCache).value val testCache = classLoaderCache.value val tmp = IO.createUniqueDirectory(taskTemporaryDirectory.value) val resources = ClasspathUtilities.createClasspathResources(fullCP, si) val raw = strategy match { - case LayeringStrategy.Flat => flatLoader(rawCP, interfaceLoader) - case s => + case ClassLoaderLayeringStrategy.Flat => flatLoader(rawCP, interfaceLoader) + case s => /* * Create a layered classloader. There are up to four layers: * 1) the scala instance class loader @@ -55,10 +55,11 @@ private[sbt] object ClassLoaders { * and test dependencies, it's important to be able to configure which layers are used. */ val (layerDependencies, layerTestDependencies) = s match { - case LayeringStrategy.Full => (true, true) - case LayeringStrategy.ScalaInstance => (false, false) - case LayeringStrategy.RuntimeDependencies => (true, false) - case _ => (false, true) + case ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies => + (true, true) + case ClassLoaderLayeringStrategy.ScalaInstance => (false, false) + case ClassLoaderLayeringStrategy.RuntimeDependencies => (true, false) + case _ => (false, true) } // Do not include exportedProducts in any cached layers because they may change between runs. val exclude = dependencyJars(exportedProducts).value.toSet ++ si.allJars.toSeq @@ -127,8 +128,8 @@ private[sbt] object ClassLoaders { val newLoader = (classpath: Seq[File]) => { val resources = ClasspathUtilities.createClasspathResources(classpath, instance) - val classLoader = layeringStrategy.value match { - case LayeringStrategy.Flat => + val classLoader = classLoaderLayeringStrategy.value match { + case ClassLoaderLayeringStrategy.Flat => ClasspathUtilities .toLoader(Nil, flatLoader(classpath, new NullLoader), resources, tmp) case _ => diff --git a/sbt/src/sbt-test/classloader-cache/akka-actor-system/test b/sbt/src/sbt-test/classloader-cache/akka-actor-system/test index 355cff205..abcb2b542 100644 --- a/sbt/src/sbt-test/classloader-cache/akka-actor-system/test +++ b/sbt/src/sbt-test/classloader-cache/akka-actor-system/test @@ -1,4 +1,4 @@ -> set Test / layeringStrategy := LayeringStrategy.Full +> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies > run diff --git a/sbt/src/sbt-test/classloader-cache/library-mismatch/test b/sbt/src/sbt-test/classloader-cache/library-mismatch/test index 825fbc4e5..abdaa10fa 100644 --- a/sbt/src/sbt-test/classloader-cache/library-mismatch/test +++ b/sbt/src/sbt-test/classloader-cache/library-mismatch/test @@ -1,4 +1,4 @@ -> set Test / layeringStrategy := LayeringStrategy.Full +> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ShareRuntimeDependenciesLayerWithTestDependencies > run @@ -6,7 +6,7 @@ # have the sbt.foo.Foo.y method defined. > test -> set Test / layeringStrategy := LayeringStrategy.TestDependencies +> set Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.TestDependencies > run diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index 071576668..3f7c68341 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -52,8 +52,8 @@ final class TestFramework(val implClassNames: String*) extends Serializable { + " using a layered class loader that cannot reach the sbt.testing.Framework class." + " The most likely cause is that your project has a runtime dependency on your" + " test framework, e.g. scalatest. To fix this, you can try to set\n" - + "Test / layeringStrategy := new LayeringStrategy.Test(false, true)\nor\n" - + "Test / layeringStrategy := LayeringStrategy.Flat" + + "Test / classLoaderLayeringStrategy := new ClassLoaderLayeringStrategy.Test(false, true)\nor\n" + + "Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat" ) None }