mirror of https://github.com/sbt/sbt.git
Replace Scala jars in UpdateReport with ScalaProvider jars in more situations. Fixes #661.
Specifically, when the Scala version for sbt is the same as that for the project being built, the jars in UpdateReport should be the same as those in ScalaProvider. This is because the loader will come from the ScalaProvider, which uses jars in the boot directory instead of the cache. The first part of the fix for #661 checks that loaded classes come from the classpath and so they need to line up.
This commit is contained in:
parent
bd0f208302
commit
5b5577a187
|
|
@ -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.*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue