diff --git a/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala b/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala index 577ecbc..a8c63e7 100644 --- a/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala +++ b/launcher-implementation/src/main/scala/xsbt/boot/Launch.scala @@ -322,11 +322,18 @@ class Launch private[xsbt] (val bootDirectory: File, val lockBoot: Boolean, val } def componentProvider(appHome: File) = new ComponentProvider(appHome, lockBoot) - def scalaProvider(scalaVersion: String, module: RetrievedModule, parentLoader: ClassLoader, scalaLibDir: File): xsbti.ScalaProvider = new xsbti.ScalaProvider { + def scalaProvider(scalaVersion: String, module: RetrievedModule, parentLoader: ClassLoader, scalaLibDir: File): xsbti.ScalaProvider = new xsbti.ExtendedScalaProvider { def launcher = Launch.this def version = scalaVersion - lazy val loader = module.createLoader(parentLoader) + private object LoaderInit { + val (library, other) = module.fullClasspath.partition(_.getName.startsWith("scala-library")) + val libraryLoader = new LibraryClassLoader(toURLs(library), parentLoader, scalaVersion) + val fullLoader = new URLClassLoader(toURLs(other), libraryLoader) + } + + override def loaderLibraryOnly(): ClassLoader = LoaderInit.libraryLoader + override def loader(): ClassLoader = LoaderInit.fullLoader def compilerJar = new File(scalaLibDir, CompilerModuleName + ".jar") def libraryJar = new File(scalaLibDir, LibraryModuleName + ".jar") def jars = module.fullClasspath diff --git a/launcher-implementation/src/main/scala/xsbt/boot/LibraryClassLoader.scala b/launcher-implementation/src/main/scala/xsbt/boot/LibraryClassLoader.scala new file mode 100644 index 0000000..810db4f --- /dev/null +++ b/launcher-implementation/src/main/scala/xsbt/boot/LibraryClassLoader.scala @@ -0,0 +1,7 @@ +package xsbt.boot + +import java.net.{ URL, URLClassLoader } + +final class LibraryClassLoader(urls: Array[URL], parent: ClassLoader, + val scalaVersion: String) extends URLClassLoader(urls, parent) with xsbti.LibraryClassLoader { +} diff --git a/launcher-implementation/src/test/scala/ScalaProviderTest.scala b/launcher-implementation/src/test/scala/ScalaProviderTest.scala index 425468a..bf2344e 100644 --- a/launcher-implementation/src/test/scala/ScalaProviderTest.scala +++ b/launcher-implementation/src/test/scala/ScalaProviderTest.scala @@ -15,8 +15,10 @@ object ScalaProviderTest extends Specification { "provide ClassLoader for Scala 2.8.2" in { checkScalaLoader("2.8.2") } "provide ClassLoader for Scala 2.9.0" in { checkScalaLoader("2.9.0") } "provide ClassLoader for Scala 2.9.2" in { checkScalaLoader("2.9.2") } - "provide ClassLoader for Scala 2.10.4" in { checkScalaLoader("2.10.4") } - "provide ClassLoader for Scala 2.11.0" in { checkScalaLoader("2.11.0") } + "provide ClassLoader for Scala 2.10.7" in { checkScalaLoader("2.10.7") } + "provide ClassLoader for Scala 2.11.12" in { checkScalaLoader("2.11.12") } + // This requires the test to run in JDK 8 + // "provide ClassLoader for Scala 2.12.4" in { checkScalaLoader("2.12.4") } } "Launch" should { @@ -68,16 +70,26 @@ object ScalaProviderTest extends Specification { val provider = launcher.getScala(version) val loader = provider.loader // ensure that this loader can load Scala classes by trying scala.ScalaObject. - tryScala(loader) + tryScala(loader, loader.getParent) getScalaVersion(loader) must beEqualTo(versionValue) + + val libraryLoader = provider.loader.getParent + // Test the structural type + libraryLoader match { + case x: ClassLoader with LibraryLoader => x.scalaVersion must be(version) + } + tryScala(libraryLoader, libraryLoader) } - private def tryScala(loader: ClassLoader) = Class.forName("scala.Product", false, loader).getClassLoader must be(loader) + private def tryScala(loader: ClassLoader, libraryLoader: ClassLoader) = + Class.forName("scala.Product", false, loader).getClassLoader must be(libraryLoader) + + type LibraryLoader = { def scalaVersion: String } } object LaunchTest { def testApp(main: String): Application = testApp(main, Array[File]()) def testApp(main: String, extra: Array[File]): Application = Application("org.scala-sbt", "launch-test", new Explicit(AppVersion), main, Nil, CrossValue.Disabled, extra) import Predefined._ - def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined(_)) + def testRepositories = List(Local, MavenCentral, SonatypeOSSSnapshots).map(Repository.Predefined(_)) def withLauncher[T](f: xsbti.Launcher => T): T = withTemporaryDirectory { bootDirectory => f(Launcher(bootDirectory, testRepositories)) diff --git a/launcher-interface/src/main/java/xsbti/ExtendedScalaProvider.java b/launcher-interface/src/main/java/xsbti/ExtendedScalaProvider.java new file mode 100644 index 0000000..12755c9 --- /dev/null +++ b/launcher-interface/src/main/java/xsbti/ExtendedScalaProvider.java @@ -0,0 +1,7 @@ +package xsbti; + +public interface ExtendedScalaProvider extends ScalaProvider +{ + /** A ClassLoader that loads the classes from scala-library.jar. It will be the parent of `loader` .*/ + public ClassLoader loaderLibraryOnly(); +} diff --git a/launcher-interface/src/main/java/xsbti/LibraryClassLoader.java b/launcher-interface/src/main/java/xsbti/LibraryClassLoader.java new file mode 100644 index 0000000..ec80256 --- /dev/null +++ b/launcher-interface/src/main/java/xsbti/LibraryClassLoader.java @@ -0,0 +1,11 @@ +package xsbti; + +import java.io.File; + +/** + * Marker interface for classloader with just scala-library. + */ +public interface LibraryClassLoader +{ + public String scalaVersion(); +}