summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/Settings.scala
blob: 22dd700d1efcef5dccd1c9b66c024a7ca4eac768 (plain) (tree)
1
2
3
4
5
6
7
8
9
                            
                                

                          

       
                       
 
                   

                      
                 
                          
 
                                                                
                                    
 
                                 
                                                                                        
                                                                                  


                                                                   
                                                             
                                                               
 
                                  
                                        
 


                                                                            
                                                                                             

                                                                                                
 
                                
                                                                
 


                                     
                                   




                                                                                                 
 




                                                                                    

   






                                                            
                                                
                                               

                                                          

   

                                                                




                                                  




                                                                                     
       

        

   









                                                                         














                                                                                                 



                                     
       


                                                          
                                       













                                                                                 
     






                                                                             


                                                
            
       


                                                                  
       


                                               
       








                                                                          
                               


                                                                  
                               





                                              























































                                                                                          
                                                                                      


                                                                                                  

 
                 







                                                                    
                               

   

































                                                                            

                                            
 




                                                                    


                                                   
                         








                                                                 

                                                               













                                                                            







                                                                        







                                                         













                                                                                                           


                                                         

   
                                                                      


                                                                     
                                                                                    

                                                                               
 





                                                                                     





                                                                               














                                                                                   
       

                                                                                      
 






                                                                  
                                                            







                                                                                       

                                                                        
                               
 
                                                             

                             
                                                 


                                                                                                 
 





                                                                                        

                                                                          


                                               
                                                         
                                           
                                                           

   
                                            



                                     

                                  
                          

                             






                                               


                                                         
                                               
                      

                                  
                                                                        

                                                                
                                                         



                                                                             
     

                                                      
                                 
 





                                                       
 
                                                                         
 

                                                               
                                           


                                                    

                               

                                     




                                                 
   
 
                                                                    


                                         
                          



                                                                    
                                                              


                                                        



                                                 


                                                                  




                                        
                          





                                                              
     
                                                                                
 
                                           




                                                 

   




                                                                                                       
                     





                                         

                                                            



                                             
                          
                         
                                       
                                                          
 
                                        



                                                               
     

                                                                   
 
                                           



                                                 

   

                                                                               
     


                                        
                                       
                        
                                                             
                   
                                     
                                                      
 





                                                                                                                        
     
                               
                                                             
 
                                                



                                                 
   
 


                                                                              
     





                                       
                                                        


                                                                          
     
                                                  
 


                                            
     
 


                                                            



                                                 
   

                                                                            

                                                                               

                                  


                                        
                                                  

                                       
 



                                                                           
     

                                                                                 
                                              
                                                               
 

                                                            

                                                 








                                                                        
                                                                                 













                                                                           

     












                                                                             



                                                 



   
                      























                                                                                                                                                                     
                                                         

                                                                                                                                                
                                                                                                                                               



                                                                                                                                                                  
                                                                                                                                                                           

                                                                            
                                                                                                                                                         

                                                                                                               
                                                                                                                                                








                                                                                                        
                                                                                                                                                               
                                                                                                                                                                                 

                                                                                                                                                                                      
                                                                                                                                                             
                                                                                                          

                                                                                                                                                      
                                                                                            
                                                                                                                  





















                                                                                                                                                               
                                                                                             








                                                                                                                        
                                                                                                 

                                                                                        








                                                                                                                            
                                                                                                                                         




                                                                                                                          
                                                                                     


                                                                                                                               
                                                                                                       
                                                                    

                                                                                               
                                                                                                                              

                                                                                                                                                     
                                                                                        
                                                                                               
                                                                                                                               
                                                                                                                                 
 










                                                                                                                      




                                                                                            
 
/* NSC -- new Scala compiler
 * Copyright 2005-2009 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc

import java.io.File
import io.AbstractFile
import util.SourceFile
import Settings._
import annotation.elidable

class Settings(errorFn: String => Unit) extends ScalacSettings {
  def this() = this(Console.println)

  // optionizes a system property
  private def syspropopt(name: String): Option[String] = onull(System.getProperty(name))
  private def sysenvopt(name: String): Option[String] = onull(System.getenv(name))

  // given any number of possible path segments, flattens down to a
  // :-separated style path
  private def concatPath(segments: Option[String]*): String =
    segments.toList.flatMap(x => x) mkString File.pathSeparator

  protected def classpathDefault =
    sysenvopt("CLASSPATH") getOrElse "."

  protected def bootclasspathDefault =
    concatPath(syspropopt("sun.boot.class.path"), guessedScalaBootClassPath)
    // syspropopt("sun.boot.class.path") getOrElse ""
    // XXX scala-library.jar was being added to both boot and regular classpath until 8/18/09
    // Removing from boot classpath caused build/quick/bin/scala to fail.
    // Note to self, figure out how/why the bootclasspath is tied up with the locker/quick/pack.

  protected def extdirsDefault =
    concatPath(syspropopt("java.ext.dirs"), guessedScalaExtDirs)

  protected def assemExtdirsDefault =
    concatPath(guessedScalaExtDirs)

  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)
  def mkPath(base: String, segments: String*) = new File(base, segments.mkString(File.separator))
  def scalaHome: Option[String] = onull(Properties.scalaHome)

  // examine path relative to scala home and return Some(path) if it meets condition
  private def guess(xs: List[String], cond: (File) => Boolean): Option[String] = {
    if (scalaHome.isEmpty) return None
    val f = mkPath(scalaHome.get, xs: _*)
    if (cond(f)) Some(f.getAbsolutePath) else None
  }

  private def guessedScalaBootClassPath: Option[String] =
    guess(List("lib", "scala-library.jar"), _.isFile) orElse
    guess(List("classes", "library"), _.isDirectory)

  private def guessedScalaExtDirs: Option[String] =
    guess(List("lib"), _.isDirectory)

  override def hashCode() = allSettings.hashCode
  override def equals(that: Any) = that match {
    case s: Settings  => this.allSettings == s.allSettings
    case _            => false
  }

  def checkDependencies: Boolean = {
    def hasValue(s: Setting, value: String): Boolean = s match {
      case bs: BooleanSetting => bs.value
      case ss: StringSetting  => ss.value == value
      case cs: ChoiceSetting  => cs.value == value
      case _ => "" == value
    }

    for (setting <- allSettings ; (dep, value) <- setting.dependency)
      if (!setting.isDefault && !hasValue(dep, value)) {
        errorFn("incomplete option " + setting.name + " (requires " + dep.name + ")")
        return false
      }

    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
   *  output directory that is used for all source files, denoted by a
   *  '*' in this list.
   */
  lazy val outputDirs = new OutputDirs


  /** 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)  =>
          val res = setter(cmd)(args)
          cmd.postSetHook()
          res
      }

    // if arg is of form -Xfoo:bar,baz,quux
    def parseColonArg(s: String): Option[List[String]] = {
      val idx = s indexWhere (_ == ':')
      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 arg :: rest = args
      if (arg == "") {
        // it looks like Ant passes "" sometimes
        rest
      }
      else if (!arg.startsWith("-")) {
        errorFn("Argument '" + arg + "' does not start with '-'.")
        args
      }
      else if (arg == "-") {
        errorFn("'-' is not a valid argument.")
        args
      }
      else
        // 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 (arg contains ":") parseColonArg(arg) match {
          case Some(_)  => rest
          case None     => args
        }
        else if (isPropertyArg(arg)) parsePropertyArg(arg) match {
          case Some(_)  => rest
          case None     => args
        }
        else parseNormalArg(arg, rest) 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())
  lazy val OutputSetting       = untupled(tupled(output _) andThen add[OutputSetting])

  override def toString() =
    "Settings(\n%s)" format (settingSet filter (s => !s.isDefault) map ("  " + _ + "\n") mkString)
}

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 }
    val choices : List[T] = Nil
  }

  /** A class for holding mappings from source directories to
   *  their output location. This functionality can be accessed
   *  only programmatically. The command line compiler uses a
   *  single output location, but tools may use this functionality
   *  to set output location per source directory.
   */
  class OutputDirs {
    /** Pairs of source directory - destination directory. */
    private var outputDirs: List[(AbstractFile, AbstractFile)] = Nil

    /** If this is not None, the output location where all
     *  classes should go.
     */
    private var singleOutDir: Option[AbstractFile] = None

    /** Add a destination directory for sources found under srcdir.
     *  Both directories should exits.
     */
    def add(srcDir: String, outDir: String): Unit =
      add(checkDir(AbstractFile.getDirectory(srcDir), srcDir),
          checkDir(AbstractFile.getDirectory(outDir), outDir))

    /** Check that dir is exists and is a directory. */
    private def checkDir(dir: AbstractFile, name: String): AbstractFile = {
      if ((dir eq null) || !dir.isDirectory)
        throw new FatalError(name + " does not exist or is not a directory")
      dir
    }

    /** Set the single output directory. From now on, all files will
     *  be dumped in there, regardless of previous calls to 'add'.
     */
    def setSingleOutput(outDir: String) {
      val dst = AbstractFile.getDirectory(outDir)
      setSingleOutput(checkDir(dst, outDir))
    }

    /** Set the single output directory. From now on, all files will
     *  be dumped in there, regardless of previous calls to 'add'.
     */
    def setSingleOutput(dir: AbstractFile) {
      singleOutDir = Some(dir)
    }

    def add(src: AbstractFile, dst: AbstractFile) {
      singleOutDir = None
      outputDirs ::= (src, dst)
    }

    /** Return the list of source-destination directory pairs. */
    def outputs: List[(AbstractFile, AbstractFile)] = outputDirs

    /** Return the output directory for the given file.
     */
    def outputDirFor(src: AbstractFile): AbstractFile = {
      def isBelow(srcDir: AbstractFile, outDir: AbstractFile) =
        src.path.startsWith(srcDir.path)

      singleOutDir match {
        case Some(d) => d
        case None =>
          (outputs find Function.tupled(isBelow)) match {
            case Some((_, d)) => d
            case _ =>
              throw new FatalError("Could not find an output directory for "
                                   + src.path + " in " + outputs)
          }
      }
    }
  }

  // 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,
      range: Option[(Int, Int)] = None,
      parser: String => Option[Int] = _ => None
    ) =
      new IntSetting(name, descr, default, range, parser)

    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()

    def output(outputDirs: OutputDirs, default: String) =
      new OutputSetting(outputDirs, default)
  }

  implicit val SettingOrdering : Ordering[Setting] = Ordering.ordered;
  /** A base class for settings of all types.
   *  Subclasses each define a `value' field of the appropriate type.
   */
  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

    /** 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 }

    /** 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 }

    /** 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
     */
    private[Settings] def tryToSetProperty(args: List[String]): Option[List[String]] =
      errorAndValue("'" + name + "' does not accept property style arguments", None)

    /**
     * Attempt to set from a properties file style property value.
     */
    def tryToSetFromPropertyValue(s : String) {
      tryToSet(s :: Nil)
    }

    /** The syntax defining this setting in a help string */
    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

    /** A list of Strings which can recreate this setting. */
    def unparse: List[String]

    /** 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 != "-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]): Boolean =
      xs.sortWith(_ < _) == ys.sortWith(_ < _)

    // Equality
    def eqValues: List[Any] = List(name, value)
    def isEq(other: Setting) = eqValues == other.eqValues
    override def hashCode() = name.hashCode
    override def toString() = "%s = %s".format(name, value)
  }

  /** A setting represented by an integer */
  class IntSetting private[Settings](
    val name: String,
    val descr: String,
    val default: Int,
    val range: Option[(Int, Int)],
    parser: String => Option[Int])
  extends Setting(descr) {
    type T = Int
    protected var v = default

    // not stable values!
    val IntMin = Int.MinValue
    val IntMax = Int.MaxValue
    def min = range map (_._1) getOrElse IntMin
    def max = range map (_._2) getOrElse IntMax

    override def value_=(s: Int) =
      if (isInputValid(s)) super.value_=(s) else errorMsg

    // Validate that min and max are consistent
    assert(min <= max)

    // Helper to validate an input
    private def isInputValid(k: Int): Boolean = (min <= k) && (k <= max)

    // Helper to generate a textual explaination of valid inputs
    private def getValidText: String = (min, max) match {
      case (IntMin, IntMax)   => "can be any integer"
      case (IntMin, x)        => "must be less than or equal to "+x
      case (x, IntMax)        => "must be greater than or equal to "+x
      case _                  => "must be between %d and %d".format(min, max)
    }

    // Ensure that the default value is actually valid
    assert(isInputValid(default))

    def parseArgument(x: String): Option[Int] = {
      parser(x) orElse {
        try   { Some(x.toInt) }
        catch { case _: NumberFormatException => None }
      }
    }

    def errorMsg = errorFn("invalid setting for -"+name+" "+getValidText)

    def tryToSet(args: List[String]) =
      if (args.isEmpty) errorAndValue("missing argument", None)
      else parseArgument(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 x: IntSetting => this isEq x
      case _            => false
    }
  }

  /** A setting represented by a boolean flag (false, unless set) */
  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 tryToSetFromPropertyValue(s : String) {
      value = s.equalsIgnoreCase("true")
    }
    override def equals(that: Any) = that match {
      case x: BooleanSetting => this isEq x
      case _            => false
    }
  }

  /** A setting represented by a string, (`default' unless set) */
  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 + ">")

    override def equals(that: Any) = that match {
      case x: StringSetting => this isEq x
      case _            => false
    }
  }

  /** Set the output directory. */
  class OutputSetting private[Settings](
    outputDirs: OutputDirs,
    default: String)
    extends StringSetting("-d", "directory", "Specify where to place generated class files", default) {
      value = default
      override def value_=(str: String) {
        super.value_=(str)
        outputDirs.setSingleOutput(str)
      }
  }

  /** A setting that accumulates all strings supplied to it,
   *  until it encounters one starting with a '-'. */
  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 appendToValue(str: String) { value ++= List(str) }

    def tryToSet(args: List[String]) = {
      val (strings, rest) = args span (x => !x.startsWith("-"))
      strings foreach appendToValue

      Some(rest)
    }
    override def tryToSetColon(args: List[String]) = tryToSet(args)
    def unparse: List[String] = value map { name + ":" + _ }

    withHelpSyntax(name + ":<" + arg + ">")
    override def equals(that: Any) = that match {
      case x: MultiStringSetting => this isEq x
      case _            => false
    }
  }

  /** A setting represented by a string in a given set of <code>choices</code>,
   *  (<code>default</code> unless set).
   */
  class ChoiceSetting private[Settings](
    val name: String,
    val descr: String,
    override val choices: List[String],
    val default: String)
  extends Setting(descr + choices.mkString(" (", ",", ")")) {
    type T = String
    protected var v: String = default
    protected def argument: String = name.substring(1)

    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)
    }
    def unparse: List[String] =
      if (value == default) Nil else List(name + ":" + value)

    withHelpSyntax(name + ":<" + argument + ">")
    override def equals(that: Any) = that match {
      case x: ChoiceSetting => this isEq x
      case _            => false
    }
  }

  /** Same as ChoiceSetting but have a <code>level</code> int which tells the
   *  index of the selected choice. The <code>defaultEmpty</code> is used when
   *  this setting is used without specifying any of the available choices.
   */
  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) = {
      super.value_=(choice)
      level = indexOf(choices, choice).get
    }

    override def tryToSet(args: List[String]) =
      if (args.isEmpty) { value = defaultEmpty ; Some(Nil) }
      else super.tryToSet(args)
    override def equals(that: Any) = that match {
      case x: DebugSetting => this isEq x
      case _            => false
    }
  }

  /** A setting represented by a list of strings which should be prefixes of
   *  phase names. This is not checked here, however.  Alternatively the string
   *  "all" can be used to represent all phases.
   *  (the empty list, unless set)
   */
  class PhasesSetting private[Settings](
    val name: String,
    val descr: String)
  extends Setting(descr + " <phase> or \"all\"") {
    type T = List[String]
    protected var v: List[String] = Nil

    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)
    }
    // 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 { phasename startsWith _ } )

    def doAllPhases() = value contains "all"
    def unparse: List[String] = value map { name + ":" + _ }

    override def equals(that: Any) = that match {
      case ps: PhasesSetting if name == ps.name =>
        (doAllPhases && ps.doAllPhases) || compareLists(value, ps.value)
      case _                                    => false
    }

    withHelpSyntax(name + ":<phase>")
  }

  /** 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 + "<prop>")

    // 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) }
    override def equals(that: Any) = that match {
      case x: DefinesSetting => this isEq x
      case _            => false
    }
  }

}

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    ("@<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 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")
  val encoding      = StringSetting     ("-encoding", "encoding", "Specify character encoding used by source files", Properties.sourceEncoding)
  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", "transitivenocp"), "all") .
                                          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")
  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")
  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-name", "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 assemextdirs  = StringSetting     ("-Xassem-extdirs", "dirs", "List of directories containing assemblies, defaults to `lib'", assemExtdirsDefault).dependsOn(target, "msil")
  val sourcedir     = StringSetting     ("-Xsourcedir", "directory", "When -target:msil, the source folder structure is mirrored in output directory.", ".").dependsOn(target, "msil")
  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 elideLevel    = IntSetting        ("-Xelide-level", "Generate calls to @elidable-marked methods only method priority is greater than argument.",
                                                elidable.ASSERTION, None, elidable.byName.get(_))
  val Xexperimental = BooleanSetting    ("-Xexperimental", "Enable experimental extensions")
  val noForwarders  = BooleanSetting    ("-Xno-forwarders", "Do not generate static forwarders in mirror classes")
  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 newArrays     = BooleanSetting    ("-Ynewarrays", "Generate code for new array scheme")

  /**
   * -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 noCompletion  = BooleanSetting    ("-Yno-completion", "Disable tab-completion in the REPL")
  val Xdce          = BooleanSetting    ("-Ydead-code", "Perform dead code elimination")
  val debug         = BooleanSetting    ("-Ydebug", "Output debugging messages")
  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:<which>")
  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, Int.MaxValue), _ => 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:<enabled>")
  val Ystatistics   = 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", "invoke-dynamic"), "poly-cache") .
                        withHelpSyntax("-Ystruct-dispatch:<method>")
  val specialize    = BooleanSetting    ("-Yspecialize", "Specialize generic code on types.")
  val Yrangepos     = BooleanSetting    ("-Yrangepos", "Use range positions for syntax trees.")
  val Yidedebug     = BooleanSetting    ("-Yide-debug", "Generate, validate and output trees using the interactive compiler.")
  val Ybuilderdebug = ChoiceSetting     ("-Ybuilder-debug", "Compile using the specified build manager", List("none", "refined", "simple"), "none") .
                        withHelpSyntax("-Ybuilder-debug:<method>")
  val Ytyperdebug   = BooleanSetting    ("-Ytyper-debug", "Trace all type assignements")
  val Ypmatdebug    = BooleanSetting    ("-Ypmat-debug", "Trace all pattern matcher activity.")
  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.")

  // Warnings
  val Xwarninit     = BooleanSetting    ("-Xwarninit", "Warn about possible changes in initialization semantics")
  val Xchecknull    = BooleanSetting    ("-Xcheck-null", "Emit warning on selection of nullable reference")
  val Xwarndeadcode = BooleanSetting    ("-Ywarn-dead-code", "Emit warnings for dead code")
  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(() =>
                            List(YwarnShadow, YwarnCatches, Xwarndeadcode, Xwarninit) foreach (_.value = true)
                          )

  /**
   * -P "Plugin" settings
   */
  val pluginOptions = MultiStringSetting("-P", "plugin:opt", "Pass an option to a plugin") .
                        withHelpSyntax("-P:<plugin>:<opt>")
}