summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/ant/Scaladoc.scala12
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala17
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala74
-rw-r--r--src/compiler/scala/tools/nsc/GenericRunnerCommand.scala75
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala55
-rw-r--r--src/compiler/scala/tools/nsc/MainGenericRunner.scala38
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala127
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala7
-rw-r--r--src/compiler/scala/tools/nsc/backend/JavaPlatform.scala1
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala3
-rw-r--r--src/compiler/scala/tools/nsc/io/AbstractFile.scala9
-rw-r--r--src/compiler/scala/tools/nsc/io/File.scala4
-rw-r--r--src/compiler/scala/tools/nsc/io/Path.scala8
-rw-r--r--src/compiler/scala/tools/nsc/io/Streamable.scala8
-rw-r--r--src/compiler/scala/tools/nsc/util/ArgumentsExpander.scala44
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala133
-rw-r--r--src/compiler/scala/tools/util/PathResolver.scala313
-rw-r--r--src/compiler/scala/tools/util/StringOps.scala10
-rw-r--r--src/partest/scala/tools/partest/nest/TestFile.scala11
-rwxr-xr-xtools/pathResolver4
22 files changed, 585 insertions, 374 deletions
diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala
index 31463d620d..03dc99be68 100644
--- a/src/compiler/scala/tools/ant/Scaladoc.scala
+++ b/src/compiler/scala/tools/ant/Scaladoc.scala
@@ -520,18 +520,8 @@ class Scaladoc extends MatchingTask {
docSettings.deprecation.value = deprecation
docSettings.unchecked.value = unchecked
log("Scaladoc params = '" + addParams + "'", Project.MSG_DEBUG)
- var args = docSettings.splitParams(addParams)
-
- while (!args.isEmpty) {
- if (args.head startsWith "-") {
- val args0 = args
- args = docSettings.parseParams(args)
- if (args0 eq args) error("Parameter '" + args.head + "' is not recognised by Scaladoc.")
- }
- else if (args.head == "") args = args.tail
- else error("Parameter '" + args.head + "' does not start with '-'.")
- }
+ docSettings processArgumentString addParams
Pair(docSettings, sourceFiles)
}
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index 06f0f15d9d..bad1cc9d1c 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -14,6 +14,7 @@ import java.security.SecureRandom
import io.{ File, Path, Process, Socket }
import scala.util.control.Exception.catching
+import scala.tools.util.StringOps.splitWhere
/** This class manages sockets for the fsc offline compiler. */
class CompileSocket {
@@ -176,18 +177,10 @@ class CompileSocket {
try { Some(x.toInt) }
catch { case _: NumberFormatException => None }
- def getSocket(serverAdr: String): Socket = {
- 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(serverAdr: String): Socket = (
+ for ((name, portStr) <- splitWhere(serverAdr, _ == ':', true) ; port <- parseInt(portStr)) yield
+ getSocket(name, port)
+ ) getOrElse fatal("Malformed server address: %s; exiting" format serverAdr)
def getSocket(hostName: String, port: Int): Socket =
Socket(hostName, port).opt getOrElse fatal("Unable to establish connection to server %s:%d; exiting".format(hostName, port))
diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala
index 75a3414d75..95a2fff8de 100644
--- a/src/compiler/scala/tools/nsc/CompilerCommand.scala
+++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala
@@ -8,6 +8,8 @@ package scala.tools.nsc
import Settings.Setting
import java.io.IOException
+import scala.collection.mutable.ListBuffer
+import scala.tools.nsc.util.ArgumentsExpander
/** A class representing command line info for scalac */
class CompilerCommand(
@@ -23,31 +25,22 @@ class CompilerCommand(
/** file extensions of files that the compiler can process */
lazy val fileEndings = Properties.fileEndings
- /** Private buffer for accumulating files to compile */
- private var fs: List[String] = List()
-
- /** Public list of files to compile */
- def files: List[String] = fs.reverse
-
/** The name of the command */
val cmdName = "scalac"
private val helpSyntaxColumnWidth: Int =
(settings.settingSet map (_.helpSyntax.length)) max
- private def format(s: String): String = {
- val buf = new StringBuilder(s)
- var i = s.length
- while (i < helpSyntaxColumnWidth) { buf.append(' '); i += 1 }
- buf.toString()
- }
+ private def format(s: String): String =
+ if (s.length >= helpSyntaxColumnWidth) s
+ else s + (" " * (helpSyntaxColumnWidth - s.length))
/** Creates a help message for a subset of options based on cond */
def createUsageMsg(label: String, cond: (Setting) => Boolean): String =
settings.settingSet .
filter(cond) .
map(s => format(s.helpSyntax) + " " + s.helpDescription) .
- mkString("Usage: %s <options> <source files>\n%s options include:\n " .
+ toList.sorted.mkString("Usage: %s <options> <source files>\n%s options include:\n " .
format(cmdName, label), "\n ", "\n")
/** Messages explaining usage and options */
@@ -76,52 +69,19 @@ class CompilerCommand(
case None => ""
}
- /** Whether the command was processed okay */
- var ok = true
-
- /** Process the arguments and update the settings accordingly.
- This method is called only once, during initialization. */
- protected def processArguments() {
- // initialization
- var args = arguments
- def errorAndNotOk(msg: String) = { error(msg) ; ok = false }
-
- // given a @ argument expands it out
- def doExpand(x: String) =
- try { args = util.ArgumentsExpander.expandArg(x) ::: args.tail }
- catch { case ex: IOException => errorAndNotOk(ex.getMessage) }
-
- // true if it's a legit looking source file
- def isSourceFile(x: String) =
- (settings.script.value != "") ||
- (fileEndings exists (x endsWith _))
-
- // given an option for scalac finds out what it is
- def doOption(x: String): Unit = {
- if (interactive)
- return errorAndNotOk("no options can be given in interactive mode")
-
- val argsLeft = settings.parseParams(args)
- if (args != argsLeft) args = argsLeft
- else errorAndNotOk("bad option: '" + x + "'")
- }
-
- // cycle through args until empty or error
- while (!args.isEmpty && ok) args.head match {
- case x if x startsWith "@" => doExpand(x)
- case x if x startsWith "-" => doOption(x)
- case x if isSourceFile(x) => fs = x :: fs ; args = args.tail
- case "" => args = args.tail // quick fix [martin: for what?]
- case x => errorAndNotOk("don't know what to do with " + x)
- }
-
- ok &&= settings.checkDependencies
- }
-
// CompilerCommand needs processArguments called at the end of its constructor,
// as does its subclass GenericRunnerCommand, but it cannot be called twice as it
// accumulates arguments. The fact that it's called from within the constructors
// makes initialization order an obstacle to simplicity.
- if (shouldProcessArguments)
- processArguments()
+ val (ok: Boolean, files: List[String]) =
+ if (shouldProcessArguments) {
+ // expand out @filename to the contents of that filename
+ val expandedArguments = arguments flatMap {
+ case x if x startsWith "@" => ArgumentsExpander expandArg x
+ case x => List(x)
+ }
+
+ settings processArguments expandedArguments
+ }
+ else (true, Nil)
}
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
index bde623b5d7..fa0b5bce9c 100644
--- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
+++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
@@ -24,56 +24,39 @@ extends CompilerCommand(allargs, settings, error, false, false)
override val cmdName = "scala"
val compCmdName = "scalac"
- /** What to run. If it is None, then the interpreter should be started */
- var thingToRun: Option[String] = None
+ /** thingToRun: What to run. If it is None, then the interpreter should be started
+ * arguments: Arguments to pass to the object or script to run
+ *
+ * we can safely process arguments since we passed the superclass shouldProcessArguments=false
+ */
+ val (thingToRun, arguments) = (settings processArguments allargs)._2 match {
+ case Nil => (None, Nil)
+ case hd :: tl => (Some(hd), tl)
+ }
- /** Arguments to pass to the object or script to run */
- var arguments: List[String] = Nil
+ override def usageMsg = """
+%s [ <option> ]... [<torun> <arguments>]
- override protected def processArguments() {
- var args = allargs
+All options to %s are allowed. See %s -help.
- while (!args.isEmpty && ok && args.head.startsWith("-")) {
- val args0 = args
- args = settings parseParams args
- if (args eq args0) {
- error("bad option: '" + args.head + "'")
- ok = false
- }
- }
+<torun>, if present, is an object or script file to run.
+If no <torun> is present, run an interactive shell.
- if (!args.isEmpty) {
- thingToRun = Some(args.head)
- arguments = args.tail
- }
- }
+Option -howtorun allows explicitly specifying how to run <torun>:
+ script: it is a script file
+ object: it is an object name
+ guess: (the default) try to guess
- // we can safely call processArguments since we passed the superclass shouldProcessArguments=false
- processArguments()
+Option -i requests that a file be pre-loaded. It is only
+meaningful for interactive shells.
- override def usageMsg = {
- cmdName + " [ <option> ]... [<torun> <arguments>]\n" +
- "\n" +
- "All options to "+compCmdName+" are allowed. See "+compCmdName+" -help.\n" +
- "\n" +
- "<torun>, if present, is an object or script file to run.\n" +
- "If no <torun> is present, run an interactive shell.\n" +
- "\n" +
- "Option -howtorun allows explicitly specifying how to run <torun>:\n" +
- " script: it is a script file\n" +
- " object: it is an object name\n" +
- " guess: (the default) try to guess\n" +
- "\n" +
- "Option -i requests that a file be pre-loaded. It is only\n" +
- "meaningful for interactive shells.\n" +
- "\n" +
- "Option -e requests that its argument be executed as Scala code.\n" +
- "\n" +
- "Option -savecompiled requests that the compiled script be saved\n" +
- "for future use.\n" +
- "\n" +
- "Option -nocompdaemon requests that the fsc offline compiler not be used.\n" +
- "\n" +
- "Option -Dproperty=value sets a Java system property.\n"
- }
+Option -e requests that its argument be executed as Scala code.
+
+Option -savecompiled requests that the compiled script be saved
+for future use.
+
+Option -nocompdaemon requests that the fsc offline compiler not be used.
+
+Option -Dproperty=value sets a Java system property.
+""".format(cmdName, compCmdName, compCmdName)
}
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 49ce29a7ff..8d460101c9 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -503,8 +503,8 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
private[nsc] val powerMkImports = List(
- "mkContext", "mkTree", "mkTrees", "mkAlias", "mkSourceFile", "mkUnit", "mkType", "mkTypedTree", "mkTypedTrees",
- "treeWrapper"
+ "mkContext", "mkTree", "mkTrees", "mkAlias", "mkSourceFile", "mkUnit", "mkType", "mkTypedTree", "mkTypedTrees"
+ // , "treeWrapper"
)
/** Compile an nsc SourceFile. Returns true if there are
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index d0c2747ae1..be6881613a 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -6,14 +6,14 @@
package scala.tools.nsc
-import java.io.{ BufferedReader, File, FileReader, PrintWriter }
+import java.io.{ BufferedReader, FileReader, PrintWriter }
import java.io.IOException
import scala.tools.nsc.{ InterpreterResults => IR }
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
import interpreter._
-import io.{ Process }
+import io.{ File, Process }
// Classes to wrap up interpreter commands and their results
// You can add new commands by adding entries to val commands
@@ -91,13 +91,13 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
var addedClasspath: List[String] = Nil
/** A reverse list of commands to replay if the user requests a :replay */
- var replayCommandsRev: List[String] = Nil
+ var replayCommandStack: List[String] = Nil
/** A list of commands to replay if the user requests a :replay */
- def replayCommands = replayCommandsRev.reverse
+ def replayCommands = replayCommandStack.reverse
/** Record a command for replay should the user request a :replay */
- def addReplay(cmd: String) = replayCommandsRev = cmd :: replayCommandsRev
+ def addReplay(cmd: String) = replayCommandStack ::= cmd
/** Close the interpreter and set the var to <code>null</code>. */
def closeInterpreter() {
@@ -246,22 +246,24 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
/** interpret all lines from a specified file */
def interpretAllFrom(filename: String) {
- val fileIn =
- try { new FileReader(filename) }
- catch { case _:IOException => return out.println("Error opening file: " + filename) }
+ val fileIn = File(filename)
+ if (!fileIn.exists)
+ return out.println("Error opening file: " + filename)
val oldIn = in
- val oldReplay = replayCommandsRev
+ val oldReplay = replayCommandStack
+
try {
- val inFile = new BufferedReader(fileIn)
- in = new SimpleReader(inFile, out, false)
- out.println("Loading " + filename + "...")
- out.flush
- repl
- } finally {
+ fileIn applyReader { reader =>
+ in = new SimpleReader(reader, out, false)
+ out.println("Loading " + filename + "...")
+ out.flush
+ repl
+ }
+ }
+ finally {
in = oldIn
- replayCommandsRev = oldReplay
- fileIn.close
+ replayCommandStack = oldReplay
}
}
@@ -292,8 +294,8 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
}
def withFile(filename: String)(action: String => Unit) {
- if (! new File(filename).exists) out.println("That file does not exist")
- else action(filename)
+ if (File(filename).exists) action(filename)
+ else out.println("That file does not exist")
}
def load(arg: String) = {
@@ -307,14 +309,13 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
def addJar(arg: String): Unit = {
- val f = new java.io.File(arg)
- if (!f.exists) {
- out.println("The file '" + f + "' doesn't seem to exist.")
- return
+ val f = File(arg).normalize
+ if (f.exists) {
+ addedClasspath :::= List(f.path)
+ println("Added " + f.path + " to your classpath.")
+ replay()
}
- addedClasspath = addedClasspath ::: List(f.getCanonicalPath)
- println("Added " + f.getCanonicalPath + " to your classpath.")
- replay()
+ else out.println("The file '" + f + "' doesn't seem to exist.")
}
/** This isn't going to win any efficiency awards, but it's only
@@ -531,7 +532,7 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {
for (filename <- settings.loadfiles.value) {
val cmd = ":load " + filename
command(cmd)
- replayCommandsRev = cmd :: replayCommandsRev
+ addReplay(cmd)
out.println()
}
case _ =>
diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/compiler/scala/tools/nsc/MainGenericRunner.scala
index cd16ef8c8b..834f4d1742 100644
--- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala
+++ b/src/compiler/scala/tools/nsc/MainGenericRunner.scala
@@ -11,6 +11,7 @@ import java.io.{ File, IOException }
import java.lang.{ClassNotFoundException, NoSuchMethodException}
import java.lang.reflect.InvocationTargetException
import java.net.{ URL, MalformedURLException }
+import scala.tools.util.PathResolver
import util.{ ClassPath, ScalaClassLoader }
import File.pathSeparator
@@ -21,37 +22,6 @@ import Properties.{ versionString, copyrightString }
* or interactive entry.
*/
object MainGenericRunner {
- /** Append jars found in ${scala.home}/lib to
- * a specified classpath. Also append "." if the
- * input classpath is empty; otherwise do not.
- *
- * @param classpath
- * @return the new classpath
- */
- private def addClasspathExtras(classpath: String): String = {
- val scalaHome = Properties.scalaHome
-
- def listDir(name: String): List[File] = {
- val libdir = new File(new File(scalaHome), name)
- if (!libdir.exists || libdir.isFile) Nil else libdir.listFiles.toList
- }
- lazy val jarsInLib = listDir("lib") filter (_.getName endsWith ".jar")
- lazy val dirsInClasses = listDir("classes") filter (_.isDirectory)
- val cpScala =
- if (scalaHome == null) {
- // this is to make the interpreter work when running without the scala script
- // (e.g. from eclipse). Before, "java.class.path" was added to the user classpath
- // in Settings; this was changed to match the behavior of Sun's javac.
- val javacp = System.getProperty("java.class.path")
- if (javacp == null) Nil
- else ClassPath.expandPath(javacp)
- }
- else (jarsInLib ::: dirsInClasses) map (_.toString)
-
- // either prepend existing classpath or append "."
- (if (classpath == "") cpScala ::: List(".") else classpath :: cpScala) mkString pathSeparator
- }
-
def main(args: Array[String]) {
def errorFn(str: String) = Console println str
@@ -62,7 +32,8 @@ object MainGenericRunner {
if (!command.ok)
return errorFn("%s\n%s".format(command.usageMsg, sampleCompiler.pluginOptionsHelp))
- settings.classpath.value = addClasspathExtras(settings.classpath.value)
+ // append the jars in ${scala.home}/lib to the classpath, as well as "." if none was given.
+ settings appendToClasspath PathResolver.basicScalaClassPath(settings.classpath.value == "")
settings.defines.applyToCurrentJVM
if (settings.version.value)
@@ -107,11 +78,12 @@ object MainGenericRunner {
val url = specToURL(spec); if !url.isEmpty
) yield url.get
- val classpath: List[URL] =
+ val classpath: List[URL] = (
paths(settings.bootclasspath.value) :::
paths(settings.classpath.value) :::
jars(settings.extdirs.value) :::
urls(settings.Xcodebase.value)
+ ).distinct
def createLoop(): InterpreterLoop = {
val loop = new InterpreterLoop
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 8c921b98a1..a2176a48ea 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -12,10 +12,38 @@ import io.AbstractFile
import util.SourceFile
import Settings._
import annotation.elidable
+import scala.tools.util.PathResolver
class Settings(errorFn: String => Unit) extends ScalacSettings {
def this() = this(Console.println)
+ /** Iterates over the arguments applying them to settings where applicable.
+ * Then verifies setting dependencies are met.
+ *
+ * Returns (success, List of unprocessed arguments)
+ */
+ def processArguments(arguments: List[String]): (Boolean, List[String]) = {
+ var args = arguments
+
+ while (args.nonEmpty) {
+ if (args.head startsWith "-") {
+ val args0 = args
+ args = this parseParams args
+ if (args eq args0) {
+ errorFn("bad option: '" + args.head + "'")
+ return (false, args)
+ }
+ }
+ else if (args.head == "") { // discard empties, sometimes they appear because of ant or etc.
+ args = args.tail
+ }
+ else return (checkDependencies, args)
+ }
+
+ (checkDependencies, args)
+ }
+ def processArgumentString(params: String) = processArguments(splitParams(params))
+
// optionizes a system property
private def syspropopt(name: String): Option[String] = Option(System.getProperty(name))
private def sysenvopt(name: String): Option[String] = Option(System.getenv(name))
@@ -84,7 +112,6 @@ class Settings(errorFn: String => Unit) extends ScalacSettings {
true
}
-
/** A list pairing source directories with their output directory.
* This option is not available on the command line, but can be set by
* other tools (IDEs especially). The command line specifies a single
@@ -93,69 +120,12 @@ class Settings(errorFn: String => Unit) extends ScalacSettings {
*/
lazy val outputDirs = new OutputDirs
- /**
- * Split command line parameters by space, properly process quoted parameter
+ /** Split the given line into parameters.
*/
- def splitParams(line: String): List[String] = {
- def parse(from: Int, i: Int, args: List[String]): List[String] = {
- if (i < line.length) {
- line.charAt(i) match {
- case ' ' =>
- val args1 = fetchArg(from, i) :: args
- val j = skipS(i + 1)
- if (j >= 0) {
- parse(j, j, args1)
- } else args1
- case '"' =>
- val j = skipTillQuote(i + 1)
- if (j > 0) {
- parse(from, j + 1, args)
- } else {
- errorFn("Parameters '" + line + "' with unmatched quote at " + i + ".")
- Nil
- }
- case _ => parse(from, i + 1, args)
- }
- } else { // done
- if (i > from) {
- fetchArg(from, i) :: args
- } else args
- }
- }
-
- def fetchArg(from: Int, until: Int) = {
- if (line.charAt(from) == '"') {
- line.substring(from + 1, until - 1)
- } else {
- line.substring(from, until)
- }
- }
-
- def skipTillQuote(i: Int): Int = {
- if (i < line.length) {
- line.charAt(i) match {
- case '"' => i
- case _ => skipTillQuote(i + 1)
- }
- } else -1
- }
-
- def skipS(i: Int): Int = {
- if (i < line.length) {
- line.charAt(i) match {
- case ' ' => skipS(i + 1)
- case _ => i
- }
- } else -1
- }
-
- // begin split
- val j = skipS(0)
- if (j >= 0) {
- parse(j, j, Nil).reverse
- } else Nil
- }
+ def splitParams(line: String) = PathResolver.splitParams(line, errorFn)
+ /** Returns any unprocessed arguments.
+ */
def parseParams(args: List[String]): List[String] = {
// verify command exists and call setter
def tryToSetIfExists(
@@ -234,6 +204,8 @@ class Settings(errorFn: String => Unit) extends ScalacSettings {
doArgs(args)
}
+ def parseParamString(params: String) = parseParams(splitParams(params))
+
// checks both name and any available abbreviations
def lookupSetting(cmd: String): Option[Setting] =
settingSet.find(x => x.name == cmd || (x.abbreviations contains cmd))
@@ -456,9 +428,9 @@ object Settings {
/** Will be called after this Setting is set, for any cases where the
* Setting wants to perform extra work. */
- private var _postSetHook: () => Unit = () => ()
- def postSetHook(): Unit = _postSetHook()
- def withPostSetHook(f: () => Unit): this.type = { _postSetHook = f ; this }
+ private var _postSetHook: this.type => Unit = (x: this.type) => ()
+ def postSetHook(): Unit = _postSetHook(this)
+ def withPostSetHook(f: this.type => Unit): this.type = { _postSetHook = f ; this }
/** After correct Setting has been selected, tryToSet is called with the
* remainder of the command line. It consumes any applicable arguments and
@@ -805,6 +777,8 @@ object Settings {
trait ScalacSettings {
self: Settings =>
+ import scala.tools.util.PathResolver
+ import PathResolver.{ Defaults, Environment }
import collection.immutable.TreeSet
/** A list of all settings */
@@ -818,7 +792,15 @@ trait ScalacSettings {
* Temporary Settings
*/
val suppressVTWarn = BooleanSetting ("-Ysuppress-vt-typer-warnings", "Suppress warnings from the typer when testing the virtual class encoding, NOT FOR FINAL!")
- def appendToClasspath(entry: String) = classpath.value += (pathSeparator + entry)
+ def appendToClasspath(entry: String) = {
+ val oldClasspath = classpath.value
+ classpath.value =
+ if (classpath.value == "") entry
+ else classpath.value + pathSeparator + entry
+
+ if (Ylogcp.value)
+ Console.println("Updated classpath from '%s' to '%s'".format(oldClasspath, classpath.value))
+ }
/**
* Standard settings
@@ -826,7 +808,9 @@ trait ScalacSettings {
// argfiles is only for the help message
val argfiles = BooleanSetting ("@<file>", "A text file containing compiler arguments (options and source files)")
val bootclasspath = StringSetting ("-bootclasspath", "path", "Override location of bootstrap class files", bootclasspathDefault)
- val classpath = StringSetting ("-classpath", "path", "Specify where to find user class files", classpathDefault).withAbbreviation("-cp")
+ val classpath = StringSetting ("-classpath", "path", "Specify where to find user class files", classpathDefault) .
+ withAbbreviation("-cp") .
+ withPostSetHook(self => if (Ylogcp.value) Console.println("Updated classpath to '%s'".format(self.value)))
val outdir = OutputSetting (outputDirs, ".")
val dependenciesFile = StringSetting ("-dependencyfile", "file", "Specify the file in which dependencies are tracked", ".scala_dependencies")
val deprecation = BooleanSetting ("-deprecation", "Output source locations where deprecated APIs are used")
@@ -839,7 +823,7 @@ trait ScalacSettings {
withHelpSyntax("-make:<strategy>")
val nowarnings = BooleanSetting ("-nowarn", "Generate no warnings")
val XO = BooleanSetting ("-optimise", "Generates faster bytecode by applying optimisations to the program").withAbbreviation("-optimize") .
- withPostSetHook(() => List(inline, Xcloselim, Xdce) foreach (_.value = true))
+ withPostSetHook(_ => List(inline, Xcloselim, Xdce) foreach (_.value = true))
val printLate = BooleanSetting ("-print", "Print program with all Scala-specific features removed")
val sourcepath = StringSetting ("-sourcepath", "path", "Specify where to find input source files", "")
val target = ChoiceSetting ("-target", "Specify for which target object files should be built", List("jvm-1.5", "msil"), "jvm-1.5")
@@ -848,6 +832,10 @@ trait ScalacSettings {
val verbose = BooleanSetting ("-verbose", "Output messages about what the compiler is doing")
val version = BooleanSetting ("-version", "Print product version and exit")
+ /** New to classpaths */
+ val javabootclasspath = StringSetting ("-javabootclasspath", "path", "Override java boot classpath.", Environment.javaBootClassPath)
+ val javaextdirs = StringSetting ("-javaextdirs", "path", "Override java extdirs classpath.", Environment.javaExtDirs)
+
/**
* -X "Advanced" settings
*/
@@ -929,6 +917,7 @@ trait ScalacSettings {
val Ypmatnaive = BooleanSetting ("-Ypmat-naive", "Desugar matches as naively as possible..")
val Ytailrec = BooleanSetting ("-Ytailrecommend", "Alert methods which would be tail-recursive if private or final.")
val Yjenkins = BooleanSetting ("-Yjenkins-hashCodes", "Use jenkins hash algorithm for case class generated hashCodes.")
+ val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.")
// Warnings
val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics")
@@ -937,7 +926,7 @@ trait ScalacSettings {
val YwarnShadow = BooleanSetting ("-Ywarn-shadowing", "Emit warnings about possible variable shadowing.")
val YwarnCatches = BooleanSetting ("-Ywarn-catches", "Emit warnings about catch blocks which catch everything.")
val Xwarnings = BooleanSetting ("-Xstrict-warnings", "Emit warnings about lots of things.") .
- withPostSetHook(() =>
+ withPostSetHook(_ =>
List(YwarnShadow, YwarnCatches, Xwarndeadcode, Xwarninit) foreach (_.value = true)
)
/**
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
index 9c34739a15..31529f83e6 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
@@ -11,6 +11,7 @@ import collection.mutable.Map
import xml.{ EntityRef, Text }
import xml.XML.{ xmlns }
import symtab.Flags.MUTABLE
+import scala.tools.util.StringOps.splitWhere
/** This class builds instance of <code>Tree</code> that represent XML.
*
@@ -160,9 +161,9 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean)
}
/** Returns (Some(prefix) | None, rest) based on position of ':' */
- def splitPrefix(name: String): (Option[String], String) = (name indexOf ':') match {
- case -1 => (None, name)
- case i => (Some(name take i), name drop (i + 1))
+ def splitPrefix(name: String): (Option[String], String) = splitWhere(name, _ == ':', true) match {
+ case Some((pre, rest)) => (Some(pre), rest)
+ case _ => (None, name)
}
/** Various node constructions. */
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
index 02199759b9..0d2f9a13d1 100644
--- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
@@ -9,6 +9,7 @@ package backend
import io.AbstractFile
import util.JavaClassPath
import util.ClassPath.{ JavaContext, DefaultJavaContext }
+import scala.tools.util.PathResolver
trait JavaPlatform extends Platform[AbstractFile] {
import global._
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 3e697dd923..c3430f8006 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1643,9 +1643,11 @@ abstract class GenICode extends SubComponent {
abstract class Cleanup;
case class MonitorRelease(m: Local) extends Cleanup {
+ override def hashCode = m.hashCode
override def equals(other: Any) = m == other;
}
case class Finalizer(f: Tree) extends Cleanup {
+ override def hashCode = f.hashCode
override def equals(other: Any) = f == other;
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
index bca2e18e39..9d604ab8b3 100644
--- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala
@@ -8,6 +8,7 @@ package scala.tools.nsc
package interpreter
import java.io.{ BufferedReader, PrintWriter }
+import io.{ Path, File, Directory }
/** Reads using standard JDK API */
class SimpleReader(
@@ -16,7 +17,9 @@ class SimpleReader(
val interactive: Boolean)
extends InteractiveReader {
def this() = this(Console.in, new PrintWriter(Console.out), true)
+ def this(in: File, out: PrintWriter, interactive: Boolean) = this(in.bufferedReader(), out, interactive)
+ def close() = in.close()
def readOneLine(prompt: String): String = {
if (interactive) {
out.print(prompt)
diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
index 31073a0799..15a05aa3fc 100644
--- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala
+++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala
@@ -20,8 +20,6 @@ import scala.collection.mutable.ArrayBuffer
*/
object AbstractFile
{
- def isJarOrZip(f: Path) = cond(f.extension) { case "zip" | "jar" => true }
-
/** Returns "getFile(new File(path))". */
def getFile(path: String): AbstractFile = getFile(Path(path))
def getFile(path: Path): AbstractFile = getFile(path.toFile)
@@ -46,7 +44,7 @@ object AbstractFile
*/
def getDirectory(file: File): AbstractFile =
if (file.isDirectory) new PlainFile(file)
- else if (file.isFile && isJarOrZip(file)) ZipArchive fromFile file
+ else if (file.isFile && Path.isJarOrZip(file)) ZipArchive fromFile file
else null
/**
@@ -58,7 +56,7 @@ object AbstractFile
* @return ...
*/
def getURL(url: URL): AbstractFile =
- Option(url) partialMap { case url: URL if isJarOrZip(url.getPath) => ZipArchive fromURL url } orNull
+ Option(url) partialMap { case url: URL if Path.isJarOrZip(url.getPath) => ZipArchive fromURL url } orNull
}
/**
@@ -93,6 +91,9 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] {
/** Returns the path of this abstract file. */
def path: String
+ /** Checks extension case insensitively. */
+ def hasExtension(other: String) = Path(path) hasExtension other
+
/** The absolute file, if this is a relative file. */
def absolute: AbstractFile
diff --git a/src/compiler/scala/tools/nsc/io/File.scala b/src/compiler/scala/tools/nsc/io/File.scala
index 7f5f535da4..4f27f4f8a5 100644
--- a/src/compiler/scala/tools/nsc/io/File.scala
+++ b/src/compiler/scala/tools/nsc/io/File.scala
@@ -14,13 +14,14 @@ package io
import java.io.{
FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
BufferedInputStream, BufferedOutputStream, IOException, File => JFile }
-import java.nio.channels.FileChannel
+import java.nio.channels.{ Channel, FileChannel }
import collection.Traversable
import scala.io.Codec
object File
{
def pathSeparator = JFile.pathSeparator
+ def separator = JFile.separator
def apply(path: Path)(implicit codec: Codec = null) =
if (codec != null) new File(path.jfile)(codec)
@@ -30,7 +31,6 @@ object 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 => }
diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala
index b3f7ea5ab5..39fba8bcb2 100644
--- a/src/compiler/scala/tools/nsc/io/Path.scala
+++ b/src/compiler/scala/tools/nsc/io/Path.scala
@@ -30,6 +30,8 @@ import scala.util.Random.nextASCIIString
object Path
{
+ def isJarOrZip(f: Path) = (f hasExtension "zip") || (f hasExtension "jar")
+
// 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)
@@ -137,6 +139,12 @@ class Path private[io] (val jfile: JFile)
case -1 => ""
case idx => name drop (idx + 1)
}
+ // compares against extension in a CASE INSENSITIVE way.
+ def hasExtension(other: String) = extension.toLowerCase == other.toLowerCase
+
+ // conditionally execute
+ def ifFile[T](f: File => T): Option[T] = if (isFile) Some(f(toFile)) else None
+ def ifDirectory[T](f: Directory => T): Option[T] = if (isDirectory) Some(f(toDirectory)) else None
// Boolean tests
def canRead = jfile.canRead()
diff --git a/src/compiler/scala/tools/nsc/io/Streamable.scala b/src/compiler/scala/tools/nsc/io/Streamable.scala
index 4dc0745534..a55028bc25 100644
--- a/src/compiler/scala/tools/nsc/io/Streamable.scala
+++ b/src/compiler/scala/tools/nsc/io/Streamable.scala
@@ -101,6 +101,14 @@ object Streamable
*/
def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec))
+ /** Creates an InputStream and applies the closure, automatically closing it on completion.
+ */
+ def applyReader[T](f: BufferedReader => T): T = {
+ val in = bufferedReader()
+ try f(in)
+ finally in.close()
+ }
+
/** Convenience function to import entire file into a String.
*/
def slurp(codec: Codec = getCodec()) = chars(codec).mkString
diff --git a/src/compiler/scala/tools/nsc/util/ArgumentsExpander.scala b/src/compiler/scala/tools/nsc/util/ArgumentsExpander.scala
index 2d8fc8c502..fb90f1d3d1 100644
--- a/src/compiler/scala/tools/nsc/util/ArgumentsExpander.scala
+++ b/src/compiler/scala/tools/nsc/util/ArgumentsExpander.scala
@@ -1,43 +1,39 @@
package scala.tools.nsc
package util
-import java.io.{FileReader, BufferedReader, StreamTokenizer, FileNotFoundException}
-import scala.tools.nsc.io.AbstractFile
-import scala.collection.mutable.ListBuffer
+import java.io.{ StreamTokenizer, FileNotFoundException }
+import scala.tools.nsc.io.File
/**
* Expands all arguments starting with @ to the contents of the
* file named like each argument.
*/
object ArgumentsExpander {
-
- def expandArg(arg: String): List[String] =
- expandFromFile(arg.substring(1))
+ def expandArg(arg: String): List[String] = {
+ require(arg.nonEmpty && arg.head == '@')
+ expandFromFile(arg drop 1)
+ }
/*
* Extracts all the arguments in a specified file.
* Throws FileNotFoundException if the file does not exist.
*/
- private def expandFromFile(fileName: String): List[String] = {
- val f = AbstractFile.getFile(fileName)
- if (f eq null) throw new FileNotFoundException(
- "argument file "+ fileName +" could not be found")
-
- val in = new BufferedReader(new FileReader(f.file))
+ def expandFromFile(fileName: String): List[String] = {
+ val file = File(fileName)
+ if (!file.exists)
+ throw new FileNotFoundException("argument file %s could not be found" format fileName)
- val tokenizer = new StreamTokenizer( in )
- tokenizer.resetSyntax
- tokenizer.wordChars(' ', 255)
- tokenizer.whitespaceChars(0, ' ')
- tokenizer.commentChar('#')
- tokenizer.quoteChar('"')
- tokenizer.quoteChar('\'')
+ file applyReader { in =>
+ val tokenizer = new StreamTokenizer( in )
+ tokenizer.resetSyntax
+ tokenizer.wordChars(' ', 255)
+ tokenizer.whitespaceChars(0, ' ')
+ tokenizer.commentChar('#')
+ tokenizer.quoteChar('"')
+ tokenizer.quoteChar('\'')
- val ts = new ListBuffer[String]
- while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
- ts += tokenizer.sval
+ def getToken = if (tokenizer.nextToken == StreamTokenizer.TT_EOF) None else Some(tokenizer.sval)
+ Iterator continually getToken takeWhile (_.isDefined) map (_.get) toList
}
- in.close()
- ts.toList
}
}
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index b00e4c0950..6be887396a 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -8,13 +8,12 @@
package scala.tools.nsc
package util
-import java.io.File
+import java.io.{ File => JFile }
import java.net.URL
-import java.util.StringTokenizer
-import scala.util.Sorting
import scala.collection.mutable.{ListBuffer, ArrayBuffer, HashSet => MutHashSet}
-import scala.tools.nsc.io.AbstractFile
+import io.{ File, Directory, Path, AbstractFile }
+import scala.tools.util.StringOps.splitWhere
/** <p>
* This module provides star expansion of '-classpath' option arguments, behaves the same as
@@ -26,26 +25,21 @@ import scala.tools.nsc.io.AbstractFile
object ClassPath {
/** Expand single path entry */
private def expandS(pattern: String): List[String] = {
- def isJar(name: String) = name.toLowerCase endsWith ".jar"
+ val wildSuffix = File.separator + "*"
/** Get all jars in directory */
- def lsJars(f: File, filt: String => Boolean = _ => true) = {
- val list = f.listFiles()
- if (list eq null) Nil
- else list.filter(f => f.isFile() && filt(f.getName) && isJar(f.getName())).map(_.getPath()).toList
- }
-
- val suffix = File.separator + "*"
+ def lsJars(dir: Directory, filt: String => Boolean = _ => true) =
+ dir.files partialMap { case f if filt(f.name) && (f hasExtension "jar") => f.path } toList
def basedir(s: String) =
if (s contains File.separator) s.substring(0, s.lastIndexOf(File.separator))
else "."
- if (pattern == "*") lsJars(new File("."))
- else if (pattern endsWith suffix) lsJars(new File(pattern dropRight 2))
+ if (pattern == "*") lsJars(Directory("."))
+ else if (pattern endsWith wildSuffix) lsJars(Directory(pattern dropRight 2))
else if (pattern contains '*') {
val regexp = ("^%s$" format pattern.replaceAll("""\*""", """.*""")).r
- lsJars(new File(basedir(pattern)), regexp findFirstIn _ isDefined)
+ lsJars(Directory(pattern).parent, regexp findFirstIn _ isDefined)
}
else List(pattern)
}
@@ -56,9 +50,21 @@ object ClassPath {
/** Expand path and possibly expanding stars */
def expandPath(path: String, expandStar: Boolean = true): List[String] =
- if (expandStar) splitPath(path).flatMap(expandS(_))
+ if (expandStar) splitPath(path) flatMap expandS
else splitPath(path)
+ /** Expand dir out to contents */
+ def expandDir(extdir: String): List[String] = {
+ for {
+ dir <- Option(AbstractFile getDirectory extdir).toList
+ file <- dir
+ if file.isDirectory || (file hasExtension "jar") || (file hasExtension "zip")
+ }
+ yield {
+ (dir.sfile / file.name).path
+ }
+ }
+
/** A useful name filter. */
def isTraitImplementation(name: String) = name endsWith "$class.class"
@@ -151,32 +157,27 @@ abstract class ClassPath[T] {
def validSourceFile(name: String) = validSourceExtensions exists (name endsWith _)
def validSourceExtensions = List(".scala", ".java")
- /** Utility */
- protected final def dropExtension(name: String) = name take (name.lastIndexOf('.') - 1)
-
/**
* Find a ClassRep given a class name of the form "package.subpackage.ClassName".
* Does not support nested classes on .NET
*/
- def findClass(name: String): Option[AnyClassRep] = {
- val i = name.indexOf('.')
- if (i < 0) {
- classes.find(_.name == name)
- } else {
- val pkg = name take i
- val rest = name drop (i + 1)
- (packages find (_.name == pkg) flatMap (_ findClass rest)) map {
- case x: ClassRep => x
- case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name))
- }
+ def findClass(name: String): Option[AnyClassRep] =
+ splitWhere(name, _ == '.', true) match {
+ case Some((pkg, rest)) =>
+ val rep = packages find (_.name == pkg) flatMap (_ findClass rest)
+ rep map {
+ case x: ClassRep => x
+ case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name))
+ }
+ case _ =>
+ classes find (_.name == name)
}
- }
- def findSourceFile(name: String): Option[AbstractFile] = {
+
+ def findSourceFile(name: String): Option[AbstractFile] =
findClass(name) match {
case Some(ClassRep(Some(x: AbstractFile), _)) => Some(x)
case _ => None
}
- }
}
trait AbstractFileClassPath extends ClassPath[AbstractFile] {
@@ -235,7 +236,7 @@ abstract class MergedClassPath[T] extends ClassPath[T] {
val cls = new ListBuffer[AnyClassRep]
for (e <- entries; c <- e.classes) {
val name = c.name
- val idx = cls.indexWhere(cl => cl.name == name)
+ val idx = cls.indexWhere(_.name == name)
if (idx >= 0) {
val existing = cls(idx)
if (existing.binary.isEmpty && c.binary.isDefined)
@@ -253,7 +254,7 @@ abstract class MergedClassPath[T] extends ClassPath[T] {
val pkg = new ListBuffer[ClassPath[T]]
for (e <- entries; p <- e.packages) {
val name = p.name
- val idx = pkg.indexWhere(pk => pk.name == name)
+ val idx = pkg.indexWhere(_.name == name)
if (idx >= 0) {
pkg(idx) = addPackage(pkg(idx), p)
} else {
@@ -263,17 +264,14 @@ abstract class MergedClassPath[T] extends ClassPath[T] {
pkg.toList
}
- lazy val sourcepaths: List[AbstractFile] = entries.flatMap(_.sourcepaths)
+ lazy val sourcepaths: List[AbstractFile] = entries flatMap (_.sourcepaths)
- private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = {
- def expand = to match {
- case cp: MergedClassPath[_] => cp.entries
- case _ => List(to)
- }
- newMergedClassPath(expand :+ pkg)
- }
+ private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = newMergedClassPath(to match {
+ case cp: MergedClassPath[_] => cp.entries :+ pkg
+ case _ => List(to, pkg)
+ })
- private def newMergedClassPath(entrs: List[ClassPath[T]]) =
+ private def newMergedClassPath(entrs: List[ClassPath[T]]): MergedClassPath[T] =
new MergedClassPath[T] {
protected val entries = entrs
val context = outer.context
@@ -301,47 +299,32 @@ class JavaClassPath(
import ClassPath._
val etr = new ListBuffer[ClassPath[AbstractFile]]
- def addFilesInPath(path: String, expand: Boolean, ctr: AbstractFile => ClassPath[AbstractFile] = x => createDirectoryPath(x)) {
- for (fileName <- expandPath(path, expandStar = expand)) {
- val file = AbstractFile.getDirectory(fileName)
- if (file ne null) etr += ctr(file)
- }
- }
+ def addFilesInPath(path: String, expand: Boolean, ctr: AbstractFile => ClassPath[AbstractFile]): Unit =
+ for (fileName <- expandPath(path, expand) ; file <- Option(AbstractFile getDirectory fileName)) yield
+ etr += ctr(file)
+
+ def addContentsOfDir(path: String): Unit =
+ for (name <- expandDir(path) ; archive <- Option(AbstractFile getDirectory name))
+ etr += createDirectoryPath(archive)
+
+ def addContentsOfURL(spec: String): Unit =
+ for (url <- specToURL(spec) ; archive <- Option(AbstractFile getURL url))
+ etr += createDirectoryPath(archive)
// 1. Boot classpath
- addFilesInPath(boot, false)
+ addFilesInPath(boot, false, createDirectoryPath)
// 2. Ext classpath
- for (fileName <- expandPath(ext, expandStar = false)) {
- val dir = AbstractFile.getDirectory(fileName)
- if (dir ne null) {
- for (file <- dir) {
- val name = file.name.toLowerCase
- if (name.endsWith(".jar") || name.endsWith(".zip") || file.isDirectory) {
- val archive = AbstractFile.getDirectory(new File(dir.file, name))
- if (archive ne null) etr += createDirectoryPath(archive)
- }
- }
- }
- }
+ addContentsOfDir(ext)
// 3. User classpath
- addFilesInPath(user, true)
+ addFilesInPath(user, true, createDirectoryPath)
// 4. Codebase entries (URLs)
- {
- for {
- spec <- Xcodebase.trim split " "
- url <- specToURL(spec)
- archive <- Option(AbstractFile getURL url)
- } {
- etr += createDirectoryPath(archive)
- }
- }
+ Xcodebase.trim split " " foreach addContentsOfURL
// 5. Source path
- if (source != "")
- addFilesInPath(source, false, x => createSourcePath(x))
+ addFilesInPath(source, false, createSourcePath)
etr.toList
}
diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala
new file mode 100644
index 0000000000..3aca9c3ee0
--- /dev/null
+++ b/src/compiler/scala/tools/util/PathResolver.scala
@@ -0,0 +1,313 @@
+/* NSC -- new Scala compiler
+ * Copyright 2006-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package util
+
+import java.net.{ URL, MalformedURLException }
+import nsc.{ Settings }
+import nsc.util.{ ClassPath, JavaClassPath, ScalaClassLoader }
+import nsc.io.{ File, Directory, Path }
+import ClassPath.DefaultJavaContext
+import File.{ pathSeparator }
+import PartialFunction.condOpt
+
+// Mostly based on the specification at:
+// https://lampsvn.epfl.ch/trac/scala/wiki/Classpath
+//
+
+object PathResolver {
+ private def propOrElse(name: String, alt: String) = System.getProperty(name, alt)
+ private def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt
+
+ private def fileOpt(f: Path): Option[String] = f ifFile (_.path)
+ private def dirOpt(d: Path): Option[String] = d ifDirectory (_.path)
+ private def expandToPath(p: Path) = joincp(ClassPath.expandPath(p.path, true))
+ private def expandToContents(p: Path) = joincp(ClassPath.expandDir(p.path))
+ private def joincp(xs: Seq[String]): String = xs filterNot (_ == "") mkString pathSeparator
+ private def splitcp(cp: String): Seq[String] = cp split pathSeparator filterNot (_ == "") toSeq
+
+ /** pretty print class path */
+ def ppcp(s: String) = splitcp(s) match {
+ case Nil => ""
+ case Seq(x) => x
+ case xs => xs map ("\n" + _) mkString
+ }
+
+ /** Values found solely by inspecting environment or property variables.
+ */
+ object Environment {
+ private def searchForBootClasspath = {
+ import scala.collection.JavaConversions._
+ System.getProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse ""
+ }
+
+ def classPathEnv = envOrElse("CLASSPATH", "")
+ def toolPathEnv = envOrElse("TOOL_CLASSPATH", "")
+ def classPathProp = propOrElse("java.class.path", "")
+ def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath)
+ def javaExtDirs = propOrElse("java.ext.dirs", "")
+ def userHome = propOrElse("user.home", "")
+ def scalaHome = propOrElse("scala.home", "")
+
+ def classPath = List(classPathProp, classPathEnv) find (_ != "") getOrElse "."
+
+ override def toString = """
+ |object Environment {
+ | classPathEnv = %s
+ | toolPathEnv = %s
+ | classPathProp = %s
+ | javaBootClassPath = %s
+ | javaExtDirs = %s
+ | userHome = %s
+ | scalaHome = %s
+ |}""".trim.stripMargin.format(
+ classPathEnv, toolPathEnv, ppcp(classPathProp), ppcp(javaBootClassPath),
+ javaExtDirs, userHome, scalaHome
+ )
+ }
+
+ /** Default values based on those in Environment as interpretered according
+ * to the path resolution specification.
+ */
+ object Defaults {
+ private lazy val guessedScalaHome = {
+ for (url <- ScalaClassLoader originOfClass classOf[ScalaObject] ; if url.getProtocol == "file") yield
+ File(url.getFile).parent.path
+ } getOrElse ""
+
+ private def translateScalaHome(s: String) = s.replaceAll("""${SCALA_HOME}""", scalaHome)
+
+ def scalaHome = Environment.scalaHome match { case "" => guessedScalaHome ; case x => x }
+ def scalaHomeDir = Directory(scalaHome)
+ def scalaLibDir = Directory(scalaHomeDir / "lib")
+ def scalaClassesDir = Directory(scalaHomeDir / "classes")
+
+ def scalaLibJar = File(scalaLibDir / "scala-library.jar")
+ def scalaLibClassDir = Directory(scalaClassesDir / "library")
+ def scalaLibFound: Option[Directory] =
+ if (scalaLibJar.isFile) Some(scalaLibDir)
+ else if (scalaLibClassDir.isDirectory) Some(scalaClassesDir)
+ else None
+ def scalaLibPath = scalaLibFound map (_.path) getOrElse ""
+
+ def scalaExtDirs = scalaLibFound map (_.path) getOrElse ""
+ def expandedScalaExtDirs = if (scalaExtDirs == "") "" else expandToContents(scalaExtDirs)
+ def expandedClassPath = expandToPath(Environment.classPath)
+
+ val pluginSearchPath = List("misc", "scala-devel", "plugins")
+ def scalaPluginDir = pluginSearchPath map (scalaHomeDir / _) find (_.isDirectory) map (_.path) getOrElse ""
+
+ // The class path that a runner script uses to interpret a program is called the “execution class path”.
+ // The execution class path is the concatenation of the following sub-path.
+ // If a class is available in multiple locations, it must be loaded from that with the lowest number.
+ def executionPath = List(
+ // 1. The Java bootstrap class path.
+ Environment.javaBootClassPath,
+ // 2. The Java extension class path.
+ Environment.javaExtDirs,
+ // 3. The first available path below.
+ // * The fixed class path (TOOL_CLASSPATH) set in the runner script when it was generated
+ // (which can be set as the "classpath" attribute when using the scalatool Ant task).
+ // This path may contain absolute locations, or locations relative to Scala's home by using
+ // the shell variable ${SCALA_HOME} in the path.
+ // * The class path formed by all JAR and ZIP files and all folders in Scala's home lib folder.
+ Environment.toolPathEnv match {
+ case "" => Defaults.expandedScalaExtDirs
+ case x => expandToPath(translateScalaHome(x))
+ }
+ )
+
+ override def toString = """
+ |object Defaults {
+ | scalaHome = %s
+ | scalaLibFound = %s
+ | scalaPluginDir = %s
+ | expandedClassPath = %s
+ |}""".trim.stripMargin.format(
+ scalaHome, scalaLibFound, scalaPluginDir,
+ ppcp(expandedScalaExtDirs), ppcp(expandedClassPath)
+ )
+ }
+
+ def executionPath = joincp(Defaults.executionPath)
+
+ /** The original logic of MainGenericRunner.
+ */
+ def basicScalaClassPath(includeCwd: Boolean): String = {
+ // this is to make the interpreter work when running without the scala script
+ // (e.g. from eclipse). Before, "java.class.path" was added to the user classpath
+ // in Settings; this was changed to match the behavior of Sun's javac.
+ def maincp =
+ if (Environment.scalaHome == "") Defaults.expandedClassPath
+ else Defaults.expandedScalaExtDirs
+ def dotcp = if (includeCwd) "." else ""
+
+ joincp(Seq(maincp, dotcp))
+ }
+
+ /** XXX not yet used.
+ */
+ def toJavaClassPath(settings: Settings): JavaClassPath = {
+ new JavaClassPath(
+ settings.bootclasspath.value, settings.extdirs.value,
+ settings.classpath.value, settings.sourcepath.value,
+ settings.Xcodebase.value, DefaultJavaContext
+ )
+ }
+
+ /** With no arguments, show the interesting values in Environment and Defaults.
+ * If there are arguments, show those in Calculated as if those options had been
+ * given to a scala runner.
+ */
+ def main(args: Array[String]): Unit = {
+ if (args.isEmpty) {
+ println(Environment)
+ println(Defaults)
+ }
+ else {
+ val settings = new Settings()
+ val rest = settings.processArguments(args.toList)._2
+ val pr = new PathResolver(settings)
+ println(" COMMAND: 'scala %s'".format(args.mkString(" ")))
+ println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" ")))
+ println(pr.Calculated)
+ }
+ }
+
+ /**
+ * Split command line parameters by space, properly process quoted parameter
+ */
+ def splitParams(line: String, errorFn: String => Unit): List[String] = {
+ def parse(from: Int, i: Int, args: List[String]): List[String] = {
+ if (i < line.length) {
+ line.charAt(i) match {
+ case ' ' =>
+ val args1 = fetchArg(from, i) :: args
+ val j = skipS(i + 1)
+ if (j >= 0) {
+ parse(j, j, args1)
+ } else args1
+ case '"' =>
+ val j = skipTillQuote(i + 1)
+ if (j > 0) {
+ parse(from, j + 1, args)
+ } else {
+ errorFn("Parameters '" + line + "' with unmatched quote at " + i + ".")
+ Nil
+ }
+ case _ => parse(from, i + 1, args)
+ }
+ } else { // done
+ if (i > from) {
+ fetchArg(from, i) :: args
+ } else args
+ }
+ }
+
+ def fetchArg(from: Int, until: Int) = {
+ if (line.charAt(from) == '"') {
+ line.substring(from + 1, until - 1)
+ } else {
+ line.substring(from, until)
+ }
+ }
+
+ def skipTillQuote(i: Int): Int = {
+ if (i < line.length) {
+ line.charAt(i) match {
+ case '"' => i
+ case _ => skipTillQuote(i + 1)
+ }
+ } else -1
+ }
+
+ def skipS(i: Int): Int = {
+ if (i < line.length) {
+ line.charAt(i) match {
+ case ' ' => skipS(i + 1)
+ case _ => i
+ }
+ } else -1
+ }
+
+ // begin split
+ val j = skipS(0)
+ if (j >= 0) {
+ parse(j, j, Nil).reverse
+ } else Nil
+ }
+}
+import PathResolver.{ Defaults, Environment, joincp, splitcp, ppcp }
+
+class PathResolver(settings: Settings) {
+ private def cmdLineOrElse(name: String, alt: String) = {
+ (commandLineFor(name) match {
+ case Some("") => None
+ case x => x
+ }) getOrElse alt
+ }
+
+ private def commandLineFor(s: String): Option[String] = condOpt(s) {
+ case "javabootclasspath" => settings.javabootclasspath.value
+ case "javaextdirs" => settings.javaextdirs.value
+ case "bootclasspath" => settings.bootclasspath.value
+ case "extdirs" => settings.extdirs.value
+ case "classpath" | "cp" => settings.classpath.value
+ case "sourcepath" => settings.sourcepath.value
+ case "Ycodebase" => settings.Xcodebase.value
+ }
+
+ /** Calculated values based on any given command line options, falling back on
+ * those in Defaults.
+ */
+ object Calculated {
+ def javaBootClassPath = cmdLineOrElse("javabootclasspath", Environment.javaBootClassPath)
+ def javaExtDirs = cmdLineOrElse("javaextdirs", Environment.javaExtDirs)
+ def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaLibPath)
+ def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs)
+ def sourcePath = cmdLineOrElse("sourcepath", "")
+ def codeBase = cmdLineOrElse("Ycodebase", "") // XXX
+ def classPath = cmdLineOrElse("cp", Environment.classPath)
+
+ def referencePath = List(
+ // 1. The value of -javabootclasspath if it is set, or the Java bootstrap class path.
+ javaBootClassPath,
+ // 2. The value of -bootclasspath if it is set,
+ // or the lib/scala-library.jar file of Scala's home if it is available,
+ // or the classes/library folder of Scala's home if it is available.
+ scalaBootClassPath,
+ // 3. All JAR and ZIP files present in any folder listed by the value of -javaextdirs, if it is set,
+ // or the Java extension class path.
+ javaExtDirs,
+ // 4. All JAR and ZIP files present in any folder listed by the value of -extdirs, if it is set.
+ scalaExtDirs,
+ // 5. The first available path below.
+ // * The value of -classpath or -cp.
+ // * The value of the CLASSPATH environment variable.
+ // * The current directory (that is the location of ".").
+ //
+ // XXX doesn't mention java.class.path
+ classPath
+ )
+
+ override def toString = """
+ |object Calculated {
+ | javaBootClassPath = %s
+ | javaExtDirs = %s
+ | scalaBootClassPath = %s
+ | scalaExtDirs = %s
+ | sourcePath = %s
+ | codeBase = %s
+ | classPath = %s
+ |}""".trim.stripMargin.format(
+ ppcp(javaBootClassPath), ppcp(javaExtDirs),
+ ppcp(scalaBootClassPath), ppcp(scalaExtDirs),
+ ppcp(sourcePath), codeBase, ppcp(classPath)
+ )
+ }
+
+ def referencePath = joincp(Calculated.referencePath)
+}
diff --git a/src/compiler/scala/tools/util/StringOps.scala b/src/compiler/scala/tools/util/StringOps.scala
index 01b2482147..21ece6490f 100644
--- a/src/compiler/scala/tools/util/StringOps.scala
+++ b/src/compiler/scala/tools/util/StringOps.scala
@@ -8,7 +8,8 @@
// $Id$
-package scala.tools.util
+package scala.tools
+package util
/** This objects provides methods to extract elements from
* a string according to some defined character separator.
@@ -31,4 +32,11 @@ object StringOps {
}
def words(str: String): List[String] = decompose(str, ' ')
+
+ def splitWhere(str: String, f: Char => Boolean, doDropIndex: Boolean = false): Option[(String, String)] =
+ splitAt(str, str indexWhere f, doDropIndex)
+
+ def splitAt(str: String, idx: Int, doDropIndex: Boolean = false): Option[(String, String)] =
+ if (idx == -1) None
+ else Some(str take idx, str drop (if (doDropIndex) idx + 1 else idx))
}
diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala
index a7656a3c12..3ae46399ec 100644
--- a/src/partest/scala/tools/partest/nest/TestFile.scala
+++ b/src/partest/scala/tools/partest/nest/TestFile.scala
@@ -8,7 +8,7 @@
package scala.tools.partest
package nest
-import java.io.{File, BufferedReader, FileReader}
+import java.io.File
import scala.tools.nsc.Settings
import scala.tools.nsc.io.{ Path, Directory }
@@ -30,13 +30,8 @@ abstract class TestFile(kind: String) {
settings.outdir.value = (Path(dir) / objDir).createDirectory(true).path
// add additional flags found in 'testname.flags'
- val flagsFile = new File(dir, fileBase + ".flags")
- if (flagsFile.exists) {
- val reader = new BufferedReader(new java.io.FileReader(flagsFile))
- val flags = reader.readLine
- if (flags ne null)
- settings.parseParams(settings.splitParams(flags))
- }
+ def flagsPath = Path(dir) / (fileBase + ".flags")
+ flagsPath ifFile { _.slurp().trim } foreach (settings processArgumentString _)
}
def defineSettings(settings: Settings) {
diff --git a/tools/pathResolver b/tools/pathResolver
new file mode 100755
index 0000000000..4deb8595e9
--- /dev/null
+++ b/tools/pathResolver
@@ -0,0 +1,4 @@
+#!/bin/sh
+#
+
+scala scala.tools.util.PathResolver $* \ No newline at end of file