mirror of https://github.com/sbt/sbt.git
eliminate temporary directory for injecting resources into a class loader
This commit is contained in:
parent
fdb4a98d8b
commit
de3ad8c860
|
|
@ -11,11 +11,11 @@ import classpath.ClasspathUtilities
|
|||
|
||||
trait ScalaRun
|
||||
{
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger): Option[String]
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String]
|
||||
}
|
||||
class ForkRun(config: ForkScalaRun) extends ScalaRun
|
||||
{
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger): Option[String] =
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Option[String] =
|
||||
{
|
||||
val scalaOptions = classpathOption(classpath) ::: mainClass :: options.toList
|
||||
val exitCode = config.outputStrategy match {
|
||||
|
|
@ -24,7 +24,7 @@ class ForkRun(config: ForkScalaRun) extends ScalaRun
|
|||
}
|
||||
processExitCode(exitCode, "runner")
|
||||
}
|
||||
private def classpathOption(classpath: Iterable[Path]) = "-cp" :: Path.makeString(classpath) :: Nil
|
||||
private def classpathOption(classpath: Seq[File]) = "-cp" :: Path.makeString(classpath) :: Nil
|
||||
private def processExitCode(exitCode: Int, label: String) =
|
||||
{
|
||||
if(exitCode == 0)
|
||||
|
|
@ -36,7 +36,7 @@ class ForkRun(config: ForkScalaRun) extends ScalaRun
|
|||
class Run(instance: ScalaInstance) extends ScalaRun
|
||||
{
|
||||
/** Runs the class 'mainClass' using the given classpath and options using the scala runner.*/
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger) =
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger) =
|
||||
{
|
||||
log.info("Running " + mainClass + " " + options.mkString(" "))
|
||||
|
||||
|
|
@ -46,16 +46,12 @@ class Run(instance: ScalaInstance) extends ScalaRun
|
|||
|
||||
Run.executeTrapExit( execute, log )
|
||||
}
|
||||
private def run0(mainClassName: String, classpath: Iterable[Path], options: Seq[String], log: Logger)
|
||||
private def run0(mainClassName: String, classpath: Seq[File], options: Seq[String], log: Logger)
|
||||
{
|
||||
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 { IO.delete(tempDir asFile) }
|
||||
val loader = ClasspathUtilities.makeLoader(classpath, instance)
|
||||
val main = getMainMethod(mainClassName, loader)
|
||||
invokeMain(loader, main, options)
|
||||
}
|
||||
private def invokeMain(loader: ClassLoader, main: Method, options: Seq[String])
|
||||
{
|
||||
|
|
@ -79,7 +75,7 @@ class Run(instance: ScalaInstance) extends ScalaRun
|
|||
/** This module is an interface to starting the scala interpreter or runner.*/
|
||||
object Run
|
||||
{
|
||||
def run(mainClass: String, classpath: Iterable[Path], options: Seq[String], log: Logger)(implicit runner: ScalaRun) =
|
||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger)(implicit runner: ScalaRun) =
|
||||
runner.run(mainClass, classpath, options, log)
|
||||
|
||||
/** Executes the given function, trapping calls to System.exit. */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package sbt
|
||||
package classpath
|
||||
|
||||
import java.lang.ref.{Reference, SoftReference, WeakReference}
|
||||
import java.io.File
|
||||
import java.net.{URI, URL, URLClassLoader}
|
||||
import java.util.Collections
|
||||
|
|
@ -13,12 +14,14 @@ import IO.{createTemporaryDirectory, write}
|
|||
|
||||
object ClasspathUtilities
|
||||
{
|
||||
def toClasspath(finder: PathFinder): Array[URL] = finder.getURLs
|
||||
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 = toLoader(paths, rootLoader)
|
||||
def toLoader(paths: Iterable[Path], parent: ClassLoader): ClassLoader = new URLClassLoader(toClasspath(paths), parent)
|
||||
def toLoader(finder: PathFinder): ClassLoader = toLoader(finder, rootLoader)
|
||||
def toLoader(finder: PathFinder, parent: ClassLoader): ClassLoader = new URLClassLoader(finder.getURLs, parent)
|
||||
|
||||
def toLoader(paths: Seq[File]): ClassLoader = toLoader(paths, rootLoader)
|
||||
def toLoader(paths: Seq[File], parent: ClassLoader): ClassLoader = new URLClassLoader(Path.toURLs(paths), parent)
|
||||
|
||||
def toLoader(paths: Seq[File], parent: ClassLoader, resourceMap: Map[String,String]): ClassLoader =
|
||||
new URLClassLoader(Path.toURLs(paths), parent) with RawResources { override def resources = resourceMap }
|
||||
|
||||
lazy val rootLoader =
|
||||
{
|
||||
|
|
@ -33,28 +36,20 @@ object ClasspathUtilities
|
|||
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(classpath: Seq[File], instance: ScalaInstance): Map[String,String] =
|
||||
createClasspathResources(classpath ++ instance.jars, instance.jars)
|
||||
|
||||
def createClasspathResources(appPaths: Iterable[Path], bootPaths: Iterable[Path], baseDir: Path): Iterable[Path] =
|
||||
def createClasspathResources(appPaths: Seq[File], bootPaths: Seq[File]): Map[String, String] =
|
||||
{
|
||||
def writePaths(name: String, paths: Iterable[Path]): Unit =
|
||||
write(baseDir / name asFile, Path.makeString(paths))
|
||||
writePaths(AppClassPath, appPaths)
|
||||
writePaths(BootClassPath, bootPaths)
|
||||
appPaths ++ Seq(baseDir)
|
||||
def make(name: String, paths: Seq[File]) = name -> Path.makeString(paths)
|
||||
Map( make(AppClassPath, appPaths), make(BootClassPath, bootPaths) )
|
||||
}
|
||||
|
||||
/** The client is responsible for cleaning up the temporary directory.*/
|
||||
def makeLoader[T](classpath: Iterable[Path], instance: ScalaInstance): (ClassLoader, Path) =
|
||||
def makeLoader[T](classpath: Seq[File], instance: ScalaInstance): ClassLoader =
|
||||
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)
|
||||
}
|
||||
|
||||
def makeLoader[T](classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance): ClassLoader =
|
||||
toLoader(classpath, parent, createClasspathResources(classpath, instance))
|
||||
|
||||
private[sbt] def printSource(c: Class[_]) =
|
||||
println(c.getName + " loader=" +c.getClassLoader + " location=" + IO.classLocationFile(c))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package classpath
|
||||
|
||||
import java.io.{ByteArrayInputStream, InputStream}
|
||||
import java.net.{Proxy, URL, URLConnection, URLStreamHandler}
|
||||
import java.util.Enumeration
|
||||
|
||||
object RawURL
|
||||
{
|
||||
def apply(file: String, value: String): URL =
|
||||
apply(file, value.getBytes)
|
||||
def apply(file: String, value: Array[Byte]): URL =
|
||||
apply(file)(new ByteArrayInputStream(value))
|
||||
def apply(file: String)(value: => InputStream): URL =
|
||||
new URL("raw", null, -1, file, new RawStreamHandler(value))
|
||||
|
||||
private[this] final class RawStreamHandler(value: => InputStream) extends URLStreamHandler
|
||||
{
|
||||
override protected def openConnection(url: URL, p: Proxy): URLConnection =
|
||||
openConnection(url)
|
||||
override protected def openConnection(url: URL): URLConnection =
|
||||
new URLConnection(url)
|
||||
{
|
||||
private lazy val in = value
|
||||
def connect() { in }
|
||||
override def getInputStream = in
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait RawResources extends FixedResources
|
||||
{
|
||||
protected def resources: Map[String, String]
|
||||
override protected final val resourceURL = resources.transform(RawURL.apply)
|
||||
}
|
||||
trait FixedResources extends ClassLoader
|
||||
{
|
||||
protected def resourceURL: Map[String, URL]
|
||||
override def findResource(s: String): URL = resourceURL.getOrElse(s, super.findResource(s))
|
||||
|
||||
import java.util.Collections.{enumeration, singletonList}
|
||||
override def findResources(s: String): Enumeration[URL] =
|
||||
{
|
||||
val sup = super.findResources(s)
|
||||
resourceURL.get(s) match
|
||||
{
|
||||
case Some(url) => new DualEnumeration(enumeration(singletonList(url)), sup)
|
||||
case None => sup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -148,6 +148,9 @@ object Path extends PathExtra
|
|||
/** Constructs a String representation of <code>Path</code>s. The absolute path String of each <code>Path</code> is
|
||||
* separated by the given separator String.*/
|
||||
def makeString(paths: Iterable[Path], sep: String): String = paths.map(_.absolutePath).mkString(sep)
|
||||
|
||||
def makeString(paths: Seq[File]): String = makeString(paths, pathSeparator)
|
||||
def makeString(paths: Seq[File], sep: String): String = paths.map(_.getAbsolutePath).mkString(sep)
|
||||
|
||||
/** Constructs a String representation of <code>Path</code>s. The relative path String of each <code>Path</code> is
|
||||
* separated by the platform's path separator.*/
|
||||
|
|
@ -262,6 +265,8 @@ object Path extends PathExtra
|
|||
|
||||
def getFiles(files: Traversable[Path]): immutable.Set[File] = files.map(_.asFile).toSet
|
||||
def getURLs(files: Traversable[Path]): Array[URL] = files.map(_.asURL).toArray
|
||||
|
||||
def toURLs(files: Seq[File]): Array[URL] = files.map(_.toURI.toURL).toArray
|
||||
}
|
||||
|
||||
/** A path finder constructs a set of paths. The set is evaluated by a call to the <code>get</code>
|
||||
|
|
|
|||
Loading…
Reference in New Issue