From 8bafc41b19deca3b95877633dd07ffa38c42feec Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 12 Mar 2009 18:15:25 +0000 Subject: A big cleanup of Settings code. manage existing settings and add new ones. It's paving the way for low-fuss scalac preferences so we can exert fine grained config file based control over compiler behavior. --- src/compiler/scala/tools/ant/Scalac.scala | 2 +- src/compiler/scala/tools/ant/Scaladoc.scala | 13 +- src/compiler/scala/tools/nsc/CompileClient.scala | 4 +- src/compiler/scala/tools/nsc/CompilerCommand.scala | 148 ++-- .../scala/tools/nsc/GenericRunnerCommand.scala | 5 +- .../scala/tools/nsc/GenericRunnerSettings.scala | 50 +- .../scala/tools/nsc/InterpreterCommand.scala | 2 +- .../scala/tools/nsc/OfflineCompilerCommand.scala | 16 +- src/compiler/scala/tools/nsc/ScriptRunner.scala | 2 +- src/compiler/scala/tools/nsc/Settings.scala | 913 ++++++++++++--------- src/compiler/scala/tools/nsc/doc/Settings.scala | 8 +- 11 files changed, 613 insertions(+), 550 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index 7518ab8664..f4cbcf1bf8 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -555,7 +555,7 @@ class Scalac extends MatchingTask { if (!assemrefs.isEmpty) settings.assemrefs.value = assemrefs.get log("Scalac params = '" + addParams + "'", Project.MSG_DEBUG) - settings.parseParams(addParams, error) + settings.parseParams(addParams) (settings, sourceFiles, javaOnly) } diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index e3178e8d9e..ebb7414734 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -567,15 +567,16 @@ class Scaladoc extends MatchingTask { var args = if (addParams.trim() == "") Nil else List.fromArray(addParams.trim().split(" ")).map(_.trim()) + while (!args.isEmpty) { - val argsBuf = args if (args.head startsWith "-") { - for (docSetting <- docSettings.allSettings) - args = docSetting.tryToSet(args); - } else error("Parameter '" + args.head + "' does not start with '-'.") - if (argsBuf eq args) - error("Parameter '" + args.head + "' is not recognised by Scaladoc.") + val args0 = args + args = docSettings.parseParams(args) + if (args0 eq args) error("Parameter '" + args.head + "' is not recognised by Scaladoc.") + } + else error("Parameter '" + args.head + "' does not start with '-'.") } + Pair(docSettings, sourceFiles) } diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index 013ce2a823..941c722efb 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -34,7 +34,7 @@ class StandardCompileClient { pathsList.map(absFileName).mkString("", sep, "") } - val fileEnding = Properties.fileEndingString + val fileEndings = Properties.fileEndingString.split("""\|""").toList protected def normalize(args: Array[String]): (String, String) = { var i = 0 @@ -42,7 +42,7 @@ class StandardCompileClient { var serverAdr = "" while (i < args.length) { val arg = args(i) - if (fileEnding split ("\\|") exists (arg endsWith _)) { + if (fileEndings exists(arg endsWith _)) { args(i) = absFileName(arg) } else if (arg startsWith "-J") { //see http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/javac.html#J diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 7896f4e270..ea0aa5f969 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -6,6 +6,8 @@ package scala.tools.nsc +import Settings.Setting +import java.io.IOException /** A class representing command line info for scalac */ class CompilerCommand( @@ -18,16 +20,17 @@ class CompilerCommand( def this(arguments: List[String], settings: Settings, error: String => Unit, interactive: Boolean) = this(arguments, settings, error, interactive, true) + /** Private buffer for accumulating files to compile */ private var fs: List[String] = List() - /** All files to compile */ + /** Public list of files to compile */ def files: List[String] = fs.reverse /** The name of the command */ val cmdName = "scalac" - /** The file extension of files that the compiler can process */ - lazy val fileEnding = Properties.fileEndingString + /** The file extensions of files that the compiler can process */ + lazy val fileEndings = Properties.fileEndingString.split("""\|""").toList private val helpSyntaxColumnWidth: Int = Iterable.max(settings.allSettings map (_.helpSyntax.length)) @@ -39,64 +42,30 @@ class CompilerCommand( buf.toString() } - /** A message explaining usage and options */ - def usageMsg: String = { - settings.allSettings - .filter(_.isStandard) - .map(setting => - format(setting.helpSyntax) + " " + setting.helpDescription) - .mkString("Usage: " + cmdName + " \n" + - "where possible standard options include:\n ", - "\n ", - "\n") - } - - /** A message explaining usage and options */ - def xusageMsg: String = { - settings.allSettings - .filter(_.isAdvanced) - .map(setting => - format(setting.helpSyntax) + " " + setting.helpDescription) - .mkString("Possible advanced options include:\n ", - "\n ", - "\n") - } - - /** A message explaining usage and options */ - def yusageMsg: String = { - settings.allSettings - .filter(_.isPrivate) - .map(setting => - format(setting.helpSyntax) + " " + setting.helpDescription) - .mkString("Possible private options include:\n ", - "\n ", - "\n") - } - - // If any of these settings is set, the compiler shouldn't - // start; an informative message of some sort - // should be printed instead. + /** Creates a help message for a subset of options based on cond */ + def createUsageMsg(label: String, cond: (Setting) => Boolean): String = + settings.allSettings . + filter(cond) . + map(s => format(s.helpSyntax) + " " + s.helpDescription) . + mkString("Usage: %s \n%s options include:\n " . + format(cmdName, label), "\n ", "\n") + + /** Messages explaining usage and options */ + def usageMsg = createUsageMsg("where possible standard", _.isStandard) + def xusageMsg = createUsageMsg("Possible advanced", _.isAdvanced) + def yusageMsg = createUsageMsg("Possible private", _.isPrivate) + + // If any of these settings is set, the compiler shouldn't start; + // an informative message of some sort should be printed instead. // (note: do not add "files.isEmpty" do this list) val stopSettings = List[(() => Boolean, (Global) => String)]( - (() => settings.help.value, - compiler => usageMsg + compiler.pluginOptionsHelp - ), - (() => settings.Xhelp.value, - compiler => xusageMsg - ), - (() => settings.Yhelp.value, - compiler => yusageMsg - ), - (() => settings.showPlugins.value, - compiler => compiler.pluginDescriptions - ), - (() => settings.showPhases.value, - compiler => compiler.phaseDescriptions - ) + (settings.help.value _, usageMsg + _.pluginOptionsHelp), + (settings.Xhelp.value _, _ => xusageMsg), + (settings.Yhelp.value _, _ => yusageMsg), + (settings.showPlugins.value _, _.pluginDescriptions), + (settings.showPlugins.value _, _.phaseDescriptions) ) - - def shouldStopWithInfo: Boolean = - stopSettings.exists(pair => (pair._1)()) + def shouldStopWithInfo: Boolean = stopSettings exists { _._1() } def getInfoMessage(compiler: Global): String = stopSettings.find(pair => (pair._1)()) match { @@ -112,42 +81,37 @@ class CompilerCommand( 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 (ext => x endsWith ext)) + + // 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 + "'") + } - while (!args.isEmpty && ok) { - if (args.head startsWith "@") { - try { - args = util.ArgumentsExpander.expandArg(args.head) ::: args.tail - } catch { - case ex: java.io.IOException => - error(ex.getMessage()) - ok = false - } - } else if (args.head startsWith "-") { - if (interactive) { - error("no options can be given in interactive mode") - ok = false - } else { - val args0 = args - for (setting <- settings.allSettings) - if (args eq args0) - args = setting.tryToSet(args) - - if (args eq args0) { - error("bad option: '" + args.head + "'") - ok = false - } - } - } else if ((settings.script.value != "") || - (fileEnding.split("\\|") exists (args.head.endsWith(_)))) { - fs = args.head :: fs - args = args.tail - } else if (args.head.length == 0) {//quick fix [martin: for what?] - args = args.tail - } else { - error("don't know what to do with " + args.head) - ok = false - } + // 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 } diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index a131a6e737..5c0f9c6d62 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -20,7 +20,6 @@ extends CompilerCommand(allargs, settings, error, false, false) def this(allargs: List[String]) = this(allargs, str => Console.println("Error: " + str)) - /** name of the associated compiler command */ val compCmdName = "scalac" @@ -35,9 +34,7 @@ extends CompilerCommand(allargs, settings, error, false, false) while (!args.isEmpty && ok && args.head.startsWith("-")) { val args0 = args - for (setting <- settings.allSettings) - if (args eq args0) - args = setting.tryToSet(args) + args = settings parseParams args if (args eq args0) { error("bad option: '" + args.head + "'") ok = false diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index fc69df6203..0e23681ed6 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -7,9 +7,6 @@ package scala.tools.nsc -import java.lang.System.getProperties -import scala.collection.mutable.Queue - class GenericRunnerSettings(error: String => Unit) extends Settings(error) { val howtorun = @@ -42,50 +39,5 @@ extends Settings(error) { "-nocompdaemon", "do not use the fsc compilation daemon") - - /** For some reason, "object defines extends Setting(...)" - * does not work here. The object is present but the setting - * is not added to allsettings. Thus, - */ - class DefinesSetting extends Setting("set a Java property") { - - def name = "-D" - - private val props = new Queue[(String, String)] - - def value = props.toList - - def tryToSet(args: List[String]): List[String] = { - args match { - case arg0::rest - if arg0.startsWith("-D") => - val stripD = arg0.substring(2) - val eqidx = stripD.indexOf('=') - val addition = - if (eqidx < 0) - (stripD, "") - else - (stripD.substring(0, eqidx), stripD.substring(eqidx+1)) - props += addition - rest - - case _ => args - } - } - - /** Apply the specified properties to the current JVM */ - def applyToCurrentJVM = { - val systemProps = getProperties - for ((key, value) <- props.toList) - systemProps.setProperty(key, value) - } - - def unparse: List[String] = - (props.toList.foldLeft[List[String]] - (Nil) - ((args, prop) => - ("-D" + prop._1 + "=" + prop._2) :: args)) - } - - val defines = new DefinesSetting + val defines = DefinesSetting } diff --git a/src/compiler/scala/tools/nsc/InterpreterCommand.scala b/src/compiler/scala/tools/nsc/InterpreterCommand.scala index f46f995ba8..3ed91c156c 100644 --- a/src/compiler/scala/tools/nsc/InterpreterCommand.scala +++ b/src/compiler/scala/tools/nsc/InterpreterCommand.scala @@ -14,5 +14,5 @@ package scala.tools.nsc class InterpreterCommand(arguments: List[String], error: String => Unit) extends CompilerCommand(arguments, new Settings(error), error, false) { override val cmdName = "scala" - override lazy val fileEnding = ".scalaint" + override lazy val fileEndings = List(".scalaint") } diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala index 2290ce517d..0bd436018c 100644 --- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala @@ -18,11 +18,13 @@ class OfflineCompilerCommand( extends CompilerCommand(arguments, new Settings(error), error, false) { override val cmdName = "fsc" - settings.disable(settings.prompt) - settings.disable(settings.resident) - new settings.BooleanSetting("-reset", "Reset compile server caches") - new settings.BooleanSetting("-shutdown", "Shutdown compile server") - new settings.StringSetting("-server", "hostname:portnumber", - "Specify compile server socket", "") - new settings.BooleanSetting("-J", "Pass directly to runtime system") + import settings._ + + disable(prompt) + disable(resident) + + BooleanSetting("-reset", "Reset compile server caches") + BooleanSetting("-shutdown", "Shutdown compile server") + StringSetting ("-server", "hostname:portnumber", "Specify compile server socket", "") + BooleanSetting("-J", "Pass directly to runtime system") } diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 261c4e4a05..aa440adbf3 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -211,7 +211,7 @@ object ScriptRunner { scriptFileIn: String): Boolean = { val scriptFile = CompileClient.absFileName(scriptFileIn) - for (setting:settings.StringSetting <- List( + for (setting <- List( settings.classpath, settings.sourcepath, settings.bootclasspath, diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index b60f54bb79..b1d7cddee9 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -7,32 +7,29 @@ package scala.tools.nsc import java.io.File -import java.lang.System - -class Settings(error: String => Unit) { +import Settings._ +class Settings(errorFn: String => Unit) extends ScalacSettings { def this() = this(Console.println) - private var allsettings: List[Setting] = List() - // optionizes a system property private def sysprop(name: String): Option[String] = onull(System.getProperty(name)) // given any number of possible path segments, flattens down to a // :-separated style path - protected def concatPath(segments: Option[String]*): String = + private def concatPath(segments: Option[String]*): String = segments.toList.flatMap(x => x) mkString File.pathSeparator - protected val classpathDefault = + protected def classpathDefault = sysprop("env.classpath") orElse sysprop("java.class.path") getOrElse "" - protected val bootclasspathDefault = + protected def bootclasspathDefault = concatPath(sysprop("sun.boot.class.path"), guessedScalaBootClassPath) - protected val extdirsDefault = + protected def extdirsDefault = concatPath(sysprop("java.ext.dirs"), guessedScalaExtDirs) - protected val pluginsDirDefault = + protected def pluginsDirDefault = guess(List("misc", "scala-devel", "plugins"), _.isDirectory) getOrElse "" def onull[T <: AnyRef](x: T): Option[T] = if (x eq null) None else Some(x) @@ -53,99 +50,7 @@ class Settings(error: String => Unit) { private def guessedScalaExtDirs: Option[String] = guess(List("lib"), _.isDirectory) - val debuginfo = new DebugSetting ("-g", "Specify level of generated debugging info", List("none", "source", "line", "vars", "notailcalls"), "vars", "vars") - val nowarnings = BooleanSetting ("-nowarn", "Generate no warnings").hideToIDE - val verbose = BooleanSetting ("-verbose", "Output messages about what the compiler is doing").hideToIDE - val deprecation = BooleanSetting ("-deprecation", "Output source locations where deprecated APIs are used").hideToIDE - val unchecked = BooleanSetting ("-unchecked", "Enable detailed unchecked warnings").hideToIDE - val classpath = (new StringSetting ("-classpath", "path", "Specify where to find user class files", classpathDefault) { override val abbreviation = "-cp" }).hideToIDE - val sourcepath = StringSetting ("-sourcepath", "path", "Specify where to find input source files", "").hideToIDE - val bootclasspath = StringSetting ("-bootclasspath", "path", "Override location of bootstrap class files", bootclasspathDefault).hideToIDE - val extdirs = StringSetting ("-extdirs", "dirs", "Override location of installed extensions", extdirsDefault).hideToIDE - val outdir = StringSetting ("-d", "directory", "Specify where to place generated class files", ".").hideToIDE - val encoding = StringSetting ("-encoding", "encoding", "Specify character encoding used by source files", Properties.encodingString).hideToIDE - val target = ChoiceSetting ("-target", "Specify for which target object files should be built", List("jvm-1.5", "jvm-1.4", "msil"), "jvm-1.5") - val printLate = BooleanSetting ("-print", "Print program with all Scala-specific features removed").hideToIDE - val XO = BooleanSetting ("-optimise", "Generates faster bytecode by applying optimisations to the program") - val explaintypes = BooleanSetting ("-explaintypes", "Explain type errors in more detail").hideToIDE - val uniqid = BooleanSetting ("-uniqid", "Print identifiers with unique names for debugging").hideToIDE - val version = BooleanSetting ("-version", "Print product version and exit").hideToIDE - val dependenciesFile = StringSetting ("-dependencyfile", "file", "Specify the file in which dependencies are tracked", ".scala_dependencies") - val make = ChoiceSetting ("-make", "Specify the behaviour for selecting which files need to be recompiled", List("all", "changed", "immediate", "transitive"), "all") - val help = BooleanSetting ("-help", "Print a synopsis of standard options").hideToIDE - val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options").hideToIDE - val argfiles = BooleanSetting ("@", "A text file containing compiler arguments (options and source files)") // only for the help message - - val assemname = StringSetting ("-Xassem", "file", "Name of the output assembly (only relevant with -target:msil)", "").dependsOn(target, "msil").hideToIDE - val assemrefs = StringSetting ("-Xassem-path", "path", "List of assemblies referenced by the program (only relevant with -target:msil)", ".").dependsOn(target, "msil").hideToIDE - val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference") - val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics") - val checkInit = BooleanSetting ("-Xcheckinit", "Add runtime checks on field accessors. Uninitialized accesses result in an exception being thrown.") - val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions and assumptions") - val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions") - val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more info on why some implicits are not applicable") - val Xnojline = new BooleanSetting("-Xnojline", "Do not use JLine for editing").hideToIDE - val nouescape = BooleanSetting ("-Xno-uescape", "Disables handling of \\u unicode escapes") - val plugin = MultiStringSetting("-Xplugin", "file", "Load a plugin from a file") - val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable a plugin") - val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins").hideToIDE - val pluginOptions = new MultiStringSetting("-P", "plugin:opt", "Pass an option to a plugin") { override def helpSyntax = "-P::" } - val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless a plugin is available") - val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Location to find compiler plugins", pluginsDirDefault) - val print = PhasesSetting ("-Xprint", "Print out program after") - val writeICode = BooleanSetting ("-Xprint-icode", "Log internal icode to *.icode files").hideToIDE - val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions (as offsets)").hideToIDE - val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option)").hideToIDE - val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option)").hideToIDE - val resident = BooleanSetting ("-Xresident", "Compiler stays resident, files to compile are read from standard input").hideToIDE - val Xshowcls = StringSetting ("-Xshow-class", "class", "Show class info", "").hideToIDE - val Xshowobj = StringSetting ("-Xshow-object", "object", "Show object info", "").hideToIDE - val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases").hideToIDE - val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "filename", "Generate the phase graphs (outputs .dot files) to filenameX.dot", "").hideToIDE - val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "scala.tools.nsc.io.SourceReader").hideToIDE - val future = BooleanSetting ("-Xfuture", "Turn on future language features") - - val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options").hideToIDE - val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") - val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") - val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination") - val Xcodebase = StringSetting ("-Ycodebase", "codebase", "Specify the URL containing the Scala libraries", "").hideToIDE - val debug = BooleanSetting ("-Ydebug", "Output debugging messages").hideToIDE - val debugger = BooleanSetting ("-Ydebugger", "Enable interactive debugger").hideToIDE - val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination") - val Xdetach = BooleanSetting ("-Ydetach", "Perform detaching of remote closures") -// val doc = BooleanSetting ("-Ydoc", "Generate documentation").hideToIDE - val inline = BooleanSetting ("-Yinline", "Perform inlining when possible") - val Xlinearizer = ChoiceSetting ("-Ylinearizer", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") - val log = PhasesSetting ("-Ylog", "Log operations in") - val noimports = BooleanSetting ("-Yno-imports", "Compile without any implicit imports") - val nopredefs = BooleanSetting ("-Yno-predefs", "Compile without any implicit predefined values") - val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0), None).hideToIDE - val script = StringSetting ("-Xscript", "object", "Compile as a script, wrapping the code into object.main()", "").hideToIDE - - val Xshowtrees = BooleanSetting ("-Yshow-trees", "Show detailed trees when used in connection with -print:phase").hideToIDE - val skip = PhasesSetting ("-Yskip", "Skip") - val completion = BooleanSetting ("-Ycompletion", "Enable tab-completion in the REPL").hideToIDE - val Xsqueeze = ChoiceSetting ("-Ysqueeze", "if on, creates compact code in matching", List("on","on","off"), "on").hideToIDE - val statistics = BooleanSetting ("-Ystatistics", "Print compiler statistics").hideToIDE - val stop = PhasesSetting ("-Ystop", "Stop after phase") - val refinementMethodDispatch - = ChoiceSetting ("-Ystruct-dispatch", "Selects what dispatch method to use for structural refinement method calls", List("no-cache", "mono-cache", "poly-cache"), "poly-cache").hideToIDE - val Xwarndeadcode = BooleanSetting ("-Ywarn-dead-code", "Emit warnings for dead code") - val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java") - - val XnoVarargsConversion = BooleanSetting("-Xno-varargs-conversion", "disable varags conversion") - val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations") - - val suppressVTWarn = BooleanSetting ("-Ysuppress-vt-typer-warnings", "Suppress warnings from the typer when testing the virtual class encoding, NOT FOR FINAL!") - - /** A list of all settings */ - def allSettings: List[Setting] = allsettings.reverse - /** Disable a setting */ - def disable(s: Setting) = { - allsettings = allsettings filter (s !=) - } - + override def hashCode() = allSettings.hashCode override def equals(that: Any) = that match { case s: Settings => this.allSettings == s.allSettings case _ => false @@ -158,97 +63,273 @@ class Settings(error: String => Unit) { case cs: ChoiceSetting => cs.value == value case _ => "" == value } - var ok = true - for (setting <- allsettings if !setting.dependency.isEmpty) { - val (dep, value) = setting.dependency.get - if (! (setting.isDefault || hasValue(dep, value))) { - error("incomplete option " + setting.name + " (requires " + dep.name + ")") - ok = false + + for (setting <- allSettings ; (dep, value) <- setting.dependency) + if (!setting.isDefault && !hasValue(dep, value)) { + errorFn("incomplete option " + setting.name + " (requires " + dep.name + ")") + return false } - } - ok + + true } - /** Try to add additional command line parameters. */ - def parseParams(line: String, error: String => Nothing) { - var args = - if (line.trim() == "") Nil - else List.fromArray(line.trim().split(" ")).map(_.trim()) - while (!args.isEmpty) { - val argsBuf = args - if (args.head startsWith "-") { - for (setting <- allSettings) - args = setting.tryToSet(args); + /** Try to add additional command line parameters. + * Returns unconsumed arguments. + */ + def parseParams(line: String): List[String] = + parseParams(line.trim.split("""\s+""").toList) + + def parseParams(args: List[String]): List[String] = { + // verify command exists and call setter + def tryToSetIfExists( + cmd: String, + args: List[String], + setter: (Setting) => (List[String] => Option[List[String]]) + ): Option[List[String]] = + lookupSetting(cmd) match { + case None => errorFn("Parameter '" + cmd + "' is not recognised by Scalac.") ; None + case Some(cmd) => setter(cmd)(args) } - else error("Parameter '" + args.head + "' does not start with '-'.") - if (argsBuf eq args) - error("Parameter '" + args.head + "' is not recognised by Scalac.") + + // if arg is of form -Xfoo:bar,baz,quux + def parseColonArg(s: String): Option[List[String]] = { + val idx = s.findIndexOf(_ == ':') + val (p, args) = (s.substring(0, idx), s.substring(idx+1).split(",").toList) + + // any non-Nil return value means failure and we return s unmodified + tryToSetIfExists(p, args, (s: Setting) => s.tryToSetColon _) + } + // if arg is of form -Dfoo=bar or -Dfoo (name = "-D") + def isPropertyArg(s: String) = lookupSetting(s.substring(0, 2)) match { + case Some(x: DefinesSetting) => true + case _ => false + } + def parsePropertyArg(s: String): Option[List[String]] = { + val (p, args) = (s.substring(0, 2), s.substring(2)) + + tryToSetIfExists(p, List(args), (s: Setting) => s.tryToSetProperty _) } + + // if arg is of form -Xfoo or -Xfoo bar (name = "-Xfoo") + def parseNormalArg(p: String, args: List[String]): Option[List[String]] = + tryToSetIfExists(p, args, (s: Setting) => s.tryToSet _) + + def doArgs(args: List[String]): List[String] = { + if (args.isEmpty) return Nil + val p = args.head + if (!p.startsWith("-")) { + errorFn("Parameter '" + p + "' does not start with '-'.") + return args + } + else if (p == "-") { + errorFn("'-' is not a valid argument.") + return args + } + + // we dispatch differently based on the appearance of p: + // 1) If it has a : it is presumed to be -Xfoo:bar,baz + // 2) If the first two chars are the name of a command, -Dfoo=bar + // 3) Otherwise, the whole string should be a command name + // + // Internally we use Option[List[String]] to discover error, + // but the outside expects our arguments back unchanged on failure + if (p contains ":") parseColonArg(p) match { + case Some(_) => args.tail + case None => args + } + else if (isPropertyArg(p)) parsePropertyArg(p) match { + case Some(_) => args.tail + case None => args + } + else parseNormalArg(p, args.tail) match { + case Some(xs) => xs + case None => args + } + } + + doArgs(args) + } + + // checks both name and any available abbreviations + def lookupSetting(cmd: String): Option[Setting] = + settingSet.find(x => x.name == cmd || (x.abbreviations contains cmd)) + + // The *Setting classes used to be case classes defined inside of Settings. + // The choice of location was poor because it tied the type of each setting + // to its enclosing instance, which broke equality, so I moved the class + // definitions into the companion object. The one benefit it was getting + // out of this was using its knowledge of the enclosing instance to add + // itself to the list of settings in the Setting constructor. However, + // this was dicey and not working predictably, as illustrated in the comment + // in GenericRunnerSettings: + // + // For some reason, "object defines extends Setting(...)" + // does not work here. The object is present but the setting + // is not added to allsettings. + // + // To capture similar semantics, I created instance methods on setting + // which call a factory method for the right kind of object and then add + // the newly constructed instance to allsettings. The constructors are + // private to force all creation to go through these methods. + // + // The usage of case classes was becoming problematic (due to custom + // equality, case class inheritance, and the need to control object + // creation without a synthetic apply method getting in the way) and + // it was providing little benefit, so they are no longer cases. + + // a wrapper for all Setting creators to keep our list up to date + // and tell them how to announce errors + private def add[T <: Setting](s: T): T = { + s setErrorHandler errorFn + allsettings += s + s + } + + /** + * The canonical creators for Setting objects. + */ + import Function.{ tupled, untupled } + import Setting._ + + // A bit too clever, but I haven't found any other way to compose + // functions with arity 2+ without having to annotate parameter types + lazy val IntSetting = untupled(tupled(sint _) andThen add[IntSetting]) + lazy val BooleanSetting = untupled(tupled(bool _) andThen add[BooleanSetting]) + lazy val StringSetting = untupled(tupled(str _) andThen add[StringSetting]) + lazy val MultiStringSetting = untupled(tupled(multi _) andThen add[MultiStringSetting]) + lazy val ChoiceSetting = untupled(tupled(choice _) andThen add[ChoiceSetting]) + lazy val DebugSetting = untupled(tupled(sdebug _) andThen add[DebugSetting]) + lazy val PhasesSetting = untupled(tupled(phase _) andThen add[PhasesSetting]) + lazy val DefinesSetting = add(defines()) +} + +object Settings +{ + // basically this is a value which remembers if it's been modified + trait SettingValue { + type T <: Any + protected var v: T + private var setByUser: Boolean = false + def isDefault: Boolean = !setByUser + def value: T = v + def value_=(arg: T) = { setByUser = true ; v = arg } + } + + // The Setting companion object holds all the factory methods + object Setting { + def bool(name: String, descr: String) = + new BooleanSetting(name, descr) + + def str(name: String, arg: String, descr: String, default: String) = + new StringSetting(name, arg, descr, default) + + def sint(name: String, descr: String, default: Int, min: Option[Int], max: Option[Int]) = + new IntSetting(name, descr, default, min, max) + + def multi(name: String, arg: String, descr: String) = + new MultiStringSetting(name, arg, descr) + + def choice(name: String, descr: String, choices: List[String], default: String): ChoiceSetting = + new ChoiceSetting(name, descr, choices, default) + + def sdebug(name: String, descr: String, choices: List[String], default: String, defaultEmpty: String) = + new DebugSetting(name, descr, choices, default, defaultEmpty) + + def phase(name: String, descr: String) = + new PhasesSetting(name, descr) + + def defines() = new DefinesSetting() } /** A base class for settings of all types. * Subclasses each define a `value' field of the appropriate type. */ - abstract class Setting(descr: String) { - + abstract class Setting(descr: String) extends Ordered[Setting] with SettingValue { /** The name of the option as written on the command line, '-' included. */ def name: String - /** If first arg defines this setting, consume it as well as all following - * args needed to define the setting. If this can be done without - * error, set value field and return suffix of args else - * issue error message and return the arguments unchanged. - * If first arg does not define this setting return args unchanged. + /** Error handling function, set after creation by enclosing Settings instance */ + private var _errorFn: String => Unit = _ + private[Settings] def setErrorHandler(e: String => Unit) = _errorFn = e + def errorFn(msg: String) = _errorFn(msg) + def errorAndValue[T](msg: String, x: T): T = { errorFn(msg) ; x } + + /** After correct Setting has been selected, tryToSet is called with the + * remainder of the command line. It consumes any applicable arguments and + * returns the unconsumed ones. + */ + private[Settings] def tryToSet(args: List[String]): Option[List[String]] + + /** Commands which can take lists of arguments in form -Xfoo:bar,baz override + * this method and accept them as a list. It returns List[String] for + * consistency with tryToSet, and should return its incoming arguments + * unmodified on failure, and Nil on success. + */ + private[Settings] def tryToSetColon(args: List[String]): Option[List[String]] = + errorAndValue("'" + name + "' does not accept multiple arguments", None) + + /** Commands which take properties in form -Dfoo=bar or -Dfoo */ - def tryToSet(args: List[String]): List[String] + private[Settings] def tryToSetProperty(args: List[String]): Option[List[String]] = + errorAndValue("'" + name + "' does not accept property style arguments", None) /** The syntax defining this setting in a help string */ - def helpSyntax: String = name + private var _helpSyntax = name + def helpSyntax: String = _helpSyntax + def withHelpSyntax(s: String): this.type = { _helpSyntax = s ; this } + + /** Abbreviations for this setting */ + private var _abbreviations: List[String] = Nil + def abbreviations = _abbreviations + def withAbbreviation(s: String): this.type = { _abbreviations ++= List(s) ; this } /** A description of the purpose of this setting in a help string */ def helpDescription = descr - /** Return a list of strings that can be used to recreate - * the receiver's current setting. - */ + /** A list of Strings which can recreate this setting. */ def unparse: List[String] - /** override if option should be hidden from IDE. - */ - var hiddenToIDE: Boolean = false - def hideToIDE: this.type = { - hiddenToIDE = true - this - } - def showToIDE: this.type = { - hiddenToIDE = false - this - } - - protected var setByUser: Boolean = false - def isDefault: Boolean = !setByUser + /** Set to false if option should be shown to IDE. */ + var hiddenToIDE: Boolean = true + def showToIDE() = hiddenToIDE = false + /** Optional dependency on another setting */ protected[Settings] var dependency: Option[(Setting, String)] = None def dependsOn(s: Setting, value: String): this.type = { dependency = Some((s, value)); this } def dependsOn(s: Setting): this.type = dependsOn(s, "") - def isStandard: Boolean = !isAdvanced && !isPrivate && !(name eq "-Y") - def isAdvanced: Boolean = - (name startsWith "-X") && !(name eq "-X") - def isPrivate: Boolean = - (name == "-P") || ((name startsWith "-Y") && !(name eq "-Y")) - def equalLists[T <% Ordered[T]](xs: List[T], ys: List[T]) = xs.sort(_ < _) == ys.sort(_ < _) - -/* - def isDocOption: Boolean = - !dependency.isEmpty && dependency.get._1 == doc -*/ - // initialization - allsettings = this :: allsettings + def isStandard: Boolean = !isAdvanced && !isPrivate && name != "-Y" + def isAdvanced: Boolean = (name startsWith "-X") && name != "-X" + def isPrivate: Boolean = (name == "-P") || ((name startsWith "-Y") && name != "-Y") + + // Ordered (so we can use TreeSet) + def compare(that: Setting): Int = name compare that.name + def compareLists[T <% Ordered[T]](xs: List[T], ys: List[T]) = xs.sort(_ < _) == ys.sort(_ < _) + + // Equality + def eqValues: List[Any] = List(name, value) + def isEq(other: this.type) = eqValues == other.eqValues + override def hashCode() = name.hashCode + override def equals(that: Any) = that match { + case x: this.type => this isEq x + case _ => false + } } /** A setting represented by a positive integer */ - case class IntSetting(name: String, descr: String, default: Int, min: Option[Int], max: Option[Int]) extends Setting(descr) { + class IntSetting private[Settings]( + val name: String, + val descr: String, + val default: Int, + val min: Option[Int], + val max: Option[Int]) + extends Setting(descr) + { + type T = Int + protected var v = default + override def value_=(s: Int) = + if (isInputValid(s)) super.value_=(s) else errorMsg + // Validate that min and max are consistent (min, max) match { case (Some(i), Some(j)) => assert(i <= j) @@ -256,248 +337,149 @@ class Settings(error: String => Unit) { } // Helper to validate an input - private def isInputValid(k: Int): Boolean = - (min, max) match { - case (Some(i), Some(j)) => (i <= k) && (k <= j) - case (Some(i), None) => (i <= k) - case (None, Some(j)) => (k <= j) - case _ => true - } + private def isInputValid(k: Int): Boolean = (min, max) match { + case (Some(i), Some(j)) => (i <= k) && (k <= j) + case (Some(i), None) => (i <= k) + case (None, Some(j)) => (k <= j) + case _ => true + } // Helper to generate a textual explaination of valid inputs - private def getValidText: String = - (min, max) match { - case (Some(i), Some(j)) => "must be between "+i+" and "+j - case (Some(i), None) => "must be greater than or equal to "+i - case (None, Some(j)) => "must be less than or equal to "+j - case _ => throw new Error("this should never be used") - } + private def getValidText: String = (min, max) match { + case (Some(i), Some(j)) => "must be between "+i+" and "+j + case (Some(i), None) => "must be greater than or equal to "+i + case (None, Some(j)) => "must be less than or equal to "+j + case _ => throw new Error("this should never be used") + } // Ensure that the default value is actually valid assert(isInputValid(default)) - protected var v: Int = default - - def errorMsg = error("invalid setting for -"+name+" "+getValidText) + def parseInt(x: String): Option[Int] = + try { Some(x.toInt) } + catch { case _: NumberFormatException => None } - def value: Int = this.v - def value_=(s: Int) { - if (!isInputValid(s)) errorMsg - setByUser = true; - this.v = s - } + def errorMsg = errorFn("invalid setting for -"+name+" "+getValidText) - def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (name == n) => - if (rest.isEmpty) { - error("missing argument") - args - } else { - try { - value = rest.head.toInt - } catch { - case e: java.lang.NumberFormatException => errorMsg - } - rest.tail - } - case _ => args - } + def tryToSet(args: List[String]) = + if (args.isEmpty) errorAndValue("missing argument", None) + else parseInt(args.head) match { + case Some(i) => value = i ; Some(args.tail) + case None => errorMsg ; None + } def unparse: List[String] = - if (value == default) Nil else List(name, value.toString) - - override def equals(that: Any) = that match { - case is: Settings#IntSetting => this.name == is.name && this.value == is.value - case _ => false - } - + if (value == default) Nil + else List(name, value.toString) } /** A setting represented by a boolean flag (false, unless set) */ - case class BooleanSetting(name: String, descr: String) extends Setting(descr) { - protected var v: Boolean = false - - def value: Boolean = this.v - def value_=(s: Boolean) { setByUser = true; this.v = s } - - def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (n == name) => value = true; rest - case _ => args - } - + class BooleanSetting private[Settings]( + val name: String, + val descr: String) + extends Setting(descr) + { + type T = Boolean + protected var v = false + + def tryToSet(args: List[String]) = { value = true ; Some(args) } def unparse: List[String] = if (value) List(name) else Nil - - override def equals(that: Any) = that match { - case bs: Settings#BooleanSetting => this.name == bs.name && this.value == bs.value - case _ => false - } - } /** A setting represented by a string, (`default' unless set) */ - case class StringSetting(name: String, arg: String, descr: String, default: String) - extends Setting(descr) { - def abbreviation: String = null - - protected var v: String = default - - def value: String = this.v - def value_=(s: String) { setByUser = true; this.v = s } - - def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (name == n || abbreviation == n) => - if (rest.isEmpty) { - error("missing argument") - args - } else { - value = rest.head - rest.tail - } - case _ => args - } - - override def helpSyntax = name + " <" + arg + ">" - - def unparse: List[String] = - if (value == default) Nil else List(name, value) - - override def equals(that: Any) = that match { - case ss: Settings# StringSetting => this.name == ss.name && this.value == ss.value - case _ => false + class StringSetting private[Settings]( + val name: String, + val arg: String, + val descr: String, + val default: String) + extends Setting(descr) + { + type T = String + protected var v = default + + def tryToSet(args: List[String]) = args match { + case Nil => errorAndValue("missing argument", None) + case x :: xs => value = x ; Some(xs) } + def unparse: List[String] = if (value == default) Nil else List(name, value) + withHelpSyntax(name + " <" + arg + ">") } /** A setting that accumulates all strings supplied to it */ - case class MultiStringSetting(name: String, arg: String, descr: String) - extends Setting(descr) { - hideToIDE + class MultiStringSetting private[Settings]( + val name: String, + val arg: String, + val descr: String) + extends Setting(descr) + { + type T = List[String] protected var v: List[String] = Nil - def value = v - def appendToValue(str: String) { v = v ::: List(str) } - - protected val nameColon = name + ":" - def tryToSet(args: List[String]): List[String] = args match { - case arg :: rest if (arg startsWith nameColon) => - val toadd = arg.substring(nameColon.length()) - if (toadd.length == 0) { - error("empty argument to " + nameColon) - args - } else { - appendToValue(toadd) - rest - } - - case opt :: arg :: rest if (opt == name) => - appendToValue(arg) - rest - - case _ => args - } - - override def helpSyntax = name + ":<" + arg + ">" + def appendToValue(str: String) { value ++= List(str) } - def unparse: List[String] = - for (opt <- value) yield nameColon+opt - - override def equals(that: Any) = that match { - case mss: Settings# MultiStringSetting => - this.name == mss.name && equalLists(this.value, mss.value) - case _ => false + def tryToSet(args: List[String]) = { + args foreach appendToValue + Some(Nil) } + override def tryToSetColon(args: List[String]) = tryToSet(args) + def unparse: List[String] = value map { name + ":" + _ } + withHelpSyntax(name + ":<" + arg + ">") } /** A setting represented by a string in a given set of choices, * (default unless set). */ - case class ChoiceSetting(name: String, descr: String, choices: List[String], default: String) - extends Setting(descr + choices.mkString(" (", ",", ")")) { + class ChoiceSetting private[Settings]( + val name: String, + val descr: String, + val choices: List[String], + val default: String) + extends Setting(descr + choices.mkString(" (", ",", ")")) + { + type T = String protected var v: String = default - - def value: String = this.v - def value_=(s: String) { setByUser = true; this.v = s } - protected def argument: String = name.substring(1) - def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (n startsWith (name + ":")) => - val choice = n.substring(name.length() + 1) - if (!(choices contains choice)) { - error( - if (choice == "") "missing " + argument - else "unknown " + argument + " '" + choice + "'") - args - } else { - value = choice - rest - } - case n :: choice :: rest if n == name => // alternative to be consistent with IDE - if (!(choices contains choice)) { - error( - if (choice == "") "missing " + argument - else "unknown " + argument + " '" + choice + "'") - args - } else { - value = choice - rest - } - case _ => args + def tryToSet(args: List[String]) = { value = default ; Some(args) } + override def tryToSetColon(args: List[String]) = args match { + case Nil => errorAndValue("missing " + argument, None) + case List(x) if choices contains x => value = x ; Some(Nil) + case List(x) => errorAndValue("'" + x + "' is not a valid choice for '" + name + "'", None) + case xs => errorAndValue("'" + name + "' does not accept multiple arguments.", None) } - - override def helpSyntax = name + ":<" + argument + ">" - def unparse: List[String] = if (value == default) Nil else List(name + ":" + value) - override def equals(that: Any) = that match { - case cs: Settings# ChoiceSetting => this.name == cs.name && this.value == cs.value - case _ => false - } - + withHelpSyntax(name + ":<" + argument + ">") } /** Same as ChoiceSetting but have a level int which tells the * index of the selected choice. The defaultEmpty is used when * this setting is used without specifying any of the available choices. */ - class DebugSetting(name: String, descr: String, choices: List[String], default: String, defaultEmpty: String) - extends ChoiceSetting(name, descr, choices, default) { - - def indexOf[a](xs: List[a], e: a): Option[Int] = xs match { - case y :: ys => if (e == y) Some(0) else indexOf(ys, e) match { - case Some(idx) => Some(1 + idx) - case None => None - } - case _ => None + class DebugSetting private[Settings]( + name: String, + descr: String, + choices: List[String], + default: String, + val defaultEmpty: String) + extends ChoiceSetting(name, descr, choices, default) + { + def indexOf[T](xs: List[T], e: T): Option[Int] = xs.indexOf(e) match { + case -1 => None + case x => Some(x) } var level: Int = indexOf(choices, default).get - override def value_=(choice: String) { - setByUser = true - this.v = choice - this.level = indexOf(choices, choice).get + override def value_=(choice: String) = { + super.value_=(choice) + level = indexOf(choices, choice).get } - override def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (n startsWith (name + ":")) => - val choice = n.substring(name.length() + 1) - if (!(choices contains choice)) { - error( - if (choice == "") "missing " + argument - else "unknown " + argument + " '" + choice + "'") - args - } else { - value = choice - rest - } - - case n :: rest if (n startsWith name) => - value = defaultEmpty - rest - - case _ => args - } + override def tryToSet(args: List[String]) = + if (args.isEmpty) { value = defaultEmpty ; Some(Nil) } + else super.tryToSet(args) } /** A setting represented by a list of strings which should be prefixes of @@ -505,43 +487,204 @@ class Settings(error: String => Unit) { * "all" can be used to represent all phases. * (the empty list, unless set) */ - case class PhasesSetting(name: String, descr: String) - extends Setting(descr + " or \"all\"") { - hideToIDE - protected var v: List[String] = List() - - def value: List[String] = this.v - def value_=(s: List[String]) { setByUser = true; this.v = s } - def doAllPhases() = value contains "all" + class PhasesSetting private[Settings]( + val name: String, + val descr: String) + extends Setting(descr + " or \"all\"") + { + type T = List[String] + protected var v: List[String] = Nil - def tryToSet(args: List[String]): List[String] = args match { - case n :: rest if (n startsWith (name + ":")) => - val phase = n.substring(name.length() + 1) - if (phase == "") { - error("missing phase") - args - } else { - value = value ::: List(phase) - rest - } - case _ => args + def tryToSet(args: List[String]) = errorAndValue("missing phase", None) + override def tryToSetColon(args: List[String]) = args match { + case Nil => errorAndValue("missing phase", None) + case xs => value ++= xs ; Some(Nil) } - - override def helpSyntax = name + ":" - // we slightly abuse the usual meaning of "contains" here by returning // true if our phase list contains "all", regardless of the incoming argument def contains(phasename: String): Boolean = - doAllPhases || (value exists (str => phasename startsWith str)) + doAllPhases || (value exists { phasename startsWith _ } ) - def unparse: List[String] = value.map(phase => name + ":" + phase) + def doAllPhases() = value contains "all" + def unparse: List[String] = value map { name + ":" + _ } override def equals(that: Any) = that match { - case ps: Settings#PhasesSetting => - (this.name == ps.name) && - (this.doAllPhases && ps.doAllPhases || equalLists(this.value, ps.value)) - case _ => false + case ps: PhasesSetting if name == ps.name => + (doAllPhases && ps.doAllPhases) || compareLists(value, ps.value) + case _ => false + } + + withHelpSyntax(name + ":") + } + + /** A setting for a -D style property definition */ + class DefinesSetting private[Settings] extends Setting("set a Java property") + { + type T = List[(String, String)] + protected var v: T = Nil + def name = "-D" + withHelpSyntax(name + "") + + // given foo=bar returns Some(foo, bar), or None if parse fails + def parseArg(s: String): Option[(String, String)] = { + if (s == "") return None + val regexp = """^(.*)?=(.*)$""".r + + regexp.findAllIn(s).matchData.toList match { + case Nil => Some(s, "") + case List(md) => md.subgroups match { case List(a,b) => Some(a,b) } + } } + def tryToSet(args: List[String]) = + if (args.isEmpty) None + else parseArg(args.head) match { + case None => None + case Some((a, b)) => value ++= List((a, b)) ; Some(args.tail) + } + + /** Apply the specified properties to the current JVM */ + def applyToCurrentJVM = + value foreach { case (k, v) => System.getProperties.setProperty(k, v) } + + def unparse: List[String] = + value map { case (k,v) => "-D" + k + (if (v == "") "" else "=" + v) } + } + +} + +trait ScalacSettings +{ + self: Settings => + + import collection.immutable.TreeSet + + /** A list of all settings */ + protected var allsettings: Set[Setting] = TreeSet[Setting]() + def settingSet: Set[Setting] = allsettings + def allSettings: List[Setting] = settingSet.toList + + /** Disable a setting */ + def disable(s: Setting) = allsettings -= s + + /** + * Temporary Settings + */ + val suppressVTWarn = BooleanSetting ("-Ysuppress-vt-typer-warnings", "Suppress warnings from the typer when testing the virtual class encoding, NOT FOR FINAL!") + + /** + * Standard settings + */ + // argfiles is only for the help message + val argfiles = BooleanSetting ("@", "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 outdir = StringSetting ("-d", "directory", "Specify where to place generated class files", ".") + 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") + val encoding = StringSetting ("-encoding", "encoding", "Specify character encoding used by source files", Properties.encodingString) + val explaintypes = BooleanSetting ("-explaintypes", "Explain type errors in more detail") + val extdirs = StringSetting ("-extdirs", "dirs", "Override location of installed extensions", extdirsDefault) + val debuginfo = DebugSetting ("-g", "Specify level of generated debugging info", List("none", "source", "line", "vars", "notailcalls"), "vars", "vars") + val help = BooleanSetting ("-help", "Print a synopsis of standard options") + val make = ChoiceSetting ("-make", "Specify recompilation detection strategy", List("all", "changed", "immediate", "transitive"), "all") . + withHelpSyntax("-make:") + val nowarnings = BooleanSetting ("-nowarn", "Generate no warnings") + val XO = BooleanSetting ("-optimise", "Generates faster bytecode by applying optimisations to the program") + 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", "jvm-1.4", "msil"), "jvm-1.5") + val unchecked = BooleanSetting ("-unchecked", "Enable detailed unchecked warnings") + val uniqid = BooleanSetting ("-uniqid", "Print identifiers with unique names for debugging") + val verbose = BooleanSetting ("-verbose", "Output messages about what the compiler is doing") + val version = BooleanSetting ("-version", "Print product version and exit") + + /** + * -X "Advanced" settings + */ + val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options") + val assemname = StringSetting ("-Xassem", "file", "Name of the output assembly (only relevant with -target:msil)", "").dependsOn(target, "msil") + val assemrefs = StringSetting ("-Xassem-path", "path", "List of assemblies referenced by the program (only relevant with -target:msil)", ".").dependsOn(target, "msil") + val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference") + val checkInit = BooleanSetting ("-Xcheckinit", "Add runtime checks on field accessors. Uninitialized accesses result in an exception being thrown.") + val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions and assumptions") + val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions") + val future = BooleanSetting ("-Xfuture", "Turn on future language features") + val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot", "") + val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more info on why some implicits are not applicable") + val nouescape = BooleanSetting ("-Xno-uescape", "Disables handling of \\u unicode escapes") + val XnoVarargsConversion = BooleanSetting("-Xno-varargs-conversion", "disable varags conversion") + val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing") + val plugin = MultiStringSetting("-Xplugin", "file", "Load a plugin from a file") + val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable a plugin") + val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins") + val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless a plugin is available") + val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Location to find compiler plugins", pluginsDirDefault) + val print = PhasesSetting ("-Xprint", "Print out program after") + val writeICode = BooleanSetting ("-Xprint-icode", "Log internal icode to *.icode files") + val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions (as offsets)") + val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option)") + val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option)") + val resident = BooleanSetting ("-Xresident", "Compiler stays resident, files to compile are read from standard input") + val script = StringSetting ("-Xscript", "object", "Compile as a script, wrapping the code into object.main()", "") + val Xshowcls = StringSetting ("-Xshow-class", "class", "Show class info", "") + val Xshowobj = StringSetting ("-Xshow-object", "object", "Show object info", "") + val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases") + val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "scala.tools.nsc.io.SourceReader") + val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics") + + /** + * -Y "Private" settings + */ + val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options") + val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") + val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") + val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination") + val Xcodebase = StringSetting ("-Ycodebase", "codebase", "Specify the URL containing the Scala libraries", "") + val completion = BooleanSetting ("-Ycompletion", "Enable tab-completion in the REPL") + val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination") + val debug = BooleanSetting ("-Ydebug", "Output debugging messages") + val debugger = BooleanSetting ("-Ydebugger", "Enable interactive debugger") + val Xdetach = BooleanSetting ("-Ydetach", "Perform detaching of remote closures") + // val doc = BooleanSetting ("-Ydoc", "Generate documentation") + val inline = BooleanSetting ("-Yinline", "Perform inlining when possible") + val Xlinearizer = ChoiceSetting ("-Ylinearizer", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") . + withHelpSyntax("-Ylinearizer:") + val log = PhasesSetting ("-Ylog", "Log operations in") + val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java") + val noimports = BooleanSetting ("-Yno-imports", "Compile without any implicit imports") + val nopredefs = BooleanSetting ("-Yno-predefs", "Compile without any implicit predefined values") + val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0), None) + val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations") + val Xshowtrees = BooleanSetting ("-Yshow-trees", "Show detailed trees when used in connection with -print:phase") + val skip = PhasesSetting ("-Yskip", "Skip") + val Xsqueeze = ChoiceSetting ("-Ysqueeze", "if on, creates compact code in matching", List("on","off"), "on") . + withHelpSyntax("-Ysqueeze:") + val statistics = BooleanSetting ("-Ystatistics", "Print compiler statistics") + val stop = PhasesSetting ("-Ystop", "Stop after phase") + val refinementMethodDispatch = + ChoiceSetting ("-Ystruct-dispatch", "Selects dispatch method for structural refinement method calls", + List("no-cache", "mono-cache", "poly-cache"), "poly-cache") . + withHelpSyntax("-Ystruct-dispatch:") + val Xwarndeadcode = BooleanSetting ("-Ywarn-dead-code", "Emit warnings for dead code") + + /** + * -P "Plugin" settings + */ + val pluginOptions = MultiStringSetting("-P", "plugin:opt", "Pass an option to a plugin") . + withHelpSyntax("-P::") + + /** + * IDE Visible Settings - "showToIDE" called on each. + */ + + val settingsForIDE = { + val xs = List( + argfiles, dependenciesFile, debuginfo, make, XO, target, + Xchecknull, checkInit, noassertions, Xexperimental, future, XlogImplicits, nouescape, XnoVarargsConversion, pluginsDir, Xwarninit, + Xcloselim, Xdce, Xdetach, inline, Xlinearizer, Ynogenericsig, noimports, nopredefs, selfInAnnots, Xwarndeadcode + ) + xs foreach { _.showToIDE } + xs } } diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index a43f61b048..a6357a31cf 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -19,10 +19,14 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { val doctitle = StringSetting ("-doctitle", "doctitle", "Include title for the overview page", "Scala 2
API Specification") val pagefooter = StringSetting ("-footer", "pagefooter", "Include footer text for each page", "") val pageheader = StringSetting ("-header", "pageheader", "Include header text for each page", "") - val linksource = BooleanSetting ("-linksource", "Generate source in HTML").hideToIDE - val nocomment = BooleanSetting ("-nocomment", "Suppress description and tags, generate only declarations.").hideToIDE + val linksource = BooleanSetting ("-linksource", "Generate source in HTML") + val nocomment = BooleanSetting ("-nocomment", "Suppress description and tags, generate only declarations.") val stylesheetfile = StringSetting ("-stylesheetfile", "stylesheetfile", "File to change style of the generated documentation", "style.css") val pagetop = StringSetting ("-top", "pagetop", "Include top text for each page", "") val windowtitle = StringSetting ("-windowtitle", "windowtitle", "Specify window title of generated HTML documentation", "Scala 2") + List( + memberaccess, pagebottom, doccharset, doctitle, pagefooter, + pageheader, stylesheetfile, pagetop, windowtitle + ) foreach { _.showToIDE } } -- cgit v1.2.3