summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/CompileServer.scala53
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala132
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala9
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala445
-rw-r--r--src/compiler/scala/tools/nsc/io/AbstractFile.scala67
-rw-r--r--src/compiler/scala/tools/nsc/io/Directory.scala73
-rw-r--r--src/compiler/scala/tools/nsc/io/File.scala122
-rw-r--r--src/compiler/scala/tools/nsc/io/FileOperationException.scala14
-rw-r--r--src/compiler/scala/tools/nsc/io/Path.scala169
-rw-r--r--src/compiler/scala/tools/nsc/io/PlainFile.scala79
-rw-r--r--src/compiler/scala/tools/nsc/io/Process.scala159
-rw-r--r--src/compiler/scala/tools/nsc/io/SourceReader.scala6
-rw-r--r--src/compiler/scala/tools/nsc/io/Streamable.scala108
-rw-r--r--src/compiler/scala/tools/nsc/io/VirtualDirectory.scala39
-rw-r--r--src/compiler/scala/tools/nsc/io/VirtualFile.scala16
-rw-r--r--src/compiler/scala/tools/nsc/io/ZipArchive.scala478
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala91
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala146
-rw-r--r--src/dotnet-library/scala/io/File.scala1
-rw-r--r--src/dotnet-library/scala/io/Process.scala1
-rw-r--r--src/dotnet-library/scala/io/Streamable.scala1
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala11
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala8
23 files changed, 1301 insertions, 927 deletions
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
index 010d183bc8..426562509d 100644
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
@@ -6,10 +6,9 @@
package scala.tools.nsc
-import java.io.{BufferedOutputStream, File, FileOutputStream, PrintStream}
-import java.lang.{Runtime, System, Thread}
+import java.io.{ BufferedOutputStream, FileOutputStream, PrintStream, File => JFile }
+import io.File
-import scala.concurrent.ops.spawn
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
import scala.tools.nsc.util.FakePos //Position
import scala.tools.util.SocketServer
@@ -22,7 +21,8 @@ import scala.tools.util.SocketServer
* @author Martin Odersky
* @version 1.0
*/
-class StandardCompileServer extends SocketServer {
+class StandardCompileServer extends SocketServer
+{
def compileSocket: CompileSocket = CompileSocket // todo: make this a lazy val
val versionMsg = "Fast Scala compiler " +
@@ -44,6 +44,7 @@ class StandardCompileServer extends SocketServer {
}
private val runtime = Runtime.getRuntime()
+ import runtime.{ totalMemory, freeMemory, maxMemory }
var reporter: ConsoleReporter = _
@@ -54,24 +55,31 @@ class StandardCompileServer extends SocketServer {
}
override def timeout() {
- if (!compileSocket.portFile(port).exists())
+ if (!compileSocket.portFile(port).exists)
fatal("port file no longer exists; skipping cleanup")
}
+ def printMemoryStats() {
+ System.out.println("New session, total memory = %s, max memory = %s, free memory = %s".format(
+ totalMemory, maxMemory, freeMemory))
+ System.out.flush()
+ }
+
+ def isMemoryFullEnough() = {
+ runtime.gc()
+ (totalMemory - freeMemory).toDouble / maxMemory.toDouble > MaxCharge
+ }
+
protected def newOfflineCompilerCommand(
arguments: List[String],
settings: Settings,
error: String => Unit,
- interactive: Boolean)
- = new OfflineCompilerCommand(arguments, settings, error, interactive)
+ interactive: Boolean
+ ) = new OfflineCompilerCommand(arguments, settings, error, interactive)
def session() {
- System.out.println("New session" +
- ", total memory = "+ runtime.totalMemory() +
- ", max memory = " + runtime.maxMemory() +
- ", free memory = " + runtime.freeMemory)
- System.out.flush()
- val password = compileSocket.getPassword(port)
+ printMemoryStats()
+ val password = compileSocket getPassword port
val guessedPassword = in.readLine()
val input = in.readLine()
if ((input ne null) && password == guessedPassword) {
@@ -127,24 +135,17 @@ class StandardCompileServer extends SocketServer {
shutDown = true
}
reporter.printSummary()
- runtime.gc()
- if ((runtime.totalMemory() - runtime.freeMemory()).toDouble /
- runtime.maxMemory().toDouble > MaxCharge) compiler = null
+ if (isMemoryFullEnough)
+ compiler = null
}
}
}
/** A directory holding redirected output */
- private val redirectDir = new File(compileSocket.tmpDir, "output-redirects")
- redirectDir.mkdirs
-
- private def redirect(setter: PrintStream => Unit, filename: String) {
- setter(
- new PrintStream(
- new BufferedOutputStream(
- new FileOutputStream(
- new File(redirectDir, filename)))))
- }
+ private val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory()
+
+ private def redirect(setter: PrintStream => Unit, filename: String): Unit =
+ setter(new PrintStream((redirectDir / filename).createFile().bufferedOutput()))
def main(args: Array[String]) {
redirect(System.setOut, "scala-compile-server-out.log")
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index 8cb679f24b..03c86ec962 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -6,12 +6,16 @@
package scala.tools.nsc
-import java.lang.{Thread, System, Runtime}
-import java.lang.NumberFormatException
-import java.io.{File, IOException, PrintWriter, FileOutputStream}
-import java.io.{BufferedReader, FileReader}
+import java.io.{ IOException, FileNotFoundException, PrintWriter, FileOutputStream }
+import java.io.{ BufferedReader, FileReader }
import java.util.regex.Pattern
import java.net._
+import java.security.SecureRandom
+
+import io.{ File, Path }
+import scala.util.control.Exception.catching
+
+// class CompileChannel { }
/** This class manages sockets for the fsc offline compiler. */
class CompileSocket {
@@ -25,17 +29,13 @@ class CompileSocket {
protected def cmdName = Properties.cmdName //todo: lazy val
/** The vm part of the command to start a new scala compile server */
- protected val vmCommand =
- Properties.scalaHome match {
- case null =>
- cmdName
- case dirname =>
- val trial = new File(new File(dirname, "bin"), cmdName)
- if (trial.canRead)
- trial.getPath
- else
- cmdName
- }
+ protected val vmCommand = Properties.scalaHome match {
+ case null => cmdName
+ case dirname =>
+ val trial = File(dirname) / "bin" / cmdName
+ if (trial.canRead) trial.path
+ else cmdName
+ }
/** The class name of the scala compile server */
protected val serverClass = "scala.tools.nsc.CompileServer"
@@ -44,7 +44,7 @@ class CompileSocket {
val errorRegex = ".*(errors? found|don't know|bad option).*"
/** A Pattern object for checking compiler output for errors */
- val errorPattern = Pattern.compile(errorRegex)
+ val errorPattern = Pattern compile errorRegex
protected def error(msg: String) = System.err.println(msg)
@@ -59,8 +59,7 @@ class CompileSocket {
/** A temporary directory to use */
val tmpDir = {
val udir = Option(Properties.userName) getOrElse "shared"
- val f = new File(Properties.tmpDir, "scala-devel/" + udir)
- f.mkdirs()
+ val f = (Path(Properties.tmpDir) / "scala-devel" / udir).createDirectory()
if (f.isDirectory && f.canWrite) {
info("[Temp directory: " + f + "]")
@@ -70,8 +69,7 @@ class CompileSocket {
}
/* A directory holding port identification files */
- val portsDir = new File(tmpDir, dirName)
- portsDir.mkdirs
+ val portsDir = (tmpDir / dirName).createDirectory()
/** Maximum number of polls for an available port */
private val MaxAttempts = 100
@@ -103,24 +101,16 @@ class CompileSocket {
}
/** The port identification file */
- def portFile(port: Int) = new File(portsDir, port.toString())
+ def portFile(port: Int) = portsDir / File(port.toString)
/** Poll for a server port number; return -1 if none exists yet */
- private def pollPort(): Int = {
- val hits = portsDir.listFiles()
- if (hits.length == 0) -1
- else
- try {
- for (i <- 1 until hits.length) hits(i).delete()
- hits(0).getName.toInt
- } catch {
- case ex: NumberFormatException =>
- fatal(ex.toString() +
- "\nbad file in temp directory: " +
- hits(0).getAbsolutePath() +
- "\nplease remove the file and try again")
- }
- }
+ private def pollPort(): Int =
+ portsDir.list.toList match {
+ case Nil => -1
+ case p :: xs =>
+ xs forall (_.delete())
+ p.name.toInt
+ }
/** Get the port number to which a scala compile server is connected;
* If no server is running yet, then create one.
@@ -131,6 +121,7 @@ class CompileSocket {
if (port < 0)
startNewServer(vmArgs)
+
while (port < 0 && attempts < MaxAttempts) {
attempts += 1
Thread.sleep(sleepTime)
@@ -144,25 +135,23 @@ class CompileSocket {
/** Set the port number to which a scala compile server is connected */
def setPort(port: Int) {
- try {
- val f = new PrintWriter(new FileOutputStream(portFile(port)))
- f.println(new java.security.SecureRandom().nextInt.toString)
- f.close()
- } catch {
- case ex: /*FileNotFound+Security*/Exception =>
- fatal("Cannot create file: " +
- portFile(port).getAbsolutePath())
+ val file = portFile(port)
+ val secret = new SecureRandom().nextInt.toString
+
+ try file writeAll List(secret) catch {
+ case e @ (_: FileNotFoundException | _: SecurityException) =>
+ fatal("Cannot create file: %s".format(file.path))
}
}
/** Delete the port number to which a scala compile server was connected */
- def deletePort(port: Int) { portFile(port).delete() }
+ def deletePort(port: Int) = portFile(port).delete()
/** Get a socket connected to a daemon. If create is true, then
* create a new daemon if necessary. Returns null if the connection
* cannot be established.
*/
- def getOrCreateSocket(vmArgs: String, create: Boolean): Socket = {
+ def getOrCreateSocket(vmArgs: String, create: Boolean = true): Socket = {
val nAttempts = 49 // try for about 5 seconds
def getsock(attempts: Int): Socket =
if (attempts == 0) {
@@ -193,45 +182,40 @@ class CompileSocket {
getsock(nAttempts)
}
- /** Same as getOrCreateSocket(vmArgs, true). */
- def getOrCreateSocket(vmArgs: String): Socket =
- getOrCreateSocket(vmArgs, true)
+ // XXX way past time for this to be central
+ def parseInt(x: String): Option[Int] =
+ try { Some(x.toInt) }
+ catch { case _: NumberFormatException => None }
def getSocket(serverAdr: String): Socket = {
- val cpos = serverAdr indexOf ':'
- if (cpos < 0)
- fatal("Malformed server address: " + serverAdr + "; exiting")
- else {
- val hostName = serverAdr.substring(0, cpos)
- val port = try {
- serverAdr.substring(cpos+1).toInt
- } catch {
- case ex: Throwable =>
- fatal("Malformed server address: " + serverAdr + "; exiting")
- }
- getSocket(hostName, port)
+ def fail = fatal("Malformed server address: %s; exiting" format serverAdr)
+ (serverAdr indexOf ':') match {
+ case -1 => fail
+ case cpos =>
+ val hostName: String = serverAdr take cpos
+ parseInt(serverAdr drop (cpos + 1)) match {
+ case Some(port) => getSocket(hostName, port)
+ case _ => fail
+ }
}
}
def getSocket(hostName: String, port: Int): Socket =
- try {
- new Socket(hostName, port)
- } catch {
- case e: /*IO+Security*/Exception =>
- fatal("Unable to establish connection to server " +
- hostName + ":" + port + "; exiting")
+ try new Socket(hostName, port) catch {
+ case e @ (_: IOException | _: SecurityException) =>
+ fatal("Unable to establish connection to server %s:%d; exiting".format(hostName, port))
}
def getPassword(port: Int): String = {
- val ff = portFile(port)
- val f = new BufferedReader(new FileReader(ff))
+ val ff = portFile(port)
+ val f = ff.bufferedReader()
+
// allow some time for the server to start up
- var retry = 50
- while (ff.length() == 0 && retry > 0) {
- Thread.sleep(100)
- retry -= 1
+ def check = {
+ Thread sleep 100
+ ff.length
}
- if (ff.length() == 0) {
+ if (Iterator continually check take 50 find (_ > 0) isEmpty) {
ff.delete()
fatal("Unable to establish connection to server.")
}
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 3f44bcdf9b..d34c4a8640 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -957,15 +957,6 @@ object Interpreter {
intLoop.closeInterpreter
}
- /** Delete a directory tree recursively. Use with care! */
- private[nsc] def deleteRecursively(path: File): Unit =
- if (path.exists) {
- if (path.isDirectory)
- path.listFiles foreach deleteRecursively
-
- path.delete
- }
-
/** Heuristically strip interpreter wrapper prefixes
* from an interpreter output string.
*/
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index e31dcd38f9..f87fdf093d 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -6,14 +6,19 @@
package scala.tools.nsc
-import java.io.{BufferedReader, File, FileInputStream, FileOutputStream,
- FileReader, InputStreamReader, PrintWriter,
- FileWriter, IOException}
+import java.io.{
+ InputStream, OutputStream,
+ BufferedReader, FileInputStream, FileOutputStream,
+ FileReader, InputStreamReader, PrintWriter, FileWriter,
+ IOException
+}
+import java.io.{ File => JFile }
+import io.{ Directory, File, Path, PlainFile }
import java.lang.reflect.InvocationTargetException
import java.net.URL
-import java.util.jar.{JarEntry, JarOutputStream}
+import java.util.jar.{ JarEntry, JarOutputStream }
+import java.util.regex.Pattern
-import scala.tools.nsc.io.PlainFile
import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}
import scala.tools.nsc.util.{ClassPath, CompoundSourceFile, BatchSourceFile, SourceFile, SourceFileFragment}
@@ -43,136 +48,123 @@ import scala.tools.nsc.util.{ClassPath, CompoundSourceFile, BatchSourceFile, Sou
* @todo It would be better if error output went to stderr instead
* of stdout...
*/
-object ScriptRunner {
+object ScriptRunner
+{
+ /* While I'm chasing down the fsc and script bugs. */
+ def DBG(msg: Any) {
+ System.err.println(msg.toString)
+ System.err.flush()
+ }
+
/** Default name to use for the wrapped script */
val defaultScriptMain = "Main"
+ private def addShutdownHook(body: => Unit) =
+ Runtime.getRuntime addShutdownHook new Thread { override def run { body } }
+
/** Pick a main object name from the specified settings */
- def scriptMain(settings: Settings) =
- if (settings.script.value == "")
- defaultScriptMain
- else
- settings.script.value
-
- /** Choose a jar filename to hold the compiled version
- * of a script
- */
+ def scriptMain(settings: Settings) = settings.script.value match {
+ case "" => defaultScriptMain
+ case x => x
+ }
+
+ /** Choose a jar filename to hold the compiled version of a script. */
private def jarFileFor(scriptFile: String): File = {
- val filename =
- if (scriptFile.matches(".*\\.[^.\\\\/]*"))
- scriptFile.replaceFirst("\\.[^.\\\\/]*$", ".jar")
- else
- scriptFile + ".jar"
+ val name =
+ if (scriptFile endsWith ".jar") scriptFile
+ else scriptFile + ".jar"
+
+ File(name)
+ }
+
+ def copyStreams(in: InputStream, out: OutputStream) = {
+ val buf = new Array[Byte](10240)
+
+ def loop: Unit = in.read(buf, 0, buf.length) match {
+ case -1 => in.close()
+ case n => out.write(buf, 0, n) ; loop
+ }
- new File(filename)
+ loop
}
/** Try to create a jar file out of all the contents
* of the directory <code>sourcePath</code>.
*/
- private def tryMakeJar(jarFile: File, sourcePath: File) = {
- try {
- val jarFileStream = new FileOutputStream(jarFile)
- val jar = new JarOutputStream(jarFileStream)
- val buf = new Array[Byte](10240)
-
- def addFromDir(dir: File, prefix: String) {
- for (entry <- dir.listFiles) {
- if (entry.isFile) {
- jar.putNextEntry(new JarEntry(prefix + entry.getName))
-
- val input = new FileInputStream(entry)
- var n = input.read(buf, 0, buf.length)
- while (n >= 0) {
- jar.write (buf, 0, n)
- n = input.read(buf, 0, buf.length)
- }
- jar.closeEntry
- input.close
- } else {
- addFromDir(entry, prefix + entry.getName + "/")
- }
- }
+ private def tryMakeJar(jarFile: File, sourcePath: Directory) = {
+ def addFromDir(jar: JarOutputStream, dir: Directory, prefix: String) {
+ def addFileToJar(entry: File) = {
+ jar putNextEntry new JarEntry(prefix + entry.name)
+ copyStreams(entry.inputStream, jar)
+ jar.closeEntry
+ }
+
+ dir.list foreach { entry =>
+ if (entry.isFile) addFileToJar(entry.toFile)
+ else addFromDir(jar, entry.toDirectory, prefix + entry.name + "/")
}
+ }
- addFromDir(sourcePath, "")
+ try {
+ val jar = new JarOutputStream(jarFile.outputStream())
+ addFromDir(jar, sourcePath, "")
jar.close
- } catch {
- case _:Error => jarFile.delete // XXX what errors to catch?
+ }
+ catch {
+ case _: Error => jarFile.delete() // XXX what errors to catch?
}
}
-
/** Read the entire contents of a file as a String. */
- private def contentsOfFile(filename: String): String = {
- val strbuf = new StringBuilder
- val reader = new FileReader(filename)
- val cbuf = new Array[Char](1024)
- while(true) {
- val n = reader.read(cbuf)
- if (n <= 0)
- return strbuf.toString
- strbuf.append(cbuf, 0, n)
- }
- throw new Error("impossible")
- }
+ private def contentsOfFile(filename: String) = File(filename).slurp()
/** Find the length of the header in the specified file, if
* there is one. The header part starts with "#!" or "::#!"
* and ends with a line that begins with "!#" or "::!#".
*/
private def headerLength(filename: String): Int = {
- import java.util.regex._
-
+ val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE)
val fileContents = contentsOfFile(filename)
+ def isValid = List("#!", "::#!") exists (fileContents startsWith _)
- if (!(fileContents.startsWith("#!") || fileContents.startsWith("::#!")))
- return 0
-
- val matcher =
- (Pattern.compile("^(::)?!#.*(\\r|\\n|\\r\\n)", Pattern.MULTILINE)
- .matcher(fileContents))
- if (!matcher.find)
- throw new IOException("script file does not close its header with !# or ::!#")
- return matcher.end
+ if (!isValid) 0 else {
+ val matcher = headerPattern matcher fileContents
+ if (matcher.find) matcher.end
+ else throw new IOException("script file does not close its header with !# or ::!#")
+ }
}
/** Split a fully qualified object name into a
* package and an unqualified object name */
- private def splitObjectName(fullname: String):
- (Option[String],String) =
- {
- val idx = fullname.lastIndexOf('.')
- if (idx < 0)
- (None, fullname)
- else
- (Some(fullname.substring(0,idx)), fullname.substring(idx+1))
- }
+ private def splitObjectName(fullname: String): (Option[String], String) =
+ (fullname lastIndexOf '.') match {
+ case -1 => (None, fullname)
+ case idx => (Some(fullname take idx), fullname drop (idx + 1))
+ }
/** Code that is added to the beginning of a script file to make
* it a complete Scala compilation unit.
*/
- protected def preambleCode(objectName: String) = {
- val (maybePack, objName) = splitObjectName(objectName)
-
- val packageDecl =
- maybePack match {
- case Some(pack) => "package " + pack + "\n"
- case None => ""
- }
-
- (packageDecl +
- "object " + objName + " {\n" +
- " def main(argv: Array[String]): Unit = {\n" +
- " val args = argv;\n" +
- " new AnyRef {\n")
+ protected def preambleCode(objectName: String): String = {
+ val (maybePack, objName) = splitObjectName(objectName)
+ val packageDecl = maybePack map ("package %s\n" format _) getOrElse ("")
+
+ return """|
+ | object %s {
+ | def main(argv: Array[String]): Unit = {
+ | val args = argv
+ | new AnyRef {
+ |""".stripMargin.format(objName)
}
/** Code that is added to the end of a script file to make
* it a complete Scala compilation unit.
*/
- val endCode = "\n} \n} }\n"
-
+ val endCode = """
+ | }
+ | }
+ | }
+ |""".stripMargin
/** Wrap a script file into a runnable object named
* <code>scala.scripting.Main</code>.
@@ -180,20 +172,12 @@ object ScriptRunner {
def wrappedScript(
objectName: String,
filename: String,
- getSourceFile: PlainFile => SourceFile): SourceFile =
+ getSourceFile: PlainFile => BatchSourceFile): SourceFile =
{
- val preamble =
- new BatchSourceFile("<script preamble>",
- preambleCode(objectName).toCharArray)
-
+ val preamble = new BatchSourceFile("<script preamble>", preambleCode(objectName).toCharArray)
val middle = {
- val f = new File(filename)
- val bsf = getSourceFile(new PlainFile(f)).asInstanceOf[BatchSourceFile]
- new SourceFileFragment(
- bsf,
- headerLength(filename),
- bsf.length)
-// f.length.asInstanceOf[Int])
+ val bsf = getSourceFile(PlainFile fromPath filename)
+ new SourceFileFragment(bsf, headerLength(filename), bsf.length)
}
val end = new BatchSourceFile("<script trailer>", endCode.toCharArray)
@@ -210,53 +194,42 @@ object ScriptRunner {
settings: GenericRunnerSettings,
scriptFileIn: String): Boolean =
{
- val scriptFile = CompileClient.absFileName(scriptFileIn)
- for (setting <- List(
- settings.classpath,
- settings.sourcepath,
- settings.bootclasspath,
- settings.extdirs,
- settings.outdir))
- setting.value = CompileClient.absFileNames(setting.value)
-
- val compSettingNames =
- (new Settings(error)).allSettings.map(_.name)
-
- val compSettings =
- settings.allSettings.filter(stg =>
- compSettingNames.contains(stg.name))
-
- val coreCompArgs =
- compSettings.foldLeft[List[String]](Nil)((args, stg) =>
- stg.unparse ::: args)
-
- val compArgs =
- (coreCompArgs :::
- List("-Xscript", scriptMain(settings), scriptFile))
-
- val socket = CompileSocket.getOrCreateSocket("")
- if (socket eq null)
- return false
-
- val out = new PrintWriter(socket.getOutputStream(), true)
- val in = new BufferedReader(new InputStreamReader(socket.getInputStream()))
+ val scriptFile = CompileClient absFileName scriptFileIn
- out.println(CompileSocket.getPassword(socket.getPort))
- out.println(compArgs.mkString("", "\0", ""))
-
- var compok = true
-
- var fromServer = in.readLine()
- while (fromServer ne null) {
- Console.err.println(fromServer)
- if (CompileSocket.errorPattern.matcher(fromServer).matches)
- compok = false
+ {
+ import settings._
+ for (setting <- List(classpath, sourcepath, bootclasspath, extdirs, outdir)) {
+ // DBG("%s = %s".format(setting.name, setting.value))
+ setting.value = CompileClient absFileName setting.value
+ }
+ }
- fromServer = in.readLine()
+ val compSettingNames = new Settings(error).allSettings map (_.name)
+ val compSettings = settings.allSettings filter (compSettingNames contains _.name)
+ val coreCompArgs = compSettings flatMap (_.unparse)
+ val compArgs = coreCompArgs ::: List("-Xscript", scriptMain(settings), scriptFile)
+ var compok = true
+
+ // XXX temporary as I started using ManagedResource not remembering it wasn't checked in.
+ def ManagedResource[T](x: => T) = Some(x)
+
+ for {
+ socket <- ManagedResource(CompileSocket getOrCreateSocket "")
+ val _ = if (socket == null) return false
+ out <- ManagedResource(new PrintWriter(socket.getOutputStream(), true))
+ in <- ManagedResource(new BufferedReader(new InputStreamReader(socket.getInputStream())))
+ } {
+ out println (CompileSocket getPassword socket.getPort)
+ out println (compArgs mkString "\0")
+
+ for (fromServer <- (Iterator continually in.readLine()) takeWhile (_ != null)) {
+ Console.err println fromServer
+ if (CompileSocket.errorPattern matcher fromServer matches)
+ compok = false
+ }
+ // XXX temp until managed resource is available
+ in.close() ; out.close() ; socket.close()
}
- in.close()
- out.close()
- socket.close()
compok
}
@@ -269,101 +242,80 @@ object ScriptRunner {
*
* @returns true if compilation and the handler succeeds, false otherwise.
*/
- private def withCompiledScript
- (settings: GenericRunnerSettings, scriptFile: String)
- (handler: String => Boolean)
- : Boolean = {
- import Interpreter.deleteRecursively
-
- /** Compiles the script file, and returns
- * the directory with the compiled class files,
- * if the compilation succeeded.
- */
- def compile: Option[File] = {
- val compiledPath = File.createTempFile("scalascript", "")
- compiledPath.delete // the file is created as a file; make it a directory
- compiledPath.mkdirs
+ private def withCompiledScript(
+ settings: GenericRunnerSettings,
+ scriptFile: String)
+ (handler: String => Boolean): Boolean =
+ {
+ /** Compiles the script file, and returns the directory with the compiled
+ * class files, if the compilation succeeded.
+ */
+ def compile: Option[Directory] = {
+ val compiledPath = Directory makeTemp "scalascript"
// delete the directory after the user code has finished
- Runtime.getRuntime.addShutdownHook(new Thread {
- override def run { deleteRecursively(compiledPath) }})
+ addShutdownHook(compiledPath.deleteRecursively())
- settings.outdir.value = compiledPath.getPath
+ settings.outdir.value = compiledPath.path
if (settings.nocompdaemon.value) {
val reporter = new ConsoleReporter(settings)
val compiler = newGlobal(settings, reporter)
val cr = new compiler.Run
- val wrapped =
- wrappedScript(
- scriptMain(settings),
- scriptFile,
- compiler.getSourceFile _)
- cr.compileSources(List(wrapped))
- if (!reporter.hasErrors)
- Some(compiledPath)
- else
- None
- } else {
- if (compileWithDaemon(settings, scriptFile))
- Some(compiledPath)
- else
- None
+ val wrapped = wrappedScript(scriptMain(settings), scriptFile, compiler getSourceFile _)
+
+ cr compileSources List(wrapped)
+ if (reporter.hasErrors) None else Some(compiledPath)
}
+ else if (compileWithDaemon(settings, scriptFile)) Some(compiledPath)
+ else None
}
if (settings.savecompiled.value) {
val jarFile = jarFileFor(scriptFile)
+ def jarOK = jarFile.canRead && (jarFile isFresher File(scriptFile))
- def jarOK = (jarFile.canRead &&
- (jarFile.lastModified > new File(scriptFile).lastModified))
-
- if (jarOK) {
- // pre-compiled jar is current
- handler(jarFile.getAbsolutePath)
- } else {
- // The pre-compiled jar is old. Recompile the script.
- jarFile.delete
+ def recompile() = {
+ jarFile.delete()
compile match {
case Some(compiledPath) =>
tryMakeJar(jarFile, compiledPath)
if (jarOK) {
- deleteRecursively(compiledPath) // may as well do it now
- handler(jarFile.getAbsolutePath)
- } else {
- // jar failed; run directly from the class files
- handler(compiledPath.getPath)
+ compiledPath.deleteRecursively()
+ handler(jarFile.toAbsolute.path)
}
- case None => false
+ // jar failed; run directly from the class files
+ else handler(compiledPath.path)
+ case _ => false
}
}
- } else {
- // don't use a cache jar at all--just use the class files
- compile match {
- case Some(compiledPath) => handler(compiledPath.getPath)
- case None => false
- }
+
+ if (jarOK) handler(jarFile.toAbsolute.path) // pre-compiled jar is current
+ else recompile() // jar old - recompile the script.
}
+ // don't use a cache jar at all--just use the class files
+ else compile map (cp => handler(cp.path)) getOrElse false
}
-
/** Run a script after it has been compiled
*
* @returns true if execution succeeded, false otherwise
*/
- private def runCompiled(settings: GenericRunnerSettings,
- compiledLocation: String,
- scriptArgs: List[String]) : Boolean = {
- def fileToURL(f: File): Option[URL] =
- try { Some(f.toURL) }
- catch { case e => Console.err.println(e); None }
+ private def runCompiled(
+ settings: GenericRunnerSettings,
+ compiledLocation: String,
+ scriptArgs: List[String]): Boolean =
+ {
+ def fileToURL(f: JFile): Option[URL] =
+ try Some(f.toURL) catch { case _: Exception => None }
def paths(str: String, expandStar: Boolean): List[URL] =
- for (
- file <- ClassPath.expandPath(str, expandStar) map (new File(_)) if file.exists;
- val url = fileToURL(file); if !url.isEmpty
- ) yield url.get
+ for {
+ file <- ClassPath.expandPath(str, expandStar) map (new JFile(_))
+ if file.exists
+ url <- fileToURL(file)
+ } yield url
val classpath =
(paths(settings.bootclasspath.value, true) :::
@@ -376,64 +328,47 @@ object ScriptRunner {
scriptMain(settings),
scriptArgs.toArray)
true
- } catch {
- case e: ClassNotFoundException =>
- Console.println(e)
- false
- case e: NoSuchMethodException =>
- Console.println(e)
+ }
+ catch {
+ case e @ (_: ClassNotFoundException | _: NoSuchMethodException) =>
+ Console println e
false
- case e:InvocationTargetException =>
+ case e: InvocationTargetException =>
e.getCause.printStackTrace
false
}
}
-
/** Run a script file with the specified arguments and compilation
* settings.
*
* @returns true if compilation and execution succeeded, false otherwise.
*/
- def runScript(settings: GenericRunnerSettings,
+ def runScript(
+ settings: GenericRunnerSettings,
scriptFile: String,
- scriptArgs: List[String]) : Boolean = {
- val f = new File(scriptFile)
- if (!f.isFile) {
- throw new IOException("no such file: " + scriptFile)
- } else {
- try {
- withCompiledScript(settings, scriptFile){compiledLocation =>
- runCompiled(settings, compiledLocation, scriptArgs)
- }
- } catch {
- case e => throw e
- }
- }
+ scriptArgs: List[String]): Boolean =
+ {
+ if (File(scriptFile).isFile)
+ withCompiledScript(settings, scriptFile) { runCompiled(settings, _, scriptArgs) }
+ else
+ throw new IOException("no such file: " + scriptFile)
}
/** Run a command
*
* @returns true if compilation and execution succeeded, false otherwise.
*/
- def runCommand(settings: GenericRunnerSettings,
- command: String,
- scriptArgs: List[String]) : Boolean = {
- val scriptFile = File.createTempFile("scalacmd", ".scala")
-
+ def runCommand(
+ settings: GenericRunnerSettings,
+ command: String,
+ scriptArgs: List[String]) : Boolean =
+ {
+ val scriptFile = File.makeTemp("scalacmd", ".scala")
// save the command to the file
- {
- val str = new FileWriter(scriptFile)
- str.write(command)
- str.close()
- }
+ scriptFile writeAll List(command)
- try {
- withCompiledScript(settings, scriptFile.getPath){compiledLocation =>
- runCompiled(settings, compiledLocation, scriptArgs)
- }
- } catch {
- case e => throw e
- } finally scriptFile.delete() // in case there was a compilation error
+ try withCompiledScript(settings, scriptFile.path) { runCompiled(settings, _, scriptArgs) }
+ finally scriptFile.delete() // in case there was a compilation error
}
}
diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
index eaa3091eee..f7d73ed200 100644
--- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala
+++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
@@ -8,8 +8,9 @@
package scala.tools.nsc
package io
-import java.io.{File, FileOutputStream, IOException, InputStream, OutputStream}
+import java.io.{ File => JFile, FileOutputStream, IOException, InputStream, OutputStream }
import java.net.URL
+import PartialFunction._
import scala.collection.mutable.ArrayBuffer
@@ -17,21 +18,23 @@ import scala.collection.mutable.ArrayBuffer
* @author Philippe Altherr
* @version 1.0, 23/03/2004
*/
-object AbstractFile {
+object AbstractFile
+{
+ def isJarOrZip(f: Path) = cond(f.extension) { case Some("zip" | "jar") => true }
/** Returns "getFile(new File(path))". */
- def getFile(path: String): AbstractFile = getFile(new File(path))
+ def getFile(path: String): AbstractFile = getFile(Path(path))
+ def getFile(path: Path): AbstractFile = getFile(path.toFile)
/**
* If the specified File exists and is a regular file, returns an
* abstract regular file backed by it. Otherwise, returns <code>null</code>.
*/
def getFile(file: File): AbstractFile =
- if (file.isFile() && file.exists()) new PlainFile(file) else null
-
+ if (file.isFile) new PlainFile(file) else null
/** Returns "getDirectory(new File(path))". */
- def getDirectory(path: String): AbstractFile = getDirectory(new File(path))
+ def getDirectory(path: Path): AbstractFile = getDirectory(path.toFile)
/**
* If the specified File exists and is either a directory or a
@@ -41,15 +44,10 @@ object AbstractFile {
* @param file ...
* @return ...
*/
- def getDirectory(file: File): AbstractFile = {
- if (file.isDirectory() && file.exists()) return new PlainFile(file)
- if (file.isFile() && file.exists()) {
- val path = file.getPath()
- if (path.endsWith(".jar") || path.endsWith(".zip"))
- return ZipArchive.fromFile(file);
- }
- null
- }
+ def getDirectory(file: File): AbstractFile =
+ if (file.isDirectory) new PlainFile(file)
+ else if (file.isFile && isJarOrZip(file)) ZipArchive fromFile file
+ else null
/**
* If the specified URL exists and is a readable zip or jar archive,
@@ -60,15 +58,7 @@ object AbstractFile {
* @return ...
*/
def getURL(url: URL): AbstractFile =
- if (url ne null) {
- val path = url.getPath()
- if (path.endsWith(".jar") || path.endsWith(".zip"))
- ZipArchive.fromURL(url)
- else
- null
- }
- else
- null
+ Option(url) filterMap { case url: URL if isJarOrZip(url.getPath) => ZipArchive fromURL url } orNull
}
/**
@@ -110,11 +100,12 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
def container : AbstractFile
/** Returns the underlying File if any and null otherwise. */
- def file: File
+ def file: JFile
+ def sfile = File(file) // XXX
/** Does this abstract file denote an existing file? */
def exists: Boolean =
- if (file ne null) file.exists()
+ if (file ne null) file.exists
else true
/** Create a file on disk, if one does not exist already. */
@@ -207,11 +198,11 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile,
path0: String,
directory: Boolean): AbstractFile = {
- val separator = File.separatorChar
+ val separator = JFile.separatorChar
// trim trailing '/'s
- val path = if (path0.charAt(path0.length - 1) == separator) path0.substring(0, path0.length - 1) else path0
+ val path: String = if (path0.last == separator) path0 dropRight 1 else path0
val length = path.length()
- assert(0 < length && path.lastIndexOf(separator) < length - 1, path)
+ assert(length > 0 && !(path.last == separator), path)
var file = this
var start = 0
while (true) {
@@ -231,14 +222,7 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
*/
def fileNamed(name: String): AbstractFile = {
assert(isDirectory)
- val existing = lookupName(name, false)
- if (existing == null) {
- val newFile = new File(file, name)
- newFile.createNewFile()
- new PlainFile(newFile)
- } else {
- existing
- }
+ Option(lookupName(name, false)) getOrElse new PlainFile((sfile / name).createFile())
}
/**
@@ -247,14 +231,7 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
*/
def subdirectoryNamed(name: String): AbstractFile = {
assert (isDirectory)
- val existing = lookupName(name, true)
- if (existing == null) {
- val dir = new File(file, name)
- dir.mkdir()
- new PlainFile(dir)
- } else {
- existing
- }
+ Option(lookupName(name, true)) getOrElse new PlainFile((sfile / name).createDirectory())
}
/** Returns the path of this abstract file. */
diff --git a/src/compiler/scala/tools/nsc/io/Directory.scala b/src/compiler/scala/tools/nsc/io/Directory.scala
new file mode 100644
index 0000000000..dd48ee2866
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/Directory.scala
@@ -0,0 +1,73 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.tools.nsc
+package io
+
+import java.io.{ File => JFile }
+import collection.Traversable
+
+object Directory
+{
+ def apply(path: Path) = path.toDirectory
+
+ // Like File.makeTemp but creates a directory instead
+ def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = {
+ val path = File.makeTemp(prefix, suffix, dir)
+ path.delete()
+ path.createDirectory()
+ }
+}
+import Path._
+
+/** An abstraction for directories.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+class Directory(jfile: JFile) extends Path(jfile)
+{
+ override def toDirectory: Directory = this
+ override def toFile: File = new File(jfile)
+ override def isValid = jfile.isDirectory() || !jfile.exists()
+
+ /** An iterator over the contents of this directory.
+ */
+ def list: Iterator[Path] =
+ jfile.listFiles match {
+ case null => Iterator.empty
+ case xs => xs.iterator map Path.apply
+ }
+
+ def dirs: Iterator[Directory] = list filterMap { case x: Directory => x }
+ def files: Iterator[File] = list filterMap { case x: File => x }
+
+ def deepList(depth: Int = 1): Iterator[Path] =
+ if (depth == 0) Iterator.empty
+ else list ++ (dirs flatMap (_ deepList (depth - 1)))
+
+ /** An iterator over the directories underneath this directory,
+ * to the (optionally) given depth.
+ */
+ def subdirs(depth: Int = 1): Iterator[Directory] =
+ deepList(depth) filterMap { case x: Directory => x }
+
+ /** Deletes the directory recursively. Returns false on failure.
+ * Use with caution!
+ */
+ def deleteRecursively(): Boolean = deleteRecursively(jfile)
+ private def deleteRecursively(f: JFile): Boolean = {
+ if (f.isDirectory) f.listFiles match {
+ case null =>
+ case xs => xs foreach deleteRecursively
+ }
+ f.delete()
+ }
+
+ override def toString() = "Directory(%s)".format(path)
+}
diff --git a/src/compiler/scala/tools/nsc/io/File.scala b/src/compiler/scala/tools/nsc/io/File.scala
new file mode 100644
index 0000000000..294139ba44
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/File.scala
@@ -0,0 +1,122 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+package scala.tools.nsc
+package io
+
+import java.io.{
+ FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
+ BufferedInputStream, BufferedOutputStream, IOException, File => JFile }
+import java.nio.channels.FileChannel
+import collection.Traversable
+import scala.io.Codec
+
+object File
+{
+ def pathSeparator = JFile.pathSeparator
+
+ def apply(path: Path)(implicit codec: Codec = null) =
+ if (codec != null) new File(path.jfile)(codec)
+ else path.toFile
+
+ // Create a temporary file
+ def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) =
+ apply(JFile.createTempFile(prefix, suffix, dir))
+
+ import java.nio.channels.Channel
+ type Closeable = { def close(): Unit }
+ def closeQuietly(target: Closeable) {
+ try target.close() catch { case e: IOException => }
+ }
+}
+import File._
+import Path._
+
+/** An abstraction for files. For character data, a Codec
+ * can be supplied at either creation time or when a method
+ * involving character data is called (with the latter taking
+ * precdence if supplied.) If neither is available, the value
+ * of scala.io.Codec.default is used.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+class File(jfile: JFile)(implicit val creationCodec: Codec = null)
+extends Path(jfile)
+with Streamable.Chars
+{
+ def withCodec(codec: Codec): File = new File(jfile)(codec)
+ override def toDirectory: Directory = new Directory(jfile)
+ override def toFile: File = this
+
+ override def isValid = jfile.isFile() || !jfile.exists()
+ override def length = super[Path].length
+
+ /** Obtains an InputStream. */
+ def inputStream() = new FileInputStream(jfile)
+
+ /** Obtains a OutputStream. */
+ def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append)
+ def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append))
+
+ /** Obtains an OutputStreamWriter wrapped around a FileOutputStream.
+ * This should behave like a less broken version of java.io.FileWriter,
+ * in that unlike the java version you can specify the encoding.
+ */
+ def writer(append: Boolean = false, codec: Codec = getCodec()) =
+ new OutputStreamWriter(outputStream(append), codec.charSet)
+
+ /** Wraps a BufferedWriter around the result of writer().
+ */
+ def bufferedWriter(append: Boolean = false, codec: Codec = getCodec()) =
+ new BufferedWriter(writer(append, codec))
+
+ /** Writes all the Strings in the given iterator to the file. */
+ def writeAll(xs: Traversable[String], append: Boolean = false, codec: Codec = getCodec()): Unit = {
+ val out = bufferedWriter(append, codec)
+ try xs foreach (out write _)
+ finally out close
+ }
+
+ def copyFile(destPath: Path, preserveFileDate: Boolean = false) = {
+ val FIFTY_MB = 1024 * 1024 * 50
+ val dest = destPath.toFile
+ if (!isValid) fail("Source %s is not a valid file." format name)
+ if (this.normalize == dest.normalize) fail("Source and destination are the same.")
+ if (!dest.parent.map(_.exists).getOrElse(false)) fail("Destination cannot be created.")
+ if (dest.exists && !dest.canWrite) fail("Destination exists but is not writable.")
+ if (dest.isDirectory) fail("Destination exists but is a directory.")
+
+ lazy val in_s = inputStream()
+ lazy val out_s = dest.outputStream()
+ lazy val in = in_s.getChannel()
+ lazy val out = out_s.getChannel()
+
+ try {
+ val size = in.size()
+ var pos, count = 0L
+ while (pos < size) {
+ count = (size - pos) min FIFTY_MB
+ pos += out.transferFrom(in, pos, count)
+ }
+ }
+ finally List[Closeable](out, out_s, in, in_s) foreach closeQuietly
+
+ if (this.length != dest.length)
+ fail("Failed to completely copy %s to %s".format(name, dest.name))
+
+ if (preserveFileDate)
+ dest.lastModified = this.lastModified
+
+ ()
+ }
+
+ override def toString() = "File(%s)".format(path)
+}
diff --git a/src/compiler/scala/tools/nsc/io/FileOperationException.scala b/src/compiler/scala/tools/nsc/io/FileOperationException.scala
new file mode 100644
index 0000000000..f4983cd156
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/FileOperationException.scala
@@ -0,0 +1,14 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+package scala.tools.nsc
+package io
+
+case class FileOperationException(msg: String) extends RuntimeException(msg)
diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala
new file mode 100644
index 0000000000..40b6a3a4ad
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/Path.scala
@@ -0,0 +1,169 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2009 LAMP/EPFL
+ */
+
+package scala.tools.nsc
+package io
+
+import java.io.{
+ FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
+ BufferedInputStream, BufferedOutputStream, File => JFile }
+import java.net.{ URI, URL }
+import collection.{ Sequence, Traversable }
+import collection.immutable.{ StringVector => SV }
+import PartialFunction._
+import scala.util.Random.nextASCIIString
+
+/** An abstraction for filesystem paths. The differences between
+ * Path, File, and Directory are primarily to communicate intent.
+ * Since the filesystem can change at any time, there is no way to
+ * reliably associate Files only with files and so on. Any Path
+ * can be converted to a File or Directory (and thus gain access to
+ * the additional entity specific methods) by calling toFile or
+ * toDirectory, which has no effect on the filesystem.
+ *
+ * Also available are createFile and createDirectory, which attempt
+ * to create the path in question.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+
+object Path
+{
+ // not certain these won't be problematic, but looks good so far
+ implicit def string2path(s: String): Path = apply(s)
+ implicit def jfile2path(jfile: JFile): Path = apply(jfile)
+
+ // java 7 style, we don't use it yet
+ // object AccessMode extends Enumeration("AccessMode") {
+ // val EXECUTE, READ, WRITE = Value
+ // }
+ // def checkAccess(modes: AccessMode*): Boolean = {
+ // modes foreach {
+ // case EXECUTE => throw new Exception("Unsupported") // can't check in java 5
+ // case READ => if (!jfile.canRead()) return false
+ // case WRITE => if (!jfile.canWrite()) return false
+ // }
+ // true
+ // }
+
+ def roots: List[Path] = JFile.listRoots().toList map Path.apply
+
+ def apply(path: String): Path = apply(new JFile(path))
+ def apply(jfile: JFile): Path =
+ if (jfile.isFile) new File(jfile)
+ else if (jfile.isDirectory) new Directory(jfile)
+ else new Path(jfile)
+
+ private[io] def randomPrefix = nextASCIIString(6)
+ private[io] def fail(msg: String) = throw FileOperationException(msg)
+}
+import Path._
+
+/** The Path constructor is private so we can enforce some
+ * semantics regarding how a Path might relate to the world.
+ */
+class Path private[io] (val jfile: JFile)
+{
+ val separator = JFile.separatorChar
+
+ // Validation: this verifies that the type of this object and the
+ // contents of the filesystem are in agreement. All objects are
+ // valid except File objects whose path points to a directory and
+ // Directory objects whose path points to a file.
+ def isValid: Boolean = true
+
+ // conversions
+ def toFile: File = new File(jfile)
+ def toDirectory: Directory = new Directory(jfile)
+ def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath())
+ def toURI: URI = jfile.toURI()
+ def toURL: URL = toURI.toURL()
+
+ /** Creates a new Path with the specified path appended. Assumes
+ * the type of the new component implies the type of the result.
+ */
+ def /(child: Path): Path = new Path(new JFile(jfile, child.path))
+ def /(child: Directory): Directory = /(child: Path).toDirectory
+ def /(child: File): File = /(child: Path).toFile
+
+ // identity
+ def name: String = jfile.getName()
+ def path: String = jfile.getPath()
+ def normalize: Path = Path(jfile.getCanonicalPath())
+ // todo -
+ // def resolve(other: Path): Path
+ // def relativize(other: Path): Path
+
+ // derived from identity
+ def root: Option[Path] = roots find (this startsWith _)
+ def segments: List[String] = (path split separator).toList filterNot (_.isEmpty)
+ def parent: Option[Path] = Option(jfile.getParent()) map Path.apply
+ def parents: List[Path] = parent match {
+ case None => Nil
+ case Some(p) => p :: p.parents
+ }
+ // if name ends with an extension (e.g. "foo.jpg") returns the extension ("jpg")
+ def extension: Option[String] =
+ condOpt(SV.lastIndexWhere(name, _ == '.')) {
+ case idx if idx != -1 => SV.drop(name, idx + 1)
+ }
+ // Alternative approach:
+ // (Option fromReturnValue SV.lastIndexWhere(name, _ == '.') map (x => SV.drop(name, x + 1))
+
+ // Boolean tests
+ def canRead = jfile.canRead()
+ def canWrite = jfile.canWrite()
+ def exists = jfile.exists()
+ def notExists = try !jfile.exists() catch { case ex: SecurityException => false }
+
+ def isFile = jfile.isFile()
+ def isDirectory = jfile.isDirectory()
+ def isAbsolute = jfile.isAbsolute()
+ def isHidden = jfile.isHidden()
+ def isSymlink = parent.isDefined && {
+ val x = parent.get / name
+ x.normalize != x.toAbsolute
+ }
+
+ // Information
+ def lastModified = jfile.lastModified()
+ def lastModified_=(time: Long) = jfile setLastModified time // should use setXXX function?
+ def length = jfile.length()
+
+ // Boolean path comparisons
+ def endsWith(other: Path) = segments endsWith other.segments
+ def startsWith(other: Path) = segments startsWith other.segments
+ def isSame(other: Path) = toAbsolute == other.toAbsolute
+ def isFresher(other: Path) = lastModified > other.lastModified
+
+ // creations
+ def createDirectory(force: Boolean = true, failIfExists: Boolean = false): Directory = {
+ val res = if (force) jfile.mkdirs() else jfile.mkdir()
+ if (!res && failIfExists && exists) fail("Directory '%s' already exists." format name)
+ else if (isDirectory) toDirectory
+ else new Directory(jfile)
+ }
+ def createFile(failIfExists: Boolean = false): File = {
+ val res = jfile.createNewFile()
+ if (!res && failIfExists && exists) fail("File '%s' already exists." format name)
+ else if (isFile) toFile
+ else new File(jfile)
+ }
+
+ // deletions
+ def delete() = jfile.delete()
+ def deleteIfExists() = if (jfile.exists()) delete() else false
+
+ // todo
+ // def copyTo(target: Path, options ...): Boolean
+ // def moveTo(target: Path, options ...): Boolean
+
+ override def toString() = "Path(%s)".format(path)
+ override def equals(other: Any) = other match {
+ case x: Path => path == x.path
+ case _ => false
+ }
+ override def hashCode() = path.hashCode()
+}
diff --git a/src/compiler/scala/tools/nsc/io/PlainFile.scala b/src/compiler/scala/tools/nsc/io/PlainFile.scala
index 0394a16a93..926f5ee042 100644
--- a/src/compiler/scala/tools/nsc/io/PlainFile.scala
+++ b/src/compiler/scala/tools/nsc/io/PlainFile.scala
@@ -8,7 +8,8 @@
package scala.tools.nsc
package io
-import java.io.{File, FileInputStream, FileOutputStream, IOException}
+import java.io.{ File => JFile, FileInputStream, FileOutputStream, IOException }
+import PartialFunction._
object PlainFile
{
@@ -16,56 +17,46 @@ object PlainFile
* If the specified File exists, returns an abstract file backed
* by it. Otherwise, returns null.
*/
- def fromFile(file: File): PlainFile =
- if (file.exists()) new PlainFile(file) else null
-
- def fromPath(path: String): PlainFile = fromFile(new File(path))
+ def fromPath(file: Path): PlainFile =
+ if (file.exists) new PlainFile(file) else null
}
/** This class implements an abstract file backed by a File.
*/
-class PlainFile(val file: File) extends AbstractFile {
- private val fpath = try { file.getCanonicalPath }
- catch { case _: IOException => file.getAbsolutePath }
+class PlainFile(val givenPath: Path) extends AbstractFile {
+ assert(path ne null)
- assert(file ne null)
-// assert(file.exists(), "non-existent file: " + file)
+ val file = givenPath.jfile
+ private val fpath = try givenPath.normalize catch { case _: IOException => givenPath.toAbsolute }
/** Returns the name of this abstract file. */
- def name = file.getName()
+ def name = givenPath.name
/** Returns the path of this abstract file. */
- def path = file.getPath()
+ def path = givenPath.path
/** The absolute file. */
- def absolute = new PlainFile(file.getCanonicalFile())
-
- override def container : AbstractFile = new PlainFile(file.getParentFile)
+ def absolute = new PlainFile(givenPath.normalize)
- override def input = new FileInputStream(file)
- override def output = new FileOutputStream(file)
-
- override def sizeOption = Some(file.length.toInt)
+ override def container: AbstractFile = new PlainFile(givenPath.parent.get)
+ override def input = givenPath.toFile.inputStream()
+ override def output = givenPath.toFile.outputStream()
+ override def sizeOption = Some(givenPath.length.toInt)
override def hashCode(): Int = fpath.hashCode
-
override def equals(that: Any): Boolean =
- that.isInstanceOf[PlainFile] &&
- fpath.equals(that.asInstanceOf[PlainFile].fpath)
+ cond(that) { case other: PlainFile => fpath == other.fpath }
/** Is this abstract file a directory? */
- def isDirectory: Boolean = file.isDirectory()
+ def isDirectory: Boolean = givenPath.isDirectory
/** Returns the time that this abstract file was last modified. */
- def lastModified: Long = file.lastModified()
+ def lastModified: Long = givenPath.lastModified
/** Returns all abstract subfiles of this abstract directory. */
def iterator: Iterator[AbstractFile] = {
- assert(isDirectory, "not a directory '" + this + "'")
- val names: Array[String] = file.list()
- if ((names eq null) || names.length == 0) Iterator.empty
- else names.iterator.map { name: String => new File(file, name) }
- .filter(_.exists).map(file => new PlainFile(file))
+ assert(isDirectory, "not a directory '%s'" format this)
+ givenPath.toDirectory.list filter (_.exists) map (new PlainFile(_))
}
/**
@@ -79,34 +70,22 @@ class PlainFile(val file: File) extends AbstractFile {
* @return ...
*/
def lookupName(name: String, directory: Boolean): AbstractFile = {
- //assert(isDirectory, "not a directory '" + this + "'")
- val child = new File(file, name)
- if (!child.exists() || (directory != child.isDirectory) ||
- directory == child.isFile()) null
- else new PlainFile(child)
+ val child = givenPath / name
+ if ((child.isDirectory && directory) || (child.isFile && !directory)) new PlainFile(child)
+ else null
}
/** Does this abstract file denote an existing file? */
- def create {
- if (!exists)
- file.createNewFile()
- }
+ def create: Unit = if (!exists) givenPath.createFile()
/** Delete the underlying file or directory (recursively). */
- def delete {
- if (file.isFile) file.delete
- else if (file.isDirectory) {
- iterator.foreach(_.delete)
- file.delete
- }
- }
+ def delete: Unit =
+ if (givenPath.isFile) givenPath.delete()
+ else if (givenPath.isDirectory) givenPath.toDirectory.deleteRecursively()
/** Returns a plain file with the given name. It does not
* check that it exists.
*/
- def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = {
- val f = new File(file, name)
- new PlainFile(f)
- }
-
+ def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
+ new PlainFile(givenPath / name)
}
diff --git a/src/compiler/scala/tools/nsc/io/Process.scala b/src/compiler/scala/tools/nsc/io/Process.scala
new file mode 100644
index 0000000000..6a1029fa02
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/Process.scala
@@ -0,0 +1,159 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2009 LAMP/EPFL
+ */
+
+package scala.tools.nsc
+package io
+
+import annotation.experimental
+import concurrent.ThreadRunner
+import scala.util.Properties.{ isWin, isMac }
+import scala.util.control.Exception.catching
+import java.lang.{ Process => JProcess, ProcessBuilder => JProcessBuilder }
+import java.io.{ IOException, InputStream, OutputStream, BufferedReader, InputStreamReader, PrintWriter, File => JFile }
+import java.util.concurrent.LinkedBlockingQueue
+
+/** The <code>Process</code> object contains convenience functions
+ * for running external processes.
+ *
+ * An example usage:
+ * <pre>
+ * io.Process("ls", cwd = io.File("/")) foreach println
+ * </pre>
+ *
+ * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4109888
+ * for a dated list of the many obstacles to a clean interface.
+ *
+ * This is not finished!! Do not rely upon it yet.
+ *
+ * TODO - remove requirement that process complete before we
+ * can get an iterator.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+
+@experimental
+object Process
+{
+ lazy val javaVmArguments = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments()
+ lazy val runtime = Runtime.getRuntime()
+
+ @experimental
+ private[Process] class ProcessBuilder(val pb: JProcessBuilder)
+ {
+ def this(cmd: String*) = this(new JProcessBuilder(cmd.toArray: _*))
+ def start() = new Process(() => pb.start())
+
+ def withOnlyEnv(env: Map[String, String]): this.type = {
+ pb.environment.clear()
+ withEnv(env)
+ }
+
+ def withEnv(env: Map[String,String]): this.type = {
+ if (env != null) {
+ val jmap = pb.environment()
+ for ((k, v) <- env) jmap.put(k, v)
+ }
+ this
+ }
+
+ def withCwd(cwd: File): this.type = {
+ if (cwd != null)
+ pb directory cwd.jfile
+
+ this
+ }
+ def withRedirectedErrorStream(merged: Boolean): this.type = {
+ pb redirectErrorStream merged
+ this
+ }
+
+ override def toString() = "ProcessBuilder(%s)" format pb.command()
+ }
+
+ // This can be fleshed out if more variations come up
+ private val shell: String => Array[String] =
+ if (isWin) Array("cmd.exe", "/C", _)
+ else Array("sh", "-c", _)
+
+ /** Executes the given command line in a shell.
+ *
+ * @param command the command line
+ * @return a Process object
+ */
+ def apply(
+ command: String,
+ env: Map[String, String] = null,
+ cwd: File = null,
+ redirect: Boolean = false
+ ): Process =
+ exec(shell(command), env, cwd)
+
+ /** Executes the given command line.
+ *
+ * @param command the command line
+ * @return a Process object
+ */
+ def exec(
+ command: Seq[String],
+ env: Map[String, String] = null,
+ cwd: File = null,
+ redirect: Boolean = false
+ ): Process =
+ new ProcessBuilder(command: _*) withEnv env withCwd cwd start
+}
+import Process._
+
+@experimental
+class Process(processCreator: () => JProcess) extends Iterable[String]
+{
+ lazy val process = processCreator()
+
+ def exitValue(): Option[Int] =
+ catching(classOf[IllegalThreadStateException]) opt process.exitValue()
+
+ def waitFor() = process.waitFor()
+ def destroy() = process.destroy()
+ def rerun() = new Process(processCreator)
+
+ def stdout = iterator
+ def iterator = _out.iterator
+ def stderr = _err.iterator
+ lazy val stdin = new PrintWriter(_in, true)
+
+ class StreamedConsumer(in: InputStream) extends Thread with Iterable[String] {
+ private val queue = new LinkedBlockingQueue[String]
+ private val reader = new BufferedReader(new InputStreamReader(in))
+
+ def iterator = {
+ join() // make sure this thread is complete
+ new Iterator[String] {
+ val it = queue.iterator()
+ def hasNext = it.hasNext
+ def next = it.next
+ }
+ }
+ override def run() {
+ reader.readLine match {
+ case null =>
+ case x =>
+ queue put x
+ run()
+ }
+ }
+ }
+
+ private val _err = createConsumer(process.getErrorStream)
+ private val _out = createConsumer(process.getInputStream)
+ private val _in = process.getOutputStream()
+
+ private def createConsumer(in: InputStream) = {
+ val t = new StreamedConsumer(in)
+ t.start()
+ t
+ }
+
+ override def toString() = "Process(%s)" format process.toString()
+}
+
diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala
index 21b4305b6b..f2b3373619 100644
--- a/src/compiler/scala/tools/nsc/io/SourceReader.scala
+++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala
@@ -8,7 +8,7 @@
package scala.tools.nsc
package io
-import java.io.{File, FileInputStream, InputStream, IOException}
+import java.io.{ FileInputStream, InputStream, IOException, File => JFile }
import java.nio.{ByteBuffer, CharBuffer}
import java.nio.channels.{FileChannel, ReadableByteChannel, Channels}
import java.nio.charset.{CharsetDecoder, CoderResult}
@@ -38,10 +38,10 @@ class SourceReader(decoder: CharsetDecoder, reporter: Reporter) {
// Public Methods
/** Reads the file with the specified name. */
- def read(filename: String): Array[Char]= read(new File(filename))
+ def read(filename: String): Array[Char]= read(new JFile(filename))
/** Reads the specified file. */
- def read(file: File): Array[Char] = {
+ def read(file: JFile): Array[Char] = {
val c = new FileInputStream(file).getChannel
try {
read(c)
diff --git a/src/compiler/scala/tools/nsc/io/Streamable.scala b/src/compiler/scala/tools/nsc/io/Streamable.scala
new file mode 100644
index 0000000000..0ff1c48ce5
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/io/Streamable.scala
@@ -0,0 +1,108 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2009 LAMP/EPFL
+ */
+
+package scala.tools.nsc
+package io
+
+import java.net.{ URI, URL }
+import java.io.{ BufferedInputStream, InputStream, PrintStream, File => JFile }
+import java.io.{ BufferedReader, InputStreamReader }
+import scala.io.{ Codec, Source }
+
+import collection.mutable.ArrayBuffer
+import Path.fail
+
+/** Traits for objects which can be represented as Streams.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
+
+object Streamable
+{
+ /** Traits which can be viewed as a sequence of bytes. Source types
+ * which know their length should override def length: Long for more
+ * efficient method implementations.
+ */
+ trait Bytes {
+ def inputStream(): InputStream
+ def length: Long = -1
+
+ def bufferedInput() = new BufferedInputStream(inputStream())
+ def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte)
+ def bytesAsInts(): Iterator[Int] = {
+ val in = bufferedInput()
+ Iterator continually in.read() takeWhile (_ != -1)
+ }
+
+ /** This method aspires to be the fastest way to read
+ * a stream of known length into memory.
+ */
+ def toByteArray(): Array[Byte] = {
+ // if we don't know the length, fall back on relative inefficiency
+ if (length == -1L)
+ return (new ArrayBuffer[Byte]() ++ bytes()).toArray
+
+ val arr = new Array[Byte](length.toInt)
+ val len = arr.length
+ lazy val in = bufferedInput()
+ var offset = 0
+
+ def loop() {
+ if (offset < len) {
+ val read = in.read(arr, offset, len - offset)
+ if (read >= 0) {
+ offset += read
+ loop()
+ }
+ }
+ }
+ try loop()
+ finally in.close()
+
+ if (offset == arr.length) arr
+ else fail("Could not read entire source (%d of %d bytes)".format(offset, len))
+ }
+ }
+
+ /** For objects which can be viewed as Chars. The abstract creationCodec
+ * can safely be defined as null and will subsequently be ignored.
+ */
+ trait Chars extends Bytes {
+ def creationCodec: Codec
+ private def failNoCodec() = fail("This method requires a Codec to be chosen explicitly.")
+
+ /** The general algorithm for any call to a method involving byte<->char
+ * transformations is: if a codec is supplied (explicitly or implicitly),
+ * use that; otherwise if a codec was defined when the object was created,
+ * use that; otherwise, use Codec.default.
+ *
+ * Note that getCodec takes a codec argument rather than having methods
+ * always default to getCodec() and use the argument otherwise, so that
+ * method implementations can, where desired, identify the case where no
+ * codec was ever explicitly supplied. If allowDefault = false, an
+ * exception will be thrown rather than falling back on Codec.default.
+ */
+ def getCodec(givenCodec: Codec = null, allowDefault: Boolean = true) =
+ if (givenCodec != null) givenCodec
+ else if (creationCodec != null) creationCodec
+ else if (allowDefault) Codec.default
+ else failNoCodec()
+
+ def chars(codec: Codec = getCodec()): Source = (Source fromInputStream inputStream())(codec)
+ def lines(codec: Codec = getCodec()): Iterator[String] = chars(codec).getLines()
+
+ /** Obtains an InputStreamReader wrapped around a FileInputStream.
+ */
+ def reader(codec: Codec = getCodec()) = new InputStreamReader(inputStream, codec.charSet)
+
+ /** Wraps a BufferedReader around the result of reader().
+ */
+ def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec))
+
+ /** Convenience function to import entire file into a String.
+ */
+ def slurp(codec: Codec = getCodec()) = chars(codec).mkString
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala b/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala
index 16381bbe6e..0d394fdddd 100644
--- a/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala
+++ b/src/compiler/scala/tools/nsc/io/VirtualDirectory.scala
@@ -1,10 +1,11 @@
/* NSC -- new Scala compiler
* Copyright 2005-2009 LAMP/EPFL
*/
-// $Id$
+
package scala.tools.nsc
package io
-import scala.collection.{mutable=>mut}
+
+import scala.collection.mutable
/**
* An in-memory directory.
@@ -24,9 +25,7 @@ extends AbstractFile {
def container = maybeContainer.get
def isDirectory = true
var lastModified: Long = System.currentTimeMillis
- private def updateLastModified {
- lastModified = System.currentTimeMillis
- }
+
override def file = null
override def input = error("directories cannot be read")
override def output = error("directories cannot be written")
@@ -47,44 +46,28 @@ extends AbstractFile {
def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
throw new UnsupportedOperationException()
- private val files = mut.Map.empty[String, AbstractFile]
+ private val files = mutable.Map.empty[String, AbstractFile]
// the toList is so that the directory may continue to be
// modified while its elements are iterated
def iterator = files.valuesIterator.toList.iterator
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- files.get(name) match {
- case None => null
- case Some(file) =>
- if (file.isDirectory == directory)
- file
- else
- null
- }
- }
+ override def lookupName(name: String, directory: Boolean): AbstractFile =
+ files get name filter (_.isDirectory == directory) orNull
- override def fileNamed(name: String): AbstractFile = {
- val existing = lookupName(name, false)
- if (existing == null) {
+ override def fileNamed(name: String): AbstractFile =
+ Option(lookupName(name, false)) getOrElse {
val newFile = new VirtualFile(name, path+'/'+name)
files(name) = newFile
newFile
- } else {
- existing
}
- }
- override def subdirectoryNamed(name: String): AbstractFile = {
- val existing = lookupName(name, true)
- if (existing == null) {
+ override def subdirectoryNamed(name: String): AbstractFile =
+ Option(lookupName(name, true)) getOrElse {
val dir = new VirtualDirectory(name, Some(this))
files(name) = dir
dir
- } else {
- existing
}
- }
def clear() {
files.clear();
diff --git a/src/compiler/scala/tools/nsc/io/VirtualFile.scala b/src/compiler/scala/tools/nsc/io/VirtualFile.scala
index 14d081991e..90769d7086 100644
--- a/src/compiler/scala/tools/nsc/io/VirtualFile.scala
+++ b/src/compiler/scala/tools/nsc/io/VirtualFile.scala
@@ -8,16 +8,16 @@
package scala.tools.nsc
package io
-import java.io.{ByteArrayInputStream, ByteArrayOutputStream,
- File, InputStream, OutputStream}
+import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, InputStream, OutputStream, File => JFile }
+import PartialFunction._
/** This class implements an in-memory file.
*
* @author Philippe Altherr
* @version 1.0, 23/03/2004
*/
-class VirtualFile(val name: String, _path: String) extends AbstractFile {
-
+class VirtualFile(val name: String, _path: String) extends AbstractFile
+{
assert((name ne null) && (path ne null), name + " - " + path)
//########################################################################
@@ -33,11 +33,7 @@ class VirtualFile(val name: String, _path: String) extends AbstractFile {
def this(name: String) = this(name, name)
override def hashCode = name.hashCode
- override def equals(that : Any) = that match {
- case that : VirtualFile => name == that.name
- case _ => false
- }
-
+ override def equals(that: Any) = cond(that) { case x: VirtualFile => x.name == name }
//########################################################################
// Private data
@@ -51,7 +47,7 @@ class VirtualFile(val name: String, _path: String) extends AbstractFile {
def absolute = this
/** Returns null. */
- final def file: File = null
+ final def file: JFile = null
override def sizeOption: Option[Int] = Some(content.size)
diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
index 9d322af889..a3c3c85a15 100644
--- a/src/compiler/scala/tools/nsc/io/ZipArchive.scala
+++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
@@ -8,28 +8,24 @@
package scala.tools.nsc
package io
-import java.io.{File, IOException, InputStream}
import java.net.URL
import java.util.Enumeration
-import java.util.zip.{ZipEntry, ZipFile, ZipInputStream}
+import java.io.{ File => JFile, IOException, InputStream, BufferedInputStream, ByteArrayInputStream }
+import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
+import PartialFunction._
-import scala.collection.mutable.{Map, HashMap}
+import scala.collection.Traversable
+import scala.collection.mutable.{ Map, HashMap }
+import scala.collection.immutable.{ StringVector => SV }
+import scala.collection.JavaConversions.asIterator
/**
* @author Philippe Altherr
* @version 1.0, 23/03/2004
*/
-object ZipArchive {
-
- //########################################################################
-
- /**
- * ...
- *
- * @param path ...
- * @return ...
- */
- def fromPath(path: String): AbstractFile = fromFile(new File(path))
+object ZipArchive
+{
+ def fromPath(path: Path): ZipArchive = fromFile(path.toFile)
/**
* If the specified file <code>file</code> exists and is a readable
@@ -39,56 +35,115 @@ object ZipArchive {
* @param file ...
* @return ...
*/
- def fromFile(file: File): AbstractFile =
- try { new ZipArchive(file, new ZipFile(file)) }
+ def fromFile(file: File): ZipArchive =
+ try new ZipArchive(file, new ZipFile(file.jfile))
catch { case _: IOException => null }
/**
* Returns an abstract directory backed by the specified archive.
- *
- * @param archive ...
- * @return ...
*/
- def fromArchive(archive: ZipFile): AbstractFile =
- new ZipArchive(new File(archive.getName()), archive)
+ def fromArchive(archive: ZipFile): ZipArchive =
+ new ZipArchive(File(archive.getName()), archive)
/**
* Returns an abstract directory backed by the specified archive.
- *
- * @param url ...
- * @return ...
*/
- def fromURL(url: URL): AbstractFile =
- new URLZipArchive(url)
+ def fromURL(url: URL): AbstractFile = new URLZipArchive(url)
+
+ private[io] class ZipEntryTraversableClass(in: InputStream) extends Traversable[ZipEntry] {
+ val zis = new ZipInputStream(in)
+
+ def foreach[U](f: ZipEntry => U) = {
+ def loop(x: ZipEntry): Unit = if (x != null) {
+ f(x)
+ zis.closeEntry()
+ loop(zis.getNextEntry())
+ }
+ loop(zis.getNextEntry())
+ }
+ }
}
-/**
- * This class implements an abstract directory backed by a zip
- * archive. We let the encoding be <code>null</code>, because we behave like
- * a directory.
- *
- * @author Philippe Altherr
- * @version 1.0, 23/03/2004
+/** This abstraction aims to factor out the common code between
+ * ZipArchive (backed by a zip file) and URLZipArchive (backed
+ * by an InputStream.)
*/
-final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) {
+private[io] trait ZipContainer extends AbstractFile
+{
+ /** Abstract types */
+ type SourceType // InputStream or AbstractFile
+ type CreationType // InputStream or ZipFile
+ type ZipTrav = Traversable[ZipEntry] { def zis: ZipInputStream }
+
+ /** Abstract values */
+ protected val creationSource: CreationType
+ protected val root: DirEntryInterface
+ protected def DirEntryConstructor: (AbstractFile, String, String) => DirEntryInterface
+ protected def FileEntryConstructor: (SourceType, String, String, ZipEntry) => FileEntryInterface
+ protected def ZipTravConstructor: CreationType => ZipTrav
+
+ protected[io] trait EntryInterface extends VirtualFile {
+ def name: String
+ def path: String
+ }
- assert(archive ne null)
- //########################################################################
- // Private Fields
+ protected[io] trait DirEntryInterface extends EntryInterface {
+ def source: SourceType
+ val entries: Map[String, EntryInterface] = new HashMap()
+ var entry: ZipEntry = _
- /** The root directory or null if not yet initialized */
- private var root: DirEntry = _
+ override def input = throw new Error("cannot read directories")
+ override def lastModified: Long =
+ if (entry ne null) entry.getTime() else super.lastModified
- //########################################################################
- // Public Methods
+ override def isDirectory = true
+ override def iterator: Iterator[AbstractFile] = entries.valuesIterator
+ override def lookupName(name: String, directory: Boolean): AbstractFile = {
+ def slashName = if (directory) name + "/" else name
+ entries.getOrElse(slashName, null)
+ }
+ }
- /** Returns true. */
- override def isDirectory = true
+ protected[io] trait FileEntryInterface extends EntryInterface {
+ def entry: ZipEntry
- /** Returns all abstract subfiles of this abstract directory. */
- override def iterator: Iterator[AbstractFile] = {
- if (root eq null) load()
- root.iterator
+ override def lastModified: Long = entry.getTime()
+ override def sizeOption = Some(entry.getSize().toInt)
+ }
+
+ class ZipRootCreator(f: ZipRootCreator => SourceType) {
+ val root = DirEntryConstructor(ZipContainer.this, "<root>", "/")
+
+ // Map from paths to DirEntries
+ val dirs = HashMap[String, DirEntryInterface]("/" -> root)
+ val traverser = ZipTravConstructor(creationSource)
+ private[this] var _parent: DirEntryInterface = _
+ def parent = _parent
+
+ def addEntry(entry: ZipEntry) {
+ val path = entry.getName
+ if (entry.isDirectory) {
+ val dir: DirEntryInterface = getDir(dirs, path)
+ if (dir.entry == null) dir.entry = entry
+ }
+ else {
+ val (home, name) = splitPath(path)
+ _parent = getDir(dirs, home)
+ _parent.entries(name) = FileEntryConstructor(f(this), name, path, entry)
+ }
+ }
+
+ def apply() = {
+ traverser foreach addEntry
+ root
+ }
+ }
+
+ protected def splitPath(path: String): (String, String) = {
+ (path lastIndexOf '/') match {
+ case -1 => ("/", path)
+ case idx => SV.splitAt(path, idx + 1)
+ }
}
/**
@@ -97,10 +152,8 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
* argument "directory" tells whether to look for a directory or
* or a regular file.
*/
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- if (root eq null) load()
+ override def lookupName(name: String, directory: Boolean): AbstractFile =
root.lookupName(name, directory)
- }
/** Returns an abstract file with the given name. It does not
* check that it exists.
@@ -108,114 +161,90 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
override def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
throw new UnsupportedOperationException()
- //########################################################################
- // Private Methods
-
- /** Loads the archive and creates the root directory. */
- private def load() {
- this.root = new DirEntry(this, "<root>", "/")
- // A path to DirEntry map
- val dirs: Map[String, DirEntry] = new HashMap()
- dirs.update("/", root)
- val entries = archive.entries()
- while (entries.hasMoreElements()) {
- val entry = entries.nextElement().asInstanceOf[ZipEntry]
- val path = entry.getName()
- assert(entry.isDirectory() == path.endsWith("/"),
- this.toString() + " - " + path);
- if (entry.isDirectory()) {
- val dir: DirEntry = getDir(dirs, path)
- // this assertion causes an unnecessary bomb if a directory is twice listed in the jar
- // assert(dir.entry eq null, this.toString() + " - " + path)
- if (dir.entry eq null) dir.entry = entry
- } else {
- val index = path.lastIndexOf('/')
- val name = if (index < 0) path else path.substring(index + 1)
- val home = if (index < 0) "/" else path.substring(0, index + 1)
- val parent: DirEntry = getDir(dirs, home)
- // OLD: assert(!parent.entries.contains(path))
- // MAYBE: assert(!parent.entries.contains(name))
- //if (parent.entries.contains(name))
- // Console.println("XXX: " + this.toString() + " - " + home + "/" + name)
- parent.entries.update(name, new FileEntry(parent, name, path, entry))
- }
- }
- }
+ /** Returns all abstract subfiles of this abstract directory. */
+ override def iterator: Iterator[AbstractFile] = root.iterator
/**
- * Lookups the specified table for a DirEntry with the specified
- * path. If successful, returns the found DirEntry. Otherwise
- * creates a new DirEntry, enters it into the table and in the
- * table of its parent ZipDir and returns it.
+ * Looks up the path in the given map and returns if found.
+ * If not present, creates a new DirEntry, adds to both given
+ * map and parent.entries, and returns it.
*/
- private def getDir(dirs: Map[String,DirEntry], path: String): DirEntry =
- dirs.get(path) match {
- case Some(dir) => dir
- case None =>
- val index = path.lastIndexOf('/', path.length() - 2);
- val name = if (index < 0) path else path.substring(index + 1);
- val home = if (index < 0) "/" else path.substring(0, index + 1);
- val parent: DirEntry = getDir(dirs, home);
- val dir = new DirEntry(parent, name.substring(0, name.length() - 1), path);
- parent.entries.update(name, dir);
- dirs.update(path, dir);
- dir
- }
-
- //########################################################################
- // Private Class - Entry
-
- /** Superclass of archive entries */
- abstract class Entry(override val container : AbstractFile, name: String, path: String)
- extends VirtualFile(name, path) {
- final override def path = ZipArchive.this.toString() + "(" + pathInArchive + ")"
- final def getArchive = ZipArchive.this.archive
- def pathInArchive = super.path
- override def hashCode = super.hashCode + container.hashCode
- override def equals(that : Any) = super.equals(that) && (that match {
- case entry : Entry => container == entry.container
- case _ => false
+ protected def getDir(dirs: Map[String, DirEntryInterface], path: String): DirEntryInterface =
+ dirs.getOrElseUpdate(path, {
+ val (home, name) = splitPath(SV init path)
+ val parent = getDir(dirs, home)
+ val dir = DirEntryConstructor(parent, name, path)
+ parent.entries(name + path.last) = dir
+ dir
})
- }
- //########################################################################
- // Private Class - DirEntry
+ override def isDirectory = true
+}
- /** A directory archive entry */
- private final class DirEntry(container : AbstractFile, name: String, path: String)
- extends Entry(container, name, path)
+/**
+ * This class implements an abstract directory backed by a zip
+ * archive. We let the encoding be <code>null</code>, because we behave like
+ * a directory.
+ *
+ * @author Philippe Altherr
+ * @version 1.0, 23/03/2004
+ */
+final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) with ZipContainer
+{
+ self =>
+
+ type SourceType = AbstractFile
+ type CreationType = ZipFile
+
+ protected val creationSource = archive
+ protected lazy val root = new ZipRootCreator(_.parent)()
+ protected def DirEntryConstructor = new DirEntry(_, _, _)
+ protected def FileEntryConstructor = new FileEntry(_, _, _, _)
+ protected def ZipTravConstructor = zipTraversableFromZipFile _
+
+ abstract class Entry(
+ override val container: AbstractFile,
+ name: String,
+ path: String
+ ) extends VirtualFile(name, path)
{
+ final override def path = "%s(%s)".format(self, pathInArchive)
+ final def getArchive = self.archive
+ def pathInArchive = super.path
- val entries: Map[String, Entry] = new HashMap()
-
- var entry: ZipEntry = _
-
- override def isDirectory = true
- override def input = throw new Error("cannot read directories")
-
- override def lastModified: Long =
- if (entry ne null) entry.getTime() else super.lastModified
-
- override def iterator: Iterator[AbstractFile] = entries.valuesIterator
-
- override def lookupName(name: String, directory: Boolean): AbstractFile =
- entries.get(if (directory) name + "/" else name) match {
- case Some(dir) => dir
- case None => null
- }
+ override def hashCode = super.hashCode + container.hashCode
+ override def equals(that : Any) =
+ super.equals(that) && (cond(that) {
+ case e: Entry => container == e.container
+ })
}
- //########################################################################
- // Private Class - FileEntry
+ final class DirEntry(
+ container: AbstractFile,
+ name: String,
+ path: String
+ ) extends Entry(container, name, path) with DirEntryInterface
+ {
+ def source = container
+ }
- /** A regular file archive entry */
- final class FileEntry(container : AbstractFile, name: String, path: String, val entry: ZipEntry)
- extends Entry(container, name, path) {
- def archive = ZipArchive.this.archive
- override def lastModified: Long = entry.getTime()
- override def input = archive.getInputStream(entry)
- override def sizeOption = Some(entry.getSize().toInt)
+ final class FileEntry(
+ container: AbstractFile,
+ name: String,
+ path: String,
+ val entry: ZipEntry
+ ) extends Entry(container, name, path) with FileEntryInterface
+ {
+ def archive = self.archive
+ override def input = archive getInputStream entry
}
+
+ private def zipTraversableFromZipFile(z: ZipFile): ZipTrav =
+ new Traversable[ZipEntry] {
+ def zis: ZipInputStream = null // not valid for this type
+ val itStream = asIterator(z.entries()).toStream
+ def foreach[U](f: ZipEntry => U) = itStream foreach f
+ }
}
/**
@@ -225,144 +254,53 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
* @author Stephane Micheloud
* @version 1.0, 29/05/2007
*/
-final class URLZipArchive(url: URL) extends AbstractFile {
- assert(url ne null)
+final class URLZipArchive(url: URL) extends AbstractFile with ZipContainer
+{
+ type SourceType = InputStream
+ type CreationType = InputStream
- private var root: DirEntry = _
+ protected lazy val creationSource = input
+ protected lazy val root = new ZipRootCreator(x => byteInputStream(x.traverser.zis))()
- def container = throw new Error("unsupported")
+ protected def DirEntryConstructor = (_, name, path) => new DirEntry(name, path)
+ protected def FileEntryConstructor = new FileEntry(_, _, _, _)
+ protected def ZipTravConstructor = new ZipArchive.ZipEntryTraversableClass(_)
def name: String = url.getFile()
-
def path: String = url.getPath()
-
- def file: File = null
-
+ def input: InputStream = url.openStream()
def absolute: AbstractFile = this
-
- def isDirectory: Boolean = true
-
def lastModified: Long =
- try { url.openConnection().getLastModified() }
- catch { case _ => 0 }
-
- /** Does this abstract file denote an existing file? */
- def create {
- throw new UnsupportedOperationException
- }
-
- /** Delete the underlying file or directory (recursively). */
- def delete {
- throw new UnsupportedOperationException
- }
-
- def input: InputStream = url.openStream()
+ try url.openConnection().getLastModified()
+ catch { case _: IOException => 0 }
+ /** Methods we don't support but have to implement because of the design */
+ def file: JFile = null
+ def create: Unit = throw new UnsupportedOperationException
+ def delete: Unit = throw new UnsupportedOperationException
def output = throw new Error("unsupported")
+ def container = throw new Error("unsupported")
- override def iterator: Iterator[AbstractFile] = {
- if (root eq null) load()
- root.iterator
- }
-
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- if (root eq null) load()
- root.lookupName(name, directory)
- }
-
- /** Returns an abstract file with the given name. It does not
- * check that it exists.
- */
- def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
- throw new UnsupportedOperationException()
-
- private def load() {
- def getEntryInputStream(in: InputStream): InputStream = {
- val buf = new scala.collection.mutable.ArrayBuffer[Byte]
- val data = new Array[Byte](1024)
- var n = in.read(data)
- while (n > 0) {
- buf.++=(data, 0, n)
- n = in.read(data)
- }
- new java.io.ByteArrayInputStream(buf.toArray)
- }
- this.root = new DirEntry("<root>", "/")
- // A path to DirEntry map
- val dirs: Map[String, DirEntry] = new HashMap()
- dirs.update("/", root)
- val zis = new ZipInputStream(input)
- var entry = zis.getNextEntry()
- while (entry ne null) {
- val path = entry.getName()
- assert(entry.isDirectory() == path.endsWith("/"),
- this.toString() + " - " + path);
- if (entry.isDirectory()) {
- val dir: DirEntry = getDir(dirs, path)
- assert(dir.entry eq null, this.toString() + " - " + path)
- dir.entry = entry
- } else {
- val index = path.lastIndexOf('/')
- val name = if (index < 0) path else path.substring(index + 1)
- val home = if (index < 0) "/" else path.substring(0, index + 1)
- val parent: DirEntry = getDir(dirs, home)
- assert(!parent.entries.contains(path), this.toString() + " - " + path)
- val in = getEntryInputStream(zis)
- parent.entries.update(name, new FileEntry(name, path, entry, in))
- }
- zis.closeEntry()
- entry = zis.getNextEntry()
- }
+ abstract class Entry(name: String, path: String) extends VirtualFile(name, path) {
+ final override def path = "%s(%s)".format(URLZipArchive.this, super.path)
}
-
- private def getDir(dirs: Map[String, DirEntry], path: String): DirEntry =
- dirs.get(path) match {
- case Some(dir) => dir
- case None =>
- val index = path.lastIndexOf('/', path.length() - 2)
- val name = if (index < 0) path else path.substring(index + 1)
- val home = if (index < 0) "/" else path.substring(0, index + 1)
- val parent: DirEntry = getDir(dirs, home)
- val dir = new DirEntry(name.substring(0, name.length() - 1), path)
- parent.entries.update(name, dir)
- dirs.update(path, dir)
- dir
- }
-
- /** Superclass of archive entries */
- abstract class Entry(name: String, path: String)
- extends VirtualFile(name, path) {
- final override def path = URLZipArchive.this.toString() + "(" + super.path + ")"
- //final def getArchive = URLZipArchive.this.archive
+ final class DirEntry(name: String, path: String) extends Entry(name, path) with DirEntryInterface {
+ def source = input
}
-
- /** A directory archive entry */
- private final class DirEntry(name: String, path: String)
- extends Entry(name, path)
+ final class FileEntry(
+ val in: InputStream,
+ name: String,
+ path: String,
+ val entry: ZipEntry
+ ) extends Entry(name, path) with FileEntryInterface
{
- val entries: Map[String, Entry] = new HashMap()
- var entry: ZipEntry = _
-
- override def isDirectory = true
- override def input = throw new Error("cannot read directories")
-
- override def lastModified: Long =
- if (entry ne null) entry.getTime() else super.lastModified
-
- override def iterator: Iterator[AbstractFile] = entries.valuesIterator
-
- override def lookupName(name: String, directory: Boolean): AbstractFile =
- entries.get(if (directory) name + "/" else name) match {
- case Some(dir) => dir
- case None => null
- }
+ override def input = in
}
- final class FileEntry(name: String, path: String,
- val entry: ZipEntry, val in: InputStream)
- extends Entry(name, path) {
- override def lastModified: Long = entry.getTime()
- override def input = in
- override def sizeOption = Some(entry.getSize().toInt)
+ /** Private methods **/
+ private def byteInputStream(in: InputStream): InputStream = {
+ val buf = new BufferedInputStream(in)
+ val bytes = Iterator continually in.read().toByte takeWhile (_ != -1)
+ new ByteArrayInputStream(bytes.toSequence.toArray)
}
}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index 3013199c36..2b61724c49 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -7,7 +7,7 @@
package scala.tools.nsc
package plugins
-import java.io.File
+import io.{ File, Path }
import java.net.URLClassLoader
import java.util.jar.JarFile
import java.util.zip.ZipException
@@ -63,39 +63,44 @@ abstract class Plugin {
* @author Lex Spoon
* @version 1.0, 2007-5-21
*/
-object Plugin {
+object Plugin
+{
+ private val PluginXML = "scalac-plugin.xml"
+
/** Create a class loader with the specified file plus
* the loader that loaded the Scala compiler.
*/
- private def loaderFor(jarfiles: Seq[File]): ClassLoader = {
+ private def loaderFor(jarfiles: Seq[Path]): ClassLoader = {
val compilerLoader = classOf[Plugin].getClassLoader
- val jarurls = jarfiles.map(_.toURL).toArray
- new URLClassLoader(jarurls, compilerLoader)
+ val jarurls = jarfiles map (_.toURL)
+
+ new URLClassLoader(jarurls.toArray, compilerLoader)
}
/** Try to load a plugin description from the specified
* file, returning None if it does not work. */
- private def loadDescription(jarfile: File): Option[PluginDescription] = {
- if (!jarfile.exists) return None
+ private def loadDescription(jarfile: Path): Option[PluginDescription] =
+ // XXX Return to this once we have some ARM support
+ if (!jarfile.exists) None
+ else try {
+ val jar = new JarFile(jarfile.jfile)
- try {
- val jar = new JarFile(jarfile)
try {
- val ent = jar.getEntry("scalac-plugin.xml")
- if (ent == null) return None
-
- val inBytes = jar.getInputStream(ent)
- val packXML = XML.load(inBytes)
- inBytes.close()
-
- PluginDescription.fromXML(packXML)
- } finally {
- jar.close()
+ (jar getEntry PluginXML) match {
+ case null => None
+ case entry =>
+ val in = jar getInputStream entry
+ val packXML = XML load in
+ in.close()
+
+ PluginDescription fromXML packXML
+ }
}
- } catch {
+ finally jar.close()
+ }
+ catch {
case _: ZipException => None
}
- }
type AnyClass = Class[_]
@@ -103,16 +108,13 @@ object Plugin {
* if the jar file has no plugin in it or if the plugin
* is badly formed.
*/
- def loadFrom(jarfile: File, loader: ClassLoader): Option[AnyClass] = {
+ def loadFrom(jarfile: Path, loader: ClassLoader): Option[AnyClass] = {
val pluginInfo = loadDescription(jarfile).get
- try {
- Some(loader.loadClass(pluginInfo.classname))
- } catch {
- case _:ClassNotFoundException =>
- println("Warning: class not found for plugin in " + jarfile +
- " (" + pluginInfo.classname + ")")
- None
+ try Some(loader loadClass pluginInfo.classname) catch {
+ case _: ClassNotFoundException =>
+ println("Warning: class not found for plugin in %s (%s)".format(jarfile, pluginInfo.classname))
+ None
}
}
@@ -121,35 +123,28 @@ object Plugin {
* directories specified. Skips all plugins in <code>ignoring</code>.
* A single classloader is created and used to load all of them.
*/
- def loadAllFrom(jars: List[File],
- dirs: List[File],
- ignoring: List[String]): List[AnyClass] =
+ def loadAllFrom(
+ jars: List[Path],
+ dirs: List[Path],
+ ignoring: List[String]): List[AnyClass] =
{
- val alljars = new ListBuffer[File]
-
- alljars ++= jars
-
- for {
+ val alljars = jars ::: (for {
dir <- dirs if dir.isDirectory
- entries = dir.listFiles
- if entries ne null
- entry <- entries.toList.sort(_.getName <= _.getName)
- if entry.toString.toLowerCase endsWith ".jar"
+ entry <- dir.toDirectory.files.toList sortWith (_.name <= _.name)
+ if entry.name.toLowerCase endsWith ".jar"
pdesc <- loadDescription(entry)
if !(ignoring contains pdesc.name)
- } alljars += entry
+ } yield entry)
- val loader = loaderFor(alljars.toList)
- alljars.toList.map(f => loadFrom(f,loader)).flatMap(x => x)
+ val loader = loaderFor(alljars)
+ alljars map (loadFrom(_, loader)) flatten
}
/** Instantiate a plugin class, given the class and
* the compiler it is to be used in.
*/
def instantiate(clazz: AnyClass, global: Global): Plugin = {
- //println("instantiating "+clazz)
- //println(clazz.getDeclaredConstructors)
- val constructor = clazz.getConstructor(classOf[Global])
- constructor.newInstance(global).asInstanceOf[Plugin]
+ val constructor = clazz getConstructor classOf[Global]
+ (constructor newInstance global).asInstanceOf[Plugin]
}
}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
index 1163ae5a6a..93a3f46ac2 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
@@ -8,7 +8,7 @@
package scala.tools.nsc
package plugins
-import java.io.File
+import io.{ File, Path }
/** Support for run-time loading of compiler plugins.
*
@@ -16,7 +16,9 @@ import java.io.File
* @version 1.1, 2009/1/2
* Updated 2009/1/2 by Anders Bach Nielsen: Added features to implement SIP 00002
*/
-trait Plugins { self: Global =>
+trait Plugins
+{
+ self: Global =>
/** Load a rough list of the plugins. For speed, it
* does not instantiate a compiler run. Therefore it cannot
@@ -24,24 +26,17 @@ trait Plugins { self: Global =>
* filtered from the final list of plugins.
*/
protected def loadRoughPluginsList(): List[Plugin] = {
- val jars = settings.plugin.value.map(new File(_))
- val dirs =
- for (name <- settings.pluginsDir.value.split(File.pathSeparator).toList)
- yield new File(name)
+ val jars = settings.plugin.value map Path.apply
+ val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map Path.apply
+ val classes = Plugin.loadAllFrom(jars, dirs, settings.disable.value)
+
+ classes foreach (c => Plugin.instantiate(c, this))
for (plugClass <- Plugin.loadAllFrom(jars, dirs, settings.disable.value))
yield Plugin.instantiate(plugClass, this)
}
- private var roughPluginsListCache: Option[List[Plugin]] = None
-
- protected def roughPluginsList: List[Plugin] =
- roughPluginsListCache match {
- case Some(list) => list
- case None =>
- roughPluginsListCache = Some(loadRoughPluginsList)
- roughPluginsListCache.get
- }
+ protected lazy val roughPluginsList: List[Plugin] = loadRoughPluginsList
/** Load all available plugins. Skips plugins that
* either have the same name as another one, or which
@@ -54,105 +49,70 @@ trait Plugins { self: Global =>
plugNames: Set[String],
phaseNames: Set[String]): List[Plugin] =
{
- plugins match {
- case Nil => Nil
- case plug :: rest =>
- val plugPhaseNames = Set.empty ++ plug.components.map(_.phaseName)
- def withoutPlug = pick(rest, plugNames, plugPhaseNames)
- def withPlug =
- (plug ::
- pick(rest,
- plugNames+plug.name,
- phaseNames++plugPhaseNames))
-
- if (plugNames.contains(plug.name)) {
- if (settings.verbose.value)
- inform("[skipping a repeated plugin: " + plug.name + "]")
- withoutPlug
- } else if (settings.disable.value contains(plug.name)) {
- if (settings.verbose.value)
- inform("[disabling plugin: " + plug.name + "]")
- withoutPlug
- } else {
- val commonPhases = phaseNames.intersect(plugPhaseNames)
- if (!commonPhases.isEmpty) {
- if (settings.verbose.value)
- inform("[skipping plugin " + plug.name +
- "because it repeats phase names: " +
- commonPhases.mkString(", ") + "]")
- withoutPlug
- } else {
- if (settings.verbose.value)
- inform("[loaded plugin " + plug.name + "]")
- withPlug
- }
- }
+ if (plugins.isEmpty) return Nil // early return
+
+ val plug :: tail = plugins
+ val plugPhaseNames = Set(plug.components map (_.phaseName): _*)
+ def withoutPlug = pick(tail, plugNames, plugPhaseNames)
+ def withPlug = plug :: pick(tail, plugNames + plug.name, phaseNames ++ plugPhaseNames)
+ lazy val commonPhases = phaseNames intersect plugPhaseNames
+
+ def note(msg: String): Unit = if (settings.verbose.value) inform(msg format plug.name)
+ def fail(msg: String) = { note(msg) ; withoutPlug }
+
+ if (plugNames contains plug.name)
+ fail("[skipping a repeated plugin: %s]")
+ else if (settings.disable.value contains plug.name)
+ fail("[disabling plugin: %s]")
+ else if (!commonPhases.isEmpty)
+ fail("[skipping plugin %s because it repeats phase names: " + (commonPhases mkString ", ") + "]")
+ else {
+ note("[loaded plugin %s]")
+ withPlug
}
}
- val plugs =
- pick(roughPluginsList,
- Set.empty,
- Set.empty ++ phasesSet.map(_.phaseName))
+ val plugs = pick(roughPluginsList, Set(), phasesSet map (_.phaseName) toSet)
- for (req <- settings.require.value; if !plugs.exists(p => p.name==req))
+ /** Verify requirements are present. */
+ for (req <- settings.require.value ; if !(plugs exists (_.name == req)))
error("Missing required plugin: " + req)
+ /** Process plugin options. */
+ def namec(plug: Plugin) = plug.name + ":"
+ def optList(xs: List[String], p: Plugin) = xs filter (_ startsWith namec(p))
+ def doOpts(p: Plugin): List[String] =
+ optList(settings.pluginOptions.value, p) map (_ stripPrefix namec(p))
- for (plug <- plugs) {
- val nameColon = plug.name + ":"
- val opts = for {
- raw <- settings.pluginOptions.value
- if raw.startsWith(nameColon)
- } yield raw.substring(nameColon.length)
-
+ for (p <- plugs) {
+ val opts = doOpts(p)
if (!opts.isEmpty)
- plug.processOptions(opts, error)
+ p.processOptions(opts, error)
}
- for {
- opt <- settings.pluginOptions.value
- if !plugs.exists(p => opt.startsWith(p.name + ":"))
- } error("bad option: -P:" + opt)
+ /** Verify no non-existent plugin given with -P */
+ for (opt <- settings.pluginOptions.value ; if plugs forall (p => optList(List(opt), p).isEmpty))
+ error("bad option: -P:" + opt)
plugs
}
- private var pluginsCache: Option[List[Plugin]] = None
-
- def plugins: List[Plugin] = {
- if (pluginsCache.isEmpty)
- pluginsCache = Some(loadPlugins)
- pluginsCache.get
- }
+ lazy val plugins: List[Plugin] = loadPlugins
/** A description of all the plugins that are loaded */
- def pluginDescriptions: String = {
- val messages =
- for (plugin <- roughPluginsList)
- yield plugin.name + " - " + plugin.description
- messages.mkString("\n")
- }
+ def pluginDescriptions: String =
+ roughPluginsList map (x => "%s - %s".format(x.name, x.description)) mkString "\n"
/**
* Extract all phases supplied by plugins and add them to the phasesSet.
* @see phasesSet
*/
- protected def computePluginPhases() {
- val plugPhases = plugins.flatMap(_.components)
- for (pPhase <- plugPhases) {
- phasesSet += pPhase
- }
- }
+ protected def computePluginPhases(): Unit =
+ phasesSet ++= (plugins flatMap (_.components))
/** Summary of the options for all loaded plugins */
- def pluginOptionsHelp: String = {
- val buf = new StringBuffer
- for (plug <- roughPluginsList; help <- plug.optionsHelp) {
- buf append ("Options for plugin " + plug.name + ":\n")
- buf append help
- buf append "\n"
- }
- buf.toString
- }
+ def pluginOptionsHelp: String =
+ (for (plug <- roughPluginsList ; help <- plug.optionsHelp) yield {
+ "Options for plugin %s:\n%s\n".format(plug.name, help)
+ }) mkString
}
diff --git a/src/dotnet-library/scala/io/File.scala b/src/dotnet-library/scala/io/File.scala
deleted file mode 100644
index e57b4fb979..0000000000
--- a/src/dotnet-library/scala/io/File.scala
+++ /dev/null
@@ -1 +0,0 @@
-/* File.scala does not exist for the dotnet target */ \ No newline at end of file
diff --git a/src/dotnet-library/scala/io/Process.scala b/src/dotnet-library/scala/io/Process.scala
deleted file mode 100644
index dda8821199..0000000000
--- a/src/dotnet-library/scala/io/Process.scala
+++ /dev/null
@@ -1 +0,0 @@
-/* Process.scala does not exist for the dotnet target */ \ No newline at end of file
diff --git a/src/dotnet-library/scala/io/Streamable.scala b/src/dotnet-library/scala/io/Streamable.scala
deleted file mode 100644
index ff90ea3bbb..0000000000
--- a/src/dotnet-library/scala/io/Streamable.scala
+++ /dev/null
@@ -1 +0,0 @@
-/* Streamable.scala does not exist for the dotnet target */ \ No newline at end of file
diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
index 3cc84b8d89..c087b58c69 100644
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala
@@ -11,6 +11,7 @@ package nest
import java.io.{File, PrintStream, FileOutputStream, BufferedReader,
InputStreamReader, StringWriter, PrintWriter}
import java.util.StringTokenizer
+import scala.tools.nsc.io.Directory
import scala.actors.Actor._
import scala.actors.TIMEOUT
@@ -66,13 +67,9 @@ trait DirectRunner {
fails += 1
}
}
- logsToDelete.foreach { log =>
- NestUI.verbose("deleting "+log)
- fileManager.deleteRecursive(log)
- }
- outdirsToDelete.foreach { outdir =>
- NestUI.verbose("deleting "+outdir)
- fileManager.deleteRecursive(outdir)
+ for (x <- logsToDelete ::: outdirsToDelete) {
+ NestUI.verbose("deleting "+x)
+ Directory(x).deleteRecursively()
}
(succs, fails)
diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala
index bdce6c8439..238681bfc6 100644
--- a/src/partest/scala/tools/partest/nest/FileManager.scala
+++ b/src/partest/scala/tools/partest/nest/FileManager.scala
@@ -10,6 +10,7 @@ package nest
import java.io.{File, FilenameFilter, IOException, StringWriter}
import java.net.URI
+import scala.tools.nsc.io.Directory
trait FileManager {
@@ -18,12 +19,7 @@ trait FileManager {
if (inx < 0) name else name.substring(0, inx)
}
- def deleteRecursive(dir: File) {
- if (dir.isDirectory) {
- for (file <- dir.list) deleteRecursive(new File(dir, file))
- }
- dir.delete
- }
+ def deleteRecursive(dir: File) { Directory(dir).deleteRecursively() }
/**
* Compares two files using a Java implementation of the GNU diff