Fix classpath ordering for layered classloaders

The order of the classpath was not previously preserved because I
converted the runtime and test classpaths to set. I fix that in this
commit.
This commit is contained in:
Ethan Atkins 2019-04-02 16:44:20 -07:00
parent 399dd920b0
commit 2c19138394
1 changed files with 20 additions and 20 deletions

View File

@ -46,14 +46,14 @@ private[sbt] object ClassLoaders {
val si = scalaInstance.value val si = scalaInstance.value
val rawCP = data(fullClasspath.value) val rawCP = data(fullClasspath.value)
val fullCP = if (si.isManagedVersion) rawCP else si.allJars.toSeq ++ rawCP val fullCP = if (si.isManagedVersion) rawCP else si.allJars.toSeq ++ rawCP
val exclude = dependencyJars(exportedProducts).value.toSet ++ si.allJars.toSeq val exclude = dependencyJars(exportedProducts).value.toSet ++ si.allJars
buildLayers( buildLayers(
strategy = classLoaderLayeringStrategy.value, strategy = classLoaderLayeringStrategy.value,
si = si, si = si,
fullCP = fullCP, fullCP = fullCP,
rawRuntimeDependencies = rawRuntimeDependencies =
dependencyJars(Runtime / dependencyClasspath).value.filterNot(exclude), dependencyJars(Runtime / dependencyClasspath).value.filterNot(exclude),
allDependencies = dependencyJars(dependencyClasspath).value.filterNot(exclude).toSet, allDependencies = dependencyJars(dependencyClasspath).value.filterNot(exclude),
base = interfaceLoader, base = interfaceLoader,
runtimeCache = (Runtime / classLoaderCache).value, runtimeCache = (Runtime / classLoaderCache).value,
testCache = (Test / classLoaderCache).value, testCache = (Test / classLoaderCache).value,
@ -91,17 +91,16 @@ private[sbt] object ClassLoaders {
val runtimeCache = (Runtime / classLoaderCache).value val runtimeCache = (Runtime / classLoaderCache).value
val testCache = (Test / classLoaderCache).value val testCache = (Test / classLoaderCache).value
val exclude = dependencyJars(exportedProducts).value.toSet ++ instance.allJars 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 = val newLoader =
(classpath: Seq[File]) => { (classpath: Seq[File]) => {
buildLayers( buildLayers(
strategy = classLoaderLayeringStrategy.value: @sbtUnchecked, strategy = classLoaderLayeringStrategy.value: @sbtUnchecked,
si = instance, si = instance,
fullCP = classpath, fullCP = classpath,
rawRuntimeDependencies = rawRuntimeDependencies = runtimeDeps,
(dependencyJars(Runtime / dependencyClasspath).value: @sbtUnchecked) allDependencies = allDeps,
.filterNot(exclude),
allDependencies =
(dependencyJars(dependencyClasspath).value: @sbtUnchecked).filterNot(exclude).toSet,
base = baseLoader, base = baseLoader,
runtimeCache = runtimeCache, runtimeCache = runtimeCache,
testCache = testCache, testCache = testCache,
@ -130,7 +129,7 @@ private[sbt] object ClassLoaders {
si: ScalaInstance, si: ScalaInstance,
fullCP: Seq[File], fullCP: Seq[File],
rawRuntimeDependencies: Seq[File], rawRuntimeDependencies: Seq[File],
allDependencies: Set[File], allDependencies: Seq[File],
base: ClassLoader, base: ClassLoader,
runtimeCache: ClassLoaderCache, runtimeCache: ClassLoaderCache,
testCache: ClassLoaderCache, testCache: ClassLoaderCache,
@ -153,30 +152,31 @@ private[sbt] object ClassLoaders {
"Flat, ScalaInstance, RuntimeDependencies }" "Flat, ScalaInstance, RuntimeDependencies }"
throw new IllegalArgumentException(msg) throw new IllegalArgumentException(msg)
} }
val allDependenciesSet = allDependencies.toSet
// The raw declarations are to avoid having to make a dynamic task. The // The raw declarations are to avoid having to make a dynamic task. The
// allDependencies and allTestDependencies create a mutually exclusive list of jar // 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 // dependencies for layers 2 and 3. Note that in the Runtime or Compile configs, it
// should always be the case that allTestDependencies == Nil // should always be the case that allTestDependencies == Nil
val allTestDependencies = if (layerTestDependencies) allDependencies else Set.empty[File] val allTestDependencies = if (layerTestDependencies) allDependenciesSet else Set.empty[File]
val allRuntimeDependencies = (if (layerDependencies) rawRuntimeDependencies else Nil).toSet val allRuntimeDependencies = (if (layerDependencies) rawRuntimeDependencies else Nil).toSet
val scalaInstanceLayer = combine(base, loader(si))
// layer 2 // layer 2
val runtimeDependencies = allDependencies intersect allRuntimeDependencies val runtimeDependencySet = allDependenciesSet intersect allRuntimeDependencies
val runtimeLayer = val runtimeDependencies = rawRuntimeDependencies.filter(runtimeDependencySet)
layer(runtimeDependencies.toSeq, loader(si), runtimeCache, resources, tmp) lazy val runtimeLayer =
if (layerDependencies)
layer(runtimeDependencies, scalaInstanceLayer, runtimeCache, resources, tmp)
else scalaInstanceLayer
// layer 3 (optional if testDependencies are empty) // layer 3 (optional if testDependencies are empty)
val testDependencySet = allTestDependencies diff runtimeDependencySet
// The top layer needs to include the interface jar or else the test task cannot be created. val testDependencies = allDependencies.filter(testDependencySet)
// It needs to be separated from the runtimeLayer or else the runtimeLayer cannot be val testLayer = layer(testDependencies, runtimeLayer, testCache, resources, tmp)
// shared between the runtime and test tasks.
val top = combine(base, runtimeLayer)
val testDependencies = allTestDependencies diff runtimeDependencies
val testLayer = layer(testDependencies.toSeq, top, testCache, resources, tmp)
// layer 4 // layer 4
val dynamicClasspath = val dynamicClasspath =
fullCP.filterNot(testDependencies ++ runtimeDependencies ++ si.allJars) fullCP.filterNot(testDependencySet ++ runtimeDependencies ++ si.allJars)
if (dynamicClasspath.nonEmpty) if (dynamicClasspath.nonEmpty)
new LayeredClassLoader(dynamicClasspath, testLayer, resources, tmp) new LayeredClassLoader(dynamicClasspath, testLayer, resources, tmp)
else testLayer else testLayer