mirror of https://github.com/sbt/sbt.git
Provide application and boot classpath to running code through resources.
This commit is contained in:
parent
5e8e72a965
commit
600053bf2c
|
|
@ -1,5 +1,5 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2008, 2009 Mark Harrah
|
||||
* Copyright 2008, 2009, 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
|
|
@ -8,6 +8,8 @@ import java.net.{URI, URL, URLClassLoader}
|
|||
import java.util.Collections
|
||||
import scala.collection.Set
|
||||
import scala.collection.mutable.{HashSet, ListBuffer}
|
||||
import xsbt.ScalaInstance
|
||||
import xsbt.FileUtilities.{createTemporaryDirectory, withTemporaryDirectory, write}
|
||||
|
||||
object ClasspathUtilities
|
||||
{
|
||||
|
|
@ -15,7 +17,7 @@ object ClasspathUtilities
|
|||
def toClasspath(paths: Iterable[Path]): Array[URL] = Path.getURLs(paths)
|
||||
def toLoader(finder: PathFinder): ClassLoader = toLoader(finder.get)
|
||||
def toLoader(finder: PathFinder, parent: ClassLoader): ClassLoader = toLoader(finder.get, parent)
|
||||
def toLoader(paths: Iterable[Path]): ClassLoader = new URLClassLoader(toClasspath(paths), rootLoader)
|
||||
def toLoader(paths: Iterable[Path]): ClassLoader = toLoader(paths, rootLoader)
|
||||
def toLoader(paths: Iterable[Path], parent: ClassLoader): ClassLoader = new URLClassLoader(toClasspath(paths), parent)
|
||||
|
||||
lazy val rootLoader =
|
||||
|
|
@ -28,6 +30,32 @@ object ClasspathUtilities
|
|||
parent(getClass.getClassLoader)
|
||||
}
|
||||
|
||||
final val AppClassPath = "app.class.path"
|
||||
final val BootClassPath = "boot.class.path"
|
||||
|
||||
def createClasspathResources(classpath: Iterable[Path], instance: ScalaInstance, baseDir: Path): Iterable[Path] =
|
||||
createClasspathResources(classpath ++ Path.fromFiles(instance.jars), Path.fromFiles(instance.jars), baseDir)
|
||||
|
||||
def createClasspathResources(appPaths: Iterable[Path], bootPaths: Iterable[Path], baseDir: Path): Iterable[Path] =
|
||||
{
|
||||
def writePaths(name: String, paths: Iterable[Path]): Unit =
|
||||
write(baseDir / name asFile, Path.makeString(paths))
|
||||
writePaths(AppClassPath, appPaths)
|
||||
writePaths(BootClassPath, bootPaths)
|
||||
appPaths ++ Seq(baseDir)
|
||||
}
|
||||
|
||||
/** The client is responsible for cleaning up the temporary directory.*/
|
||||
def makeLoader[T](classpath: Iterable[Path], instance: ScalaInstance): (ClassLoader, Path) =
|
||||
makeLoader(classpath, instance.loader, instance)
|
||||
/** The client is responsible for cleaning up the temporary directory.*/
|
||||
def makeLoader[T](classpath: Iterable[Path], parent: ClassLoader, instance: ScalaInstance): (ClassLoader, Path) =
|
||||
{
|
||||
val dir = Path.fromFile(createTemporaryDirectory)
|
||||
val modifiedClasspath = createClasspathResources(classpath, instance, dir)
|
||||
(toLoader(modifiedClasspath, parent), dir)
|
||||
}
|
||||
|
||||
private[sbt] def printSource(c: Class[_]) =
|
||||
println(c.getName + " loader=" +c.getClassLoader + " location=" + FileUtilities.classLocationFile(c))
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,8 @@ abstract class BasicScalaProject extends ScalaProject with BasicDependencyProjec
|
|||
protected def getFingerprints(frameworks: Seq[TestFramework]): Fingerprints =
|
||||
{
|
||||
import org.scalatools.testing.{SubclassFingerprint, AnnotatedFingerprint}
|
||||
val loader = TestFramework.createTestLoader(classpath.get, buildScalaInstance.loader)
|
||||
val (loader, tempDir) = TestFramework.createTestLoader(classpath.get, buildScalaInstance)
|
||||
xsbt.FileUtilities.delete(tempDir.asFile)
|
||||
val annotations = new ListBuffer[String]
|
||||
val superclasses = new ListBuffer[String]
|
||||
frameworks flatMap { _.create(loader, log) } flatMap(TestFramework.getTests) foreach {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import scala.tools.nsc.util.ClassPath
|
|||
|
||||
import java.io.File
|
||||
import java.net.{URL, URLClassLoader}
|
||||
import java.lang.reflect.Modifier.{isPublic, isStatic}
|
||||
import java.lang.reflect.{Method, Modifier}
|
||||
import Modifier.{isPublic, isStatic}
|
||||
|
||||
trait ScalaRun
|
||||
{
|
||||
|
|
@ -51,9 +52,17 @@ class Run(instance: xsbt.ScalaInstance) extends ScalaRun
|
|||
}
|
||||
private def run0(mainClassName: String, classpath: Iterable[Path], options: Seq[String], log: Logger)
|
||||
{
|
||||
val loader = getLoader(classpath, log)
|
||||
val main = getMainMethod(mainClassName, loader)
|
||||
|
||||
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
|
||||
val (loader, tempDir) = ClasspathUtilities.makeLoader(classpath, instance)
|
||||
try
|
||||
{
|
||||
val main = getMainMethod(mainClassName, loader)
|
||||
invokeMain(loader, main, options)
|
||||
}
|
||||
finally { xsbt.FileUtilities.delete(tempDir asFile) }
|
||||
}
|
||||
private def invokeMain(loader: ClassLoader, main: Method, options: Seq[String])
|
||||
{
|
||||
val currentThread = Thread.currentThread
|
||||
val oldLoader = Thread.currentThread.getContextClassLoader()
|
||||
currentThread.setContextClassLoader(loader)
|
||||
|
|
@ -69,11 +78,6 @@ class Run(instance: xsbt.ScalaInstance) extends ScalaRun
|
|||
if(!isStatic(modifiers)) throw new NoSuchMethodException(mainClassName + ".main is not static")
|
||||
method
|
||||
}
|
||||
def getLoader(classpath: Iterable[Path], log: Logger) =
|
||||
{
|
||||
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
|
||||
ClasspathUtilities.toLoader(classpath, instance.loader)
|
||||
}
|
||||
}
|
||||
|
||||
/** This module is an interface to starting the scala interpreter or runner.*/
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ trait ScalaProject extends SimpleScalaProject with FileTasks with MultiTaskProje
|
|||
}
|
||||
def includeTest(test: TestDefinition) = !excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name))
|
||||
val tests = HashSet.empty[TestDefinition] ++ analysis.allTests.map(_.toDefinition).filter(includeTest)
|
||||
TestFramework.testTasks(frameworks, classpath.get, buildScalaInstance.loader, tests.toSeq, log,
|
||||
TestFramework.testTasks(frameworks, classpath.get, buildScalaInstance, tests.toSeq, log,
|
||||
testListeners.readOnly, false, setup.readOnly, cleanup.readOnly, testArgsByFramework)
|
||||
}
|
||||
private def flatten[T](i: Iterable[Iterable[T]]) = i.flatMap(x => x)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package sbt
|
|||
import java.net.URLClassLoader
|
||||
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, SubclassFingerprint, TestFingerprint}
|
||||
import org.scalatools.testing.{Event, EventHandler, Framework, Runner, Runner2, Logger=>TLogger}
|
||||
import xsbt.ScalaInstance
|
||||
|
||||
object Result extends Enumeration
|
||||
{
|
||||
|
|
@ -120,7 +121,7 @@ object TestFramework
|
|||
|
||||
def testTasks(frameworks: Seq[TestFramework],
|
||||
classpath: Iterable[Path],
|
||||
scalaLoader: ClassLoader,
|
||||
scalaInstance: ScalaInstance,
|
||||
tests: Seq[TestDefinition],
|
||||
log: Logger,
|
||||
listeners: Seq[TestReportListener],
|
||||
|
|
@ -130,16 +131,17 @@ object TestFramework
|
|||
testArgsByFramework: Map[TestFramework, Seq[String]]):
|
||||
(Iterable[NamedTestTask], Iterable[NamedTestTask], Iterable[NamedTestTask]) =
|
||||
{
|
||||
val loader = createTestLoader(classpath, scalaLoader)
|
||||
val (loader, tempDir) = createTestLoader(classpath, scalaInstance)
|
||||
val arguments = immutable.Map() ++
|
||||
( for(framework <- frameworks; created <- framework.create(loader, log)) yield
|
||||
(created, testArgsByFramework.getOrElse(framework, Nil)) )
|
||||
val cleanTmp = () => FileUtilities.clean(tempDir, log)
|
||||
|
||||
val mappedTests = testMap(arguments.keys.toList, tests, arguments)
|
||||
if(mappedTests.isEmpty)
|
||||
(new NamedTestTask(TestStartName, None) :: Nil, Nil, new NamedTestTask(TestFinishName, { log.info("No tests to run."); None }) :: Nil )
|
||||
(new NamedTestTask(TestStartName, None) :: Nil, Nil, new NamedTestTask(TestFinishName, { log.info("No tests to run."); cleanTmp() }) :: Nil )
|
||||
else
|
||||
createTestTasks(loader, mappedTests, log, listeners, endErrorsEnabled, setup, cleanup)
|
||||
createTestTasks(loader, mappedTests, log, listeners, endErrorsEnabled, setup, Seq(cleanTmp) ++ cleanup)
|
||||
}
|
||||
|
||||
private def testMap(frameworks: Seq[Framework], tests: Seq[TestDefinition], args: Map[Framework, Seq[String]]):
|
||||
|
|
@ -221,12 +223,12 @@ object TestFramework
|
|||
val endTask = new NamedTestTask(TestFinishName, end() ) :: createTasks(cleanup, "Test cleanup")
|
||||
(startTask, testTasks, endTask)
|
||||
}
|
||||
def createTestLoader(classpath: Iterable[Path], scalaLoader: ClassLoader): ClassLoader =
|
||||
def createTestLoader(classpath: Iterable[Path], scalaInstance: ScalaInstance): (ClassLoader, Path) =
|
||||
{
|
||||
val filterCompilerLoader = new FilteredLoader(scalaLoader, ScalaCompilerJarPackages)
|
||||
val filterCompilerLoader = new FilteredLoader(scalaInstance.loader, ScalaCompilerJarPackages)
|
||||
val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.")
|
||||
val notInterfaceFilter = (name: String) => !interfaceFilter(name)
|
||||
val dual = new xsbt.DualLoader(filterCompilerLoader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
|
||||
ClasspathUtilities.toLoader(classpath, dual)
|
||||
ClasspathUtilities.makeLoader(classpath, dual, scalaInstance)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
project.name=Run And Compiler
|
||||
project.version=1.0
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import sbt._
|
||||
|
||||
class A(info: ProjectInfo) extends DefaultProject(info)
|
||||
{
|
||||
val snaps = ScalaToolsSnapshots
|
||||
val specs28 = "org.scala-tools.testing" %% "specs" % "1.6.5-SNAPSHOT" % "test"
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package foo.bar
|
||||
|
||||
import java.io.File
|
||||
import File.{pathSeparator => / }
|
||||
import scala.io.Source
|
||||
|
||||
class Holder { var value: Any = _ }
|
||||
|
||||
import scala.tools.nsc.{GenericRunnerSettings, Interpreter, Settings}
|
||||
|
||||
class Foo {
|
||||
val g = new GenericRunnerSettings(System.err.println)
|
||||
val settings = new Settings()
|
||||
val loader = getClass.getClassLoader
|
||||
settings.classpath.value = classpath("app", loader).getOrElse(error("Error: could not find application classpath"))
|
||||
settings.bootclasspath.value = settings.bootclasspath.value + / + classpath("boot", loader).getOrElse(error("Error: could not find boot classpath"))
|
||||
val inter = new Interpreter(settings) {
|
||||
override protected def parentClassLoader = Foo.this.getClass.getClassLoader
|
||||
}
|
||||
def eval(code: String): Any = {
|
||||
val h = new Holder
|
||||
inter.bind("$r_", h.getClass.getName, h)
|
||||
val r = inter.interpret("$r_.value = " + code)
|
||||
h.value
|
||||
}
|
||||
|
||||
private def classpath(name: String, loader: ClassLoader) =
|
||||
Option(loader.getResource(name + ".class.path")).map { cp =>
|
||||
Source.fromURL(cp).mkString
|
||||
}
|
||||
}
|
||||
|
||||
object Test
|
||||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
val foo = new Foo
|
||||
args.foreach { arg => foo.eval(arg) == arg.toInt }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package foo.bar
|
||||
|
||||
import org.specs._
|
||||
|
||||
class ATest extends Specification
|
||||
{
|
||||
"application and boot classpath" should {
|
||||
"be provided to running applications and tests" in {
|
||||
val foo = new Foo
|
||||
val numbers = List[Any](1,2,5, 19)
|
||||
numbers.map(i => foo.eval(i.toString)) must haveTheSameElementsAs(numbers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
> ++2.8.0.RC2
|
||||
> update
|
||||
> test
|
||||
Loading…
Reference in New Issue