summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-02-08 22:28:17 +0000
committerPaul Phillips <paulp@improving.org>2010-02-08 22:28:17 +0000
commit171d21f11a58e905de8f96a9bac41f6aced3e384 (patch)
tree1abc4a6ca8597e517b78f2faef13064d7c69cfc6 /src/compiler
parent53545e7af8d3455f97d88c1a455fea86461419fb (diff)
downloadscala-171d21f11a58e905de8f96a9bac41f6aced3e384.tar.gz
scala-171d21f11a58e905de8f96a9bac41f6aced3e384.tar.bz2
scala-171d21f11a58e905de8f96a9bac41f6aced3e384.zip
Some work on classpaths.
at https://lampsvn.epfl.ch/trac/scala/wiki/Classpath modulo some minor details which remain to be investigated. It is not entirely integrated, and should not involve any behavioral changes. The patch also contains a number of small improvements targetting widely duplicated code. PathResolver offers a main method. If run with no arguments it will output a pile of information about classpath relevant environment vars and properties. If given arguments, it will output the classpath info that any scala runner script would use if given the same args. There is a wrapper in the tools directory. Example: tools/pathResolver -extdirs /foo -sourcepath /bar | egrep "sourcePath|scalaExtDirs" scalaExtDirs = /foo sourcePath = /bar There is also a (probably temporary) command line option -Ylog-classpath which will print out the settings.classpath value anytime it changes. Review by community.
Diffstat (limited to 'src/compiler')
-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
20 files changed, 578 insertions, 366 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))
}