diff --git a/build.sbt b/build.sbt index d05ce24475..15c5e40b59 100644 --- a/build.sbt +++ b/build.sbt @@ -337,6 +337,7 @@ lazy val compilerInterface = (project in internalPath / "compiler-interface") import com.typesafe.tools.mima.core.ProblemFilters._ Seq( exclude[ReversedMissingMethodProblem]("xsbti.compile.ExternalHooks#Lookup.hashClasspath"), + exclude[ReversedMissingMethodProblem]("xsbti.compile.ScalaInstance.loaderLibraryOnly"), ) }, ) diff --git a/internal/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java b/internal/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java index bee8abcf8f..f213edc25e 100644 --- a/internal/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java +++ b/internal/compiler-interface/src/main/java/xsbti/compile/ScalaInstance.java @@ -31,12 +31,13 @@ public interface ScalaInstance { /** A class loader providing access to the classes and resources in the library and compiler jars. */ ClassLoader loader(); - /** @deprecated Only `jars` can be reliably provided for modularized Scala (since 0.13.0). */ - @Deprecated + /** A class loader providing access to the classes and resources in the library. */ + ClassLoader loaderLibraryOnly(); + + /** Only `jars` can be reliably provided for modularized Scala. */ File libraryJar(); - /** @deprecated Only `jars` can be reliably provided for modularized Scala (since 0.13.0). */ - @Deprecated + /** Only `jars` can be reliably provided for modularized Scala. */ File compilerJar(); /** @deprecated Only `jars` can be reliably provided for modularized Scala (since 0.13.0). */ diff --git a/internal/zinc-classpath/src/main/scala/sbt/internal/inc/ScalaInstance.scala b/internal/zinc-classpath/src/main/scala/sbt/internal/inc/ScalaInstance.scala index 1de97f80d9..8909e6b406 100644 --- a/internal/zinc-classpath/src/main/scala/sbt/internal/inc/ScalaInstance.scala +++ b/internal/zinc-classpath/src/main/scala/sbt/internal/inc/ScalaInstance.scala @@ -12,6 +12,8 @@ package inc import java.io.File import xsbti.ArtifactInfo.ScalaOrganization import sbt.io.IO +import scala.language.reflectiveCalls +import sbt.internal.inc.classpath.ClasspathUtilities /** * A Scala instance encapsulates all the information that is bound to a concrete @@ -35,12 +37,33 @@ import sbt.io.IO final class ScalaInstance( val version: String, val loader: ClassLoader, + val loaderLibraryOnly: ClassLoader, val libraryJar: File, val compilerJar: File, val allJars: Array[File], val explicitActual: Option[String] ) extends xsbti.compile.ScalaInstance { + @deprecated("Use constructor with loaderLibraryOnly", "1.1.2") + def this( + version: String, + loader: ClassLoader, + libraryJar: File, + compilerJar: File, + allJars: Array[File], + explicitActual: Option[String] + ) { + this( + version, + loader, + ClasspathUtilities.rootLoader, + libraryJar, + compilerJar, + allJars, + explicitActual + ) + } + /** * Check whether `scalaInstance` comes from a managed (i.e. ivy-resolved) * scala **or** if it's a free-floating `ScalaInstance`, in which case we @@ -75,7 +98,13 @@ final class ScalaInstance( override def toString: String = s"Scala instance { version label $version, actual version $actualVersion, $jarStrings }" } + object ScalaInstance { + /* + * Structural extention for the ScalaProvider post 1.0.3 launcher. + * See https://github.com/sbt/zinc/pull/505. + */ + private type ScalaProvider2 = { def loaderLibraryOnly: ClassLoader } /** Name of scala organisation to be used for artifact resolution. */ val ScalaOrg = ScalaOrganization @@ -123,30 +152,47 @@ object ScalaInstance { } } val jars = provider.jars - val loader = provider.loader val libraryJar = findOrCrash(jars, "scala-library.jar") val compilerJar = findOrCrash(jars, "scala-compiler.jar") - new ScalaInstance(version, loader, libraryJar, compilerJar, jars, None) + def fallbackClassLoaders = { + val l = ClasspathUtilities.toLoader(Vector(libraryJar)) + val c = scalaLoader(l)(jars.toVector filterNot { _ == libraryJar }) + (c, l) + } + // sbt launcher 1.0.3 will construct layered classloader. Use them if we find them. + // otherwise, construct layered loaders manually. + val (loader, loaderLibraryOnly) = { + (try { + provider match { + case p: ScalaProvider2 @unchecked => Option((provider.loader, p.loaderLibraryOnly)) + } + } catch { + case _: NoSuchMethodError => None + }) getOrElse fallbackClassLoaders + } + new ScalaInstance(version, loader, loaderLibraryOnly, libraryJar, compilerJar, jars, None) } def apply(scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = - apply(scalaHome)(scalaLoader(launcher)) + apply(scalaHome)(scalaLibraryLoader(launcher)) def apply(scalaHome: File)(classLoader: List[File] => ClassLoader): ScalaInstance = { val all = allJars(scalaHome) - val loader = classLoader(all.toList) val library = libraryJar(scalaHome) + val loaderLibraryOnly = classLoader(List(library)) + val loader = scalaLoader(loaderLibraryOnly)(all.toVector filterNot { _ == library }) val version = actualVersion(loader)(" (library jar " + library.getAbsolutePath + ")") val compiler = compilerJar(scalaHome) - new ScalaInstance(version, loader, library, compiler, all.toArray, None) + new ScalaInstance(version, loader, loaderLibraryOnly, library, compiler, all.toArray, None) } def apply(version: String, scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = { val all = allJars(scalaHome) - val loader = scalaLoader(launcher)(all.toList) val library = libraryJar(scalaHome) + val loaderLibraryOnly = scalaLibraryLoader(launcher)(List(library)) + val loader = scalaLoader(loaderLibraryOnly)(all.toVector) val compiler = compilerJar(scalaHome) - new ScalaInstance(version, loader, library, compiler, all.toArray, None) + new ScalaInstance(version, loader, loaderLibraryOnly, library, compiler, all.toArray, None) } /** Return all the required Scala jars from a path `scalaHome`. */ @@ -209,12 +255,12 @@ object ScalaInstance { } finally stream.close() } - private def scalaLoader(launcher: xsbti.Launcher): Seq[File] => ClassLoader = { jars => - import java.net.{ URL, URLClassLoader } - new URLClassLoader( - jars.map(_.toURI.toURL).toArray[URL], - launcher.topLoader - ) + private def scalaLibraryLoader(launcher: xsbti.Launcher): Seq[File] => ClassLoader = { jars => + ClasspathUtilities.toLoader(jars, launcher.topLoader) + } + + private def scalaLoader(parent: ClassLoader): Seq[File] => ClassLoader = { jars => + ClasspathUtilities.toLoader(jars, parent) } } diff --git a/internal/zinc-classpath/src/main/scala/sbt/internal/inc/classpath/ClasspathUtilities.scala b/internal/zinc-classpath/src/main/scala/sbt/internal/inc/classpath/ClasspathUtilities.scala index e9f78b3691..1aca97f0df 100644 --- a/internal/zinc-classpath/src/main/scala/sbt/internal/inc/classpath/ClasspathUtilities.scala +++ b/internal/zinc-classpath/src/main/scala/sbt/internal/inc/classpath/ClasspathUtilities.scala @@ -63,8 +63,10 @@ object ClasspathUtilities { final val AppClassPath = "app.class.path" final val BootClassPath = "boot.class.path" - def createClasspathResources(classpath: Seq[File], instance: ScalaInstance): Map[String, String] = - createClasspathResources(classpath, instance.allJars) + def createClasspathResources(classpath: Seq[File], + instance: ScalaInstance): Map[String, String] = { + createClasspathResources(classpath, Array(instance.libraryJar)) + } def createClasspathResources(appPaths: Seq[File], bootPaths: Seq[File]): Map[String, String] = { def make(name: String, paths: Seq[File]) = name -> Path.makeString(paths) @@ -74,11 +76,16 @@ object ClasspathUtilities { private[sbt] def filterByClasspath(classpath: Seq[File], loader: ClassLoader): ClassLoader = new ClasspathFilter(loader, xsbtiLoader, classpath.toSet) + /** + * Creates a ClassLoader that contains the classpath and the scala-library from + * the given instance. + */ def makeLoader(classpath: Seq[File], instance: ScalaInstance): ClassLoader = - filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance)) + filterByClasspath(classpath, makeLoader(classpath, instance.loaderLibraryOnly, instance)) def makeLoader(classpath: Seq[File], instance: ScalaInstance, nativeTemp: File): ClassLoader = - filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance, nativeTemp)) + filterByClasspath(classpath, + makeLoader(classpath, instance.loaderLibraryOnly, instance, nativeTemp)) def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance): ClassLoader = toLoader(classpath, parent, createClasspathResources(classpath, instance)) diff --git a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala index 234a9c3a9b..f516720380 100644 --- a/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala +++ b/internal/zinc-ivy-integration/src/main/scala/sbt/internal/inc/ZincComponentCompiler.scala @@ -140,11 +140,19 @@ private[sbt] object ZincComponentCompiler { val scalaLibrary = scalaArtifacts.library val jarsToLoad = (scalaCompiler +: scalaLibrary +: scalaArtifacts.others).toArray assert(jarsToLoad.forall(_.exists), "One or more jar(s) in the Scala instance do not exist.") - val loader = new URLClassLoader(toURLs(jarsToLoad), ClasspathUtilities.rootLoader) + val loaderLibraryOnly = ClasspathUtilities.toLoader(Vector(scalaLibrary)) + val loader = ClasspathUtilities.toLoader(jarsToLoad.toVector filterNot { _ == scalaLibrary }, + loaderLibraryOnly) val properties = ResourceLoader.getSafePropertiesFor("compiler.properties", loader) val loaderVersion = Option(properties.getProperty("version.number")) val scalaV = loaderVersion.getOrElse("unknown") - new ScalaInstance(scalaV, loader, scalaLibrary, scalaCompiler, jarsToLoad, loaderVersion) + new ScalaInstance(scalaV, + loader, + loaderLibraryOnly, + scalaLibrary, + scalaCompiler, + jarsToLoad, + loaderVersion) } }