mirror of
https://github.com/sbt/sbt.git
synced 2026-08-22 14:17:13 +02:00
Merge pull request #1584 from sbt/wip/fix-test-classloader-issues
Fix issues with specifying scalaHome/scalaInstance and running tests
This commit is contained in:
@@ -2,7 +2,7 @@ package sbt
|
|||||||
|
|
||||||
import internals.{
|
import internals.{
|
||||||
DslEntry,
|
DslEntry,
|
||||||
DslSetting,
|
DslConfigs,
|
||||||
DslEnablePlugins,
|
DslEnablePlugins,
|
||||||
DslDisablePlugins
|
DslDisablePlugins
|
||||||
}
|
}
|
||||||
@@ -10,4 +10,6 @@ import internals.{
|
|||||||
package object dsl {
|
package object dsl {
|
||||||
def enablePlugins(ps: AutoPlugin*): DslEntry = DslEnablePlugins(ps)
|
def enablePlugins(ps: AutoPlugin*): DslEntry = DslEnablePlugins(ps)
|
||||||
def disablePlugins(ps: AutoPlugin*): DslEntry = DslDisablePlugins(ps)
|
def disablePlugins(ps: AutoPlugin*): DslEntry = DslDisablePlugins(ps)
|
||||||
|
def configs(cs: Configuration*): DslEntry = DslConfigs(cs)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -54,4 +54,8 @@ case class DslEnablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulatio
|
|||||||
case class DslDisablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation {
|
case class DslDisablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation {
|
||||||
override val toFunction: Project => Project = _.disablePlugins(plugins: _*)
|
override val toFunction: Project => Project = _.disablePlugins(plugins: _*)
|
||||||
}
|
}
|
||||||
|
/** Represents registering a set of configurations with the current project. */
|
||||||
|
case class DslConfigs(cs: Seq[Configuration]) extends ProjectManipulation {
|
||||||
|
override val toFunction: Project => Project = _.configs(cs: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
lazy val OtherScala = config("other-scala").hide
|
||||||
|
|
||||||
|
configs(OtherScala)
|
||||||
|
|
||||||
|
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.1" % OtherScala.name
|
||||||
|
|
||||||
|
managedClasspath in OtherScala := Classpaths.managedJars(OtherScala, classpathTypes.value, update.value)
|
||||||
|
|
||||||
|
// Hack in the scala instance
|
||||||
|
scalaInstance := {
|
||||||
|
val rawJars = (managedClasspath in OtherScala).value.map(_.data)
|
||||||
|
val scalaHome = (target.value / "scala-home")
|
||||||
|
def removeVersion(name: String): String =
|
||||||
|
name.replaceAll("\\-2.11.1", "")
|
||||||
|
for(jar <- rawJars) {
|
||||||
|
val tjar = scalaHome / s"lib/${removeVersion(jar.getName)}"
|
||||||
|
IO.copyFile(jar, tjar)
|
||||||
|
}
|
||||||
|
IO.listFiles(scalaHome).foreach(f => System.err.println(s" * $f}"))
|
||||||
|
ScalaInstance(scalaHome, appConfiguration.value.provider.scalaProvider.launcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
|
||||||
|
|
||||||
|
libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.3.3" % "test"
|
||||||
|
|
||||||
|
scalaVersion := "2.11.0"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package akka.actor
|
||||||
|
|
||||||
|
import org.junit._
|
||||||
|
|
||||||
|
class BadTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def testCpIssue(): Unit = {
|
||||||
|
// TODO - This is merely the laziest way to run the test. What we want to do:
|
||||||
|
// * Load something from our own classloader that's INSIDE the scala library
|
||||||
|
// * Try to load that same something from the THREAD CONTEXT classloader.
|
||||||
|
// * Ensure we can do both, i.e. the second used to be filtered and broken.
|
||||||
|
val current = Thread.currentThread.getContextClassLoader
|
||||||
|
val mine = this.getClass.getClassLoader
|
||||||
|
val system = ActorSystem()
|
||||||
|
def evilGetThreadExectionContextName =
|
||||||
|
system.asInstanceOf[ActorSystemImpl].internalCallingThreadExecutionContext.getClass.getName
|
||||||
|
val expected = "scala.concurrent.Future$InternalCallbackExecutor$"
|
||||||
|
Assert.assertEquals("Failed to grab appropriate Akka name", expected, evilGetThreadExectionContextName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
> test
|
||||||
@@ -191,7 +191,12 @@ object TestFramework {
|
|||||||
val notInterfaceFilter = (name: String) => !interfaceFilter(name)
|
val notInterfaceFilter = (name: String) => !interfaceFilter(name)
|
||||||
val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
|
val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
|
||||||
val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir)
|
val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir)
|
||||||
ClasspathUtilities.filterByClasspath(interfaceJar +: classpath, main)
|
// TODO - There's actually an issue with the classpath facility such that unmanagedScalaInstances are not added
|
||||||
|
// to the classpath correctly. We have a temporary workaround here.
|
||||||
|
val cp: Seq[File] =
|
||||||
|
if (scalaInstance.isManagedVersion) interfaceJar +: classpath
|
||||||
|
else scalaInstance.allJars ++ (interfaceJar +: classpath)
|
||||||
|
ClasspathUtilities.filterByClasspath(cp, main)
|
||||||
}
|
}
|
||||||
def createTestFunction(loader: ClassLoader, taskDef: TaskDef, runner: TestRunner, testTask: TestTask): TestFunction =
|
def createTestFunction(loader: ClassLoader, taskDef: TaskDef, runner: TestRunner, testTask: TestTask): TestFunction =
|
||||||
new TestFunction(taskDef, runner, (r: TestRunner) => withContextLoader(loader) { r.run(taskDef, testTask) }) { def tags = testTask.tags }
|
new TestFunction(taskDef, runner, (r: TestRunner) => withContextLoader(loader) { r.run(taskDef, testTask) }) { def tags = testTask.tags }
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ final class ScalaInstance(val version: String, val loader: ClassLoader, val libr
|
|||||||
@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 compilerJar: File,
|
||||||
@deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0") val extraJars: Seq[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 {
|
val explicitActual: Option[String]) extends xsbti.compile.ScalaInstance {
|
||||||
|
/**
|
||||||
|
* This tells us if the scalaInstance is from a managed (i.e. ivy-resolved) scala *or*
|
||||||
|
* if it's a free-floating ScalaInstance, in which case we need to do tricks to the classpaths we find
|
||||||
|
* because it won't be on them.
|
||||||
|
*/
|
||||||
|
final def isManagedVersion = explicitActual.isDefined
|
||||||
// These are to implement xsbti.ScalaInstance
|
// These are to implement xsbti.ScalaInstance
|
||||||
@deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0")
|
@deprecated("Only `allJars` and `jars` can be reliably provided for modularized Scala.", "0.13.0")
|
||||||
def otherJars: Array[File] = extraJars.toArray
|
def otherJars: Array[File] = extraJars.toArray
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ final class SelfFirstLoader(classpath: Seq[URL], parent: ClassLoader) extends Lo
|
|||||||
|
|
||||||
/** Doesn't load any classes itself, but instead verifies that all classes loaded through `parent` either come from `root` or `classpath`.*/
|
/** Doesn't load any classes itself, but instead verifies that all classes loaded through `parent` either come from `root` or `classpath`.*/
|
||||||
final class ClasspathFilter(parent: ClassLoader, root: ClassLoader, classpath: Set[File]) extends ClassLoader(parent) {
|
final class ClasspathFilter(parent: ClassLoader, root: ClassLoader, classpath: Set[File]) extends ClassLoader(parent) {
|
||||||
|
override def toString =
|
||||||
|
s"""|ClasspathFilter(
|
||||||
|
| parent = $parent
|
||||||
|
| root = $root
|
||||||
|
| cp = $classpath
|
||||||
|
|)""".stripMargin
|
||||||
|
|
||||||
private[this] val directories: Seq[File] = classpath.toSeq.filter(_.isDirectory)
|
private[this] val directories: Seq[File] = classpath.toSeq.filter(_.isDirectory)
|
||||||
override def loadClass(className: String, resolve: Boolean): Class[_] =
|
override def loadClass(className: String, resolve: Boolean): Class[_] =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,13 @@ object ClasspathUtilities {
|
|||||||
new URLClassLoader(Path.toURLs(paths), parent) with RawResources with NativeCopyLoader {
|
new URLClassLoader(Path.toURLs(paths), parent) with RawResources with NativeCopyLoader {
|
||||||
override def resources = resourceMap
|
override def resources = resourceMap
|
||||||
override val config = new NativeCopyConfig(nativeTemp, paths, javaLibraryPaths)
|
override val config = new NativeCopyConfig(nativeTemp, paths, javaLibraryPaths)
|
||||||
|
override def toString =
|
||||||
|
s"""|URLClassLoader with NativeCopyLoader with RawResources(
|
||||||
|
| urls = $paths,
|
||||||
|
| parent = $parent,
|
||||||
|
| resourceMap = ${resourceMap.keySet},
|
||||||
|
| nativeTemp = $nativeTemp
|
||||||
|
|)""".stripMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
def javaLibraryPaths: Seq[File] = IO.parseClasspath(System.getProperty("java.library.path"))
|
def javaLibraryPaths: Seq[File] = IO.parseClasspath(System.getProperty("java.library.path"))
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ final class NullLoader extends ClassLoader {
|
|||||||
override final def loadClass(className: String, resolve: Boolean): Class[_] = throw new ClassNotFoundException("No classes can be loaded from the null loader")
|
override final def loadClass(className: String, resolve: Boolean): Class[_] = throw new ClassNotFoundException("No classes can be loaded from the null loader")
|
||||||
override def getResource(name: String): URL = null
|
override def getResource(name: String): URL = null
|
||||||
override def getResources(name: String): Enumeration[URL] = null
|
override def getResources(name: String): Enumeration[URL] = null
|
||||||
|
override def toString = "NullLoader"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Exception thrown when `loaderA` and `loaderB` load a different Class for the same name. */
|
/** Exception thrown when `loaderA` and `loaderB` load a different Class for the same name. */
|
||||||
@@ -84,6 +85,8 @@ class DualLoader(parentA: ClassLoader, aOnlyClasses: String => Boolean, aOnlyRes
|
|||||||
new DualEnumeration(urlsA, urlsB)
|
new DualEnumeration(urlsA, urlsB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def toString = s"DualLoader(a = $parentA, b = $parentB)"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Concatenates `a` and `b` into a single `Enumeration`.*/
|
/** Concatenates `a` and `b` into a single `Enumeration`.*/
|
||||||
|
|||||||
Reference in New Issue
Block a user