summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/CompilerCommand.scala
blob: 9b8e9fa3304f523391b149d620bfd1885e1dac9e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc

import io.File

/** A class representing command line info for scalac */
class CompilerCommand(arguments: List[String], val settings: Settings) {
  def this(arguments: List[String], error: String => Unit) = this(arguments, new Settings(error))
  def this(arguments: List[String], settings: Settings, error: String => Unit) = this(arguments, settings withErrorFn error)

  type Setting = Settings#Setting

  private val processArgumentsResult =
    if (shouldProcessArguments) processArguments
    else (true, Nil)
  def ok    = processArgumentsResult._1
  def files = processArgumentsResult._2

  /** The name of the command. */
  def cmdName = "scalac"

  /** A descriptive alias for version and help messages. */
  def cmdDesc = "compiler"

  private def explainAdvanced = "\n" + """
    |-- Notes on option parsing --
    |Boolean settings are always false unless set.
    |Where multiple values are accepted, they should be comma-separated.
    |  example: -Xplugin:option1,option2
    |<phases> means one or a comma-separated list of:
    |  (partial) phase names, phase ids, phase id ranges, or the string "all".
    |  example: -Xprint:all prints all phases.
    |  example: -Xprint:expl,24-26 prints phases explicitouter, closelim, dce, jvm.
    |  example: -Xprint:-4 prints only the phases up to typer.
    |
  """.stripMargin.trim + "\n"

  def shortUsage = "Usage: %s <options> <source files>" format cmdName

  /** Creates a help message for a subset of options based on cond */
  def createUsageMsg(cond: Setting => Boolean): String = {
    val baseList            = (settings.visibleSettings filter cond).toList sortBy (_.name)
    val width               = (baseList map (_.helpSyntax.length)).max
    def format(s: String)   = ("%-" + width + "s") format s
    def helpStr(s: Setting) = {
      val str    = format(s.helpSyntax) + "  " + s.helpDescription
      val suffix = s.deprecationMessage match {
        case Some(msg) => "\n" + format("") + "      deprecated: " + msg
        case _         => ""
      }
      str + suffix
    }
    val debugs      = baseList filter (_.isForDebug)
    val deprecateds = baseList filter (_.isDeprecated)
    val theRest     = baseList filterNot (debugs.toSet ++ deprecateds)

    def sstring(msg: String, xs: List[Setting]) =
      if (xs.isEmpty) None else Some(msg :: xs.map(helpStr) mkString "\n  ")

    List(
      sstring("", theRest),
      sstring("\nAdditional debug settings:", debugs),
      sstring("\nDeprecated settings:", deprecateds)
    ).flatten mkString "\n"
  }

  def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting => Boolean): String = {
    val prefix = List(
      Some(shortUsage),
      Some(explainAdvanced) filter (_ => shouldExplain),
      Some(label + " options include:")
    ).flatten mkString "\n"

    prefix + createUsageMsg(cond)
  }

  /** Messages explaining usage and options */
  def usageMsg    = createUsageMsg("where possible standard", shouldExplain = false, _.isStandard)
  def xusageMsg   = createUsageMsg("Possible advanced", shouldExplain = true, _.isAdvanced)
  def yusageMsg   = createUsageMsg("Possible private", shouldExplain = true, _.isPrivate)

  /** For info settings, compiler should just print a message and quit. */
  def shouldStopWithInfo = settings.isInfo

  def getInfoMessage(global: Global): String = {
    import settings._
    import Properties.{ versionString, copyrightString } //versionFor
    def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString"

    if (version)            versionFor(cmdDesc)
    else if (help)          usageMsg + global.pluginOptionsHelp
    else if (Xhelp)         xusageMsg
    else if (Yhelp)         yusageMsg
    else if (showPlugins)   global.pluginDescriptions
    else if (showPhases)    global.phaseDescriptions + (
      if (debug) "\n" + global.phaseFlagDescriptions else ""
    )
    else if (genPhaseGraph.isSetByUser) {
      val components = global.phaseNames  // global.phaseDescriptors // one initializes
      s"Phase graph of ${components.size} components output to ${genPhaseGraph.value}*.dot."
    }
    // would be nicer if we could ask all the options for their helpful messages
    else {
      val sb = new StringBuilder
      allSettings foreach {
        case s: MultiChoiceSetting[_] if s.isHelping => sb append s.help
        case _ =>
      }
      sb.toString
    }
  }

  /**
   * Expands all arguments starting with @ to the contents of the
   * file named like each argument.
   */
  def expandArg(arg: String): List[String] = {
    def stripComment(s: String) = s takeWhile (_ != '#')
    val file = File(arg stripPrefix "@")
    if (!file.exists)
      throw new java.io.FileNotFoundException("argument file %s could not be found" format file.name)

    settings splitParams (file.lines() map stripComment mkString " ")
  }

  // override this if you don't want arguments processed here
  def shouldProcessArguments: Boolean = true

  def processArguments: (Boolean, List[String]) = {
    // expand out @filename to the contents of that filename
    val expandedArguments = arguments flatMap {
      case x if x startsWith "@"  => expandArg(x)
      case x                      => List(x)
    }

    settings.processArguments(expandedArguments, processAll = true)
  }
}