diff --git a/interface/src/main/java/xsbti/compile/ScalaInstance.java b/interface/src/main/java/xsbti/compile/ScalaInstance.java index 4e41e1ca2..c7f3984e3 100644 --- a/interface/src/main/java/xsbti/compile/ScalaInstance.java +++ b/interface/src/main/java/xsbti/compile/ScalaInstance.java @@ -17,13 +17,16 @@ public interface ScalaInstance /** A class loader providing access to the classes and resources in the library and compiler jars. */ ClassLoader loader(); - /** The library jar file.*/ + /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ + @Deprecated File libraryJar(); - /** The compiler jar file.*/ + /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ + @Deprecated File compilerJar(); - /** Jars provided by this Scala instance other than the compiler and library jars. */ + /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ + @Deprecated File[] otherJars(); /** All jar files provided by this Scala instance.*/ diff --git a/launch/interface/src/main/java/xsbti/ScalaProvider.java b/launch/interface/src/main/java/xsbti/ScalaProvider.java index 381985c61..435625eee 100644 --- a/launch/interface/src/main/java/xsbti/ScalaProvider.java +++ b/launch/interface/src/main/java/xsbti/ScalaProvider.java @@ -13,10 +13,17 @@ public interface ScalaProvider public ClassLoader loader(); /** Returns the scala-library.jar and scala-compiler.jar for this version of Scala. */ public File[] jars(); + + /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ + @Deprecated public File libraryJar(); + + /**@deprecated Only `jars` can be reliably provided for modularized Scala. (Since 0.13.0) */ + @Deprecated public File compilerJar(); + /** Creates an application provider that will use 'loader()' as the parent ClassLoader for * the application given by 'id'. This method will retrieve the application if it has not already * been retrieved.*/ public AppProvider app(ApplicationID id); -} \ No newline at end of file +} diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index b68e9e2c3..f5ce27e6e 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -647,7 +647,8 @@ object Defaults extends BuildCommon def consoleTask(classpath: TaskKey[Classpath], task: TaskKey[_]): Initialize[Task[Unit]] = (compilers in task, classpath in task, scalacOptions in task, initialCommands in task, cleanupCommands in task, taskTemporaryDirectory in task, scalaInstance in task, streams) map { (cs, cp, options, initCommands, cleanup, temp, si, s) => - val loader = sbt.classpath.ClasspathUtilities.makeLoader(data(cp), si, IO.createUniqueDirectory(temp)) + val fullcp = (data(cp) ++ si.jars).distinct + val loader = sbt.classpath.ClasspathUtilities.makeLoader(fullcp, si, IO.createUniqueDirectory(temp)) (new Console(cs.scalac))(data(cp), options, loader, initCommands, cleanup)()(s.log).foreach(msg => error(msg)) println() } @@ -1027,12 +1028,20 @@ object Classpaths val depsUpdated = transitiveUpdate.value.exists(!_.stats.cached) val isRoot = executionRoots.value contains resolvedScoped.value val s = streams.value - val si = Defaults.unmanagedScalaInstanceOnly.value.map(si => (si, scalaOrganization.value)) + val subScalaJars: Seq[File] = Defaults.unmanagedScalaInstanceOnly.value match { + case Some(si) => si.jars + case None => + val scalaProvider = appConfiguration.value.provider.scalaProvider + // substitute the Scala jars from the provider so that when the provider's loader is used, + // the jars on the classpath match the jars used by the loader + if(scalaProvider.version == scalaVersion.value) scalaProvider.jars else Nil + } + val transform: UpdateReport => UpdateReport = if(subScalaJars.isEmpty) idFun else r => substituteScalaFiles(subScalaJars, scalaOrganization.value, r) val show = Reference.display(thisProjectRef.value) - cachedUpdate(s.cacheDirectory, show, ivyModule.value, updateConfiguration.value, si, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log) + cachedUpdate(s.cacheDirectory, show, ivyModule.value, updateConfiguration.value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log) } - def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration, scalaInstance: Option[(ScalaInstance, String)], skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport = + def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration, transform: UpdateReport=>UpdateReport, skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport = { implicit val updateCache = updateIC type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil @@ -1040,7 +1049,7 @@ object Classpaths log.info("Updating " + label + "...") val r = IvyActions.update(module, config, log) log.info("Done updating.") - scalaInstance match { case Some((si,scalaOrg)) => substituteScalaFiles(si, scalaOrg, r); case None => r } + transform(r) } def uptodate(inChanged: Boolean, out: UpdateReport): Boolean = !force && @@ -1263,6 +1272,7 @@ object Classpaths def unmanagedScalaLibrary: Initialize[Task[Seq[File]]] = Def.taskDyn { if(autoScalaLibrary.value && scalaHome.value.isDefined) + // TODO: what goes here when Scala library is modularized? Def.task { scalaInstance.value.libraryJar :: Nil } else Def.task { Nil } @@ -1293,23 +1303,21 @@ object Classpaths @deprecated("Doesn't properly handle non-standard Scala organizations.", "0.13.0") def substituteScalaFiles(scalaInstance: ScalaInstance, report: UpdateReport): UpdateReport = substituteScalaFiles(scalaInstance, ScalaArtifacts.Organization, report) - def substituteScalaFiles(scalaInstance: ScalaInstance, ScalaOrg: String, report: UpdateReport): UpdateReport = - { - val scalaJars = scalaInstance.jars + + @deprecated("Directly provide the jar files.", "0.13.0") + def substituteScalaFiles(scalaInstance: ScalaInstance, scalaOrg: String, report: UpdateReport): UpdateReport = + substituteScalaFiles(scalaInstance.jars, scalaOrg, report) + + def substituteScalaFiles(scalaJars: Seq[File], scalaOrg: String, report: UpdateReport): UpdateReport = report.substitute { (configuration, module, arts) => import ScalaArtifacts._ - (module.organization, module.name) match - { - case (ScalaOrg, LibraryID) => (Artifact(LibraryID), scalaInstance.libraryJar) :: Nil - case (ScalaOrg, CompilerID) => (Artifact(CompilerID), scalaInstance.compilerJar) :: Nil - case (ScalaOrg, id) => - val jarName = id + ".jar" - val replaceWith = scalaJars.filter(_.getName == jarName).map(f => (Artifact(f.getName), f)) - if(replaceWith.isEmpty) arts else replaceWith - case _ => arts - } + if(module.organization == scalaOrg) { + val jarName = module.name + ".jar" + val replaceWith = scalaJars.filter(_.getName == jarName).map(f => (Artifact(f.getName.stripSuffix(".jar")), f)) + if(replaceWith.isEmpty) arts else replaceWith + } else + arts } - } // try/catch for supporting earlier launchers def bootIvyHome(app: xsbti.AppConfiguration): Option[File] = diff --git a/util/classpath/src/main/scala/sbt/ScalaInstance.scala b/util/classpath/src/main/scala/sbt/ScalaInstance.scala index fcb24e9cf..66ab2f243 100644 --- a/util/classpath/src/main/scala/sbt/ScalaInstance.scala +++ b/util/classpath/src/main/scala/sbt/ScalaInstance.scala @@ -4,15 +4,27 @@ package sbt import java.io.File + import xsbti.ArtifactInfo.{ScalaCompilerID, ScalaLibraryID, ScalaOrganization} /** Represents the source for Scala classes for a given version. The reason both a ClassLoader and the jars are required * is that the compiler requires the location of the library/compiler jars on the (boot)classpath and the loader is used * for the compiler itself. * The 'version' field is the version used to obtain the Scala classes. This is typically the version for the maven repository. -* The 'actualVersion' field should be used to uniquely identify the compiler. It is obtained from the compiler.properties file.*/ -final class ScalaInstance(val version: String, val loader: ClassLoader, val libraryJar: File, val compilerJar: File, val extraJars: Seq[File], val explicitActual: Option[String]) extends xsbti.compile.ScalaInstance +* The 'actualVersion' field should be used to uniquely identify the compiler. It is obtained from the compiler.properties file. +* +* This should be constructed via the ScalaInstance.apply methods. The primary constructor is deprecated. +**/ +final class ScalaInstance(val version: String, val loader: ClassLoader, + @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") + val libraryJar: File, + @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") + val compilerJar: File, + @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") + val extraJars: Seq[File], + val explicitActual: Option[String]) extends xsbti.compile.ScalaInstance { // These are to implement xsbti.ScalaInstance + @deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") def otherJars: Array[File] = extraJars.toArray def allJars: Array[File] = jars.toArray @@ -25,7 +37,7 @@ final class ScalaInstance(val version: String, val loader: ClassLoader, val libr } object ScalaInstance { - val ScalaOrg = "org.scala-lang" + val ScalaOrg = ScalaOrganization val VersionPrefix = "version " def apply(org: String, version: String, launcher: xsbti.Launcher): ScalaInstance = @@ -35,7 +47,7 @@ object ScalaInstance else try { apply(version, launcher.getScala(version, "", org)) } catch { - case x: NoSuchMethodError => error("Incompatible version of the xsbti.Launcher interface. Use sbt-0.12.x launcher instead.") + case x: NoSuchMethodError => error("Incompatible version of the xsbti.Launcher interface. Use an sbt 0.12+ launcher instead.") } /** Creates a ScalaInstance using the given provider to obtain the jars and loader.*/ @@ -45,38 +57,59 @@ object ScalaInstance new ScalaInstance(version, provider.loader, provider.libraryJar, provider.compilerJar, (provider.jars.toSet - provider.libraryJar - provider.compilerJar).toSeq, None) def apply(scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = - apply(libraryJar(scalaHome), compilerJar(scalaHome), launcher, extraJars(scalaHome): _*) + apply(libraryJar(scalaHome), compilerJar(scalaHome), launcher, allJars(scalaHome): _*) + def apply(scalaHome: File)(classLoader: List[File] => ClassLoader): ScalaInstance = - apply(libraryJar(scalaHome), compilerJar(scalaHome), extraJars(scalaHome): _*)(classLoader) + apply(libraryJar(scalaHome), compilerJar(scalaHome), allJars(scalaHome): _*)(classLoader) def apply(version: String, scalaHome: File, launcher: xsbti.Launcher): ScalaInstance = - apply(version, libraryJar(scalaHome), compilerJar(scalaHome), launcher, extraJars(scalaHome) : _*) + apply(version, libraryJar(scalaHome), compilerJar(scalaHome), launcher, allJars(scalaHome) : _*) + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = apply(libraryJar, compilerJar, extraJars : _*)( scalaLoader(launcher) ) + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = { val loader = classLoader(libraryJar :: compilerJar :: extraJars.toList) val version = actualVersion(loader)(" (library jar " + libraryJar.getAbsolutePath + ")") new ScalaInstance(version, loader, libraryJar, compilerJar, extraJars, None) } + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(version: String, libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = apply(version, None, libraryJar, compilerJar, launcher, extraJars : _*) + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(version: String, libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = apply(version, None, libraryJar, compilerJar, extraJars : _*)(classLoader) + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, launcher: xsbti.Launcher, extraJars: File*): ScalaInstance = apply(version, explicitActual, libraryJar, compilerJar, extraJars : _*)( scalaLoader(launcher) ) + + @deprecated("Does not handle modularized Scala. Use a variant that only accepts all jars.", "0.13.0") def apply(version: String, explicitActual: Option[String], libraryJar: File, compilerJar: File, extraJars: File*)(classLoader: List[File] => ClassLoader): ScalaInstance = new ScalaInstance(version, classLoader(libraryJar :: compilerJar :: extraJars.toList), libraryJar, compilerJar, extraJars, explicitActual) + @deprecated("Cannot be reliably provided for modularized Scala.", "0.13.0") def extraJars(scalaHome: File): Seq[File] = optScalaJar(scalaHome, "jline.jar") ++ optScalaJar(scalaHome, "fjbg.jar") ++ optScalaJar(scalaHome, "scala-reflect.jar") + + def allJars(scalaHome: File): Seq[File] = IO.listFiles(scalaLib(scalaHome)).filter(f => !blacklist(f.getName)) + private[this] def scalaLib(scalaHome: File): File = new File(scalaHome, "lib") + private[this] val blacklist: Set[String] = Set("scala-actors.jar", "scalacheck.jar", "scala-partest.jar", "scala-partest-javaagent.jar", "scalap.jar", "scala-swing.jar") private def compilerJar(scalaHome: File) = scalaJar(scalaHome, "scala-compiler.jar") private def libraryJar(scalaHome: File) = scalaJar(scalaHome, "scala-library.jar") + @deprecated("No longer used.", "0.13.0") def scalaJar(scalaHome: File, name: String) = new File(scalaHome, "lib" + File.separator + name) + + @deprecated("No longer used.", "0.13.0") def optScalaJar(scalaHome: File, name: String): List[File] = { val jar = scalaJar(scalaHome, name) diff --git a/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala b/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala index 507b506a2..228228030 100644 --- a/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala +++ b/util/classpath/src/main/scala/sbt/classpath/ClassLoaders.scala @@ -56,9 +56,11 @@ final class ClasspathFilter(parent: ClassLoader, root: ClassLoader, classpath: S throw new ClassNotFoundException(className) } private[this] def fromClasspath(c: Class[_]): Boolean = - try { onClasspath(IO.classLocation(c)) } - catch { case e: RuntimeException => false } - + { + val codeSource = c.getProtectionDomain.getCodeSource + (codeSource eq null) || + onClasspath(codeSource.getLocation) + } private[this] def onClasspath(src: URL): Boolean = (src eq null) || ( IO.urlAsFile(src) match {