Skip to content

Commit 388f889

Browse files
committed
staging: fix classpath discovery with sbt >= 1.3
This fixes the sbt-dotty/quoted-example-project scripted test after the previous commit. Getting a classpath from a classloader is impossible in general, so we really shouldn't be relying on it, but I fixed the thing we currently use to at least work with sbt >= 1.3, sbt now uses multiple layers of classloaders so we have to recurse on the parent of the classloader to find all the URLs. I also made it more correct by not appending System.getProperty("java.class.path") to the classpath, this is incorrect in general and can lead to classpath pollution (e.g., when launched from sbt where java.class.path will contain the jars used by sbt itself, which might include a different version of scala-library than the one we use).
1 parent 6e0ec50 commit 388f889

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

staging/src/scala/quoted/staging/QuoteDriver.scala

+27-12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import dotty.tools.dotc.reporting._
1111
import scala.quoted._
1212
import scala.quoted.staging.Toolbox
1313
import java.net.URLClassLoader
14+
import java.nio.file.Paths
15+
import java.io.File
16+
import scala.annotation.tailrec
1417

1518
/** Driver to compile quoted code
1619
*
@@ -53,7 +56,7 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver {
5356

5457
override def initCtx: Context = {
5558
val ictx = contextBase.initialCtx
56-
ictx.settings.classpath.update(getCurrentClasspath(appClassloader))(ictx)
59+
ictx.settings.classpath.update(classpathFromClassloader(appClassloader))(ictx)
5760
ictx
5861
}
5962

@@ -64,17 +67,29 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver {
6467
ctx.setReporter(new ThrowingReporter(ctx.reporter))
6568
}
6669

67-
private def getCurrentClasspath(cl: ClassLoader): String = {
68-
val classpath0 = System.getProperty("java.class.path")
69-
cl match {
70-
case cl: URLClassLoader =>
71-
// Loads the classes loaded by this class loader
72-
// When executing `run` or `test` in sbt the classpath is not in the property java.class.path
73-
import java.nio.file.Paths
74-
val newClasspath = cl.getURLs.map(url => Paths.get(url.toURI).toString)
75-
newClasspath.mkString("", java.io.File.pathSeparator, if (classpath0 == "") "" else java.io.File.pathSeparator + classpath0)
76-
case _ => classpath0
77-
}
70+
/** Attempt to recreate a classpath from a classloader.
71+
*
72+
* BEWARE: with exotic enough classloaders, this may not work at all or do
73+
* the wrong thing.
74+
*/
75+
private def classpathFromClassloader(cl: ClassLoader): String = {
76+
@tailrec
77+
def loop(cl: ClassLoader, suffixClasspath: String): String =
78+
cl match {
79+
case cl: URLClassLoader =>
80+
val updatedClasspath = cl.getURLs
81+
.map(url => Paths.get(url.toURI).toAbsolutePath.toString)
82+
.mkString(
83+
"",
84+
File.pathSeparator,
85+
if (suffixClasspath.isEmpty) "" else File.pathSeparator + suffixClasspath
86+
)
87+
loop(cl.getParent, updatedClasspath)
88+
case _ =>
89+
suffixClasspath
90+
}
91+
92+
loop(cl, "")
7893
}
7994
}
8095

0 commit comments

Comments
 (0)