Skip to content

Commit 0a7b0b9

Browse files
committed
include only scala-library into the boot classpath during run
Fixes sbt/sbt#3405 Ref scala/scala-xml#195 sbt's `run` is emulated using a classloader trick that includes ScalaInstance as the parent classloader under the classpath. The problem is the ScalaInstance classloader currently contains both compiler, library, and their transitive JARs: ```scala res0: Array[java.io.File] = Array(scala-library.jar, scala-compiler.jar, jline.jar, scala-reflect.jar, scala-xml_2.12.jar) ``` This could have been causing various issues, but most recently it showed up as wrong version of scala-xml getting prioritized over what's passed by the user.
1 parent ef5b5fc commit 0a7b0b9

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

internal/zinc-classpath/src/main/scala/sbt/internal/inc/ScalaInstance.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ final class ScalaInstance(
6666
}
6767
}
6868

69+
/** ClassLoader with just scala-library.jar. */
70+
lazy val libraryLoader: ClassLoader = {
71+
classpath.ClasspathUtilities.toLibraryLoader(this)
72+
}
73+
6974
/** Get the string representation of all the available jars. */
7075
private def jarStrings: String = {
7176
val other = otherJars.mkString(", ")
@@ -75,6 +80,7 @@ final class ScalaInstance(
7580
override def toString: String =
7681
s"Scala instance { version label $version, actual version $actualVersion, $jarStrings }"
7782
}
83+
7884
object ScalaInstance {
7985

8086
/** Name of scala organisation to be used for artifact resolution. */

internal/zinc-classpath/src/main/scala/sbt/internal/inc/classpath/ClasspathUtilities.scala

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,44 @@ object ClasspathUtilities {
6363
final val AppClassPath = "app.class.path"
6464
final val BootClassPath = "boot.class.path"
6565

66-
def createClasspathResources(classpath: Seq[File], instance: ScalaInstance): Map[String, String] =
67-
createClasspathResources(classpath, instance.allJars)
66+
private[sbt] def findLibrary(instance: ScalaInstance): File = {
67+
instance match {
68+
case i: inc.ScalaInstance => i.libraryJar
69+
case _ =>
70+
(instance.allJars find { x =>
71+
x.getName.startsWith("scala-library")
72+
}).getOrElse {
73+
throw new InvalidScalaProvider(s"Couldn't find scala-library")
74+
}
75+
}
76+
}
77+
78+
/** returns the cached library loader if it's inc.ScalaInstance. */
79+
def getLibraryLoader(instance: ScalaInstance): ClassLoader = {
80+
instance match {
81+
case i: inc.ScalaInstance => i.libraryLoader
82+
case _ => toLibraryLoader(instance)
83+
}
84+
}
85+
86+
/**
87+
* Include only the scala-library.jar, if any, into the boot classpath.
88+
* 1. Supposedly using boot classpath here is for performance reason:
89+
* https://groups.google.com/d/msg/scala-internals/tT1pjH5GECE/dtgRj3w7ovIJ
90+
* 2. Using instance.allJars causes compiler and scala-xml (different version from cp!) to get pulled in.
91+
* https://github.com/sbt/sbt/issues/3405
92+
*/
93+
private[sbt] def toLibraryLoader(instance: ScalaInstance): ClassLoader = {
94+
val libraryJar = findLibrary(instance)
95+
val cp = Vector(libraryJar)
96+
toLoader(cp, rootLoader, createClasspathResources(cp, instance))
97+
}
98+
99+
def createClasspathResources(classpath: Seq[File],
100+
instance: ScalaInstance): Map[String, String] = {
101+
val libraryJar = findLibrary(instance)
102+
createClasspathResources(classpath, Array(libraryJar))
103+
}
68104

69105
def createClasspathResources(appPaths: Seq[File], bootPaths: Seq[File]): Map[String, String] = {
70106
def make(name: String, paths: Seq[File]) = name -> Path.makeString(paths)
@@ -74,11 +110,16 @@ object ClasspathUtilities {
74110
private[sbt] def filterByClasspath(classpath: Seq[File], loader: ClassLoader): ClassLoader =
75111
new ClasspathFilter(loader, xsbtiLoader, classpath.toSet)
76112

113+
/**
114+
* Creates a ClassLoader that contains the classpath and the scala-library from
115+
* the given instance.
116+
*/
77117
def makeLoader(classpath: Seq[File], instance: ScalaInstance): ClassLoader =
78-
filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance))
118+
filterByClasspath(classpath, makeLoader(classpath, getLibraryLoader(instance), instance))
79119

80120
def makeLoader(classpath: Seq[File], instance: ScalaInstance, nativeTemp: File): ClassLoader =
81-
filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance, nativeTemp))
121+
filterByClasspath(classpath,
122+
makeLoader(classpath, getLibraryLoader(instance), instance, nativeTemp))
82123

83124
def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance): ClassLoader =
84125
toLoader(classpath, parent, createClasspathResources(classpath, instance))

0 commit comments

Comments
 (0)