aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/config/CompilerCommand.scala
blob: c2301a3aa252c08613a8655dbe6525ac63f2587f (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
package dotty.tools.dotc
package config

import java.io.File
import Settings._
import core.Contexts._
import util.DotClass
import Properties._

object CompilerCommand extends DotClass {

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

  private def explainAdvanced = """
    |-- Notes on option parsing --
    |Boolean settings are always false unless set.
    |Where multiple values are accepted, they should be comma-separated.
    |  example: -Xplugin:plugin1,plugin2
    |<phases> means one or a comma-separated list of:
    |  - (partial) phase names with an optional "+" suffix to include the next phase
    |  - the string "all"
    |  example: -Xprint:all prints all phases.
    |  example: -Xprint:front,mixin prints the frontend and mixin phases.
    |  example: -Ylog:erasure+ logs the erasure phase and the phase after the erasure phase.
    |           This is useful because during the tree transform of phase X, we often
    |           already are in phase X + 1.
  """

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

  def versionMsg = s"Dotty compiler $versionString -- $copyrightString"

  /** Distill arguments into summary detailing settings, errors and files to compiler */
  def distill(args: Array[String])(implicit ctx: Context): ArgsSummary = {
    /**
     * Expands all arguments starting with @ to the contents of the
     * file named like each argument.
     */
    def expandArg(arg: String): List[String] = unsupported("expandArg")/*{
      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 " ")
    }*/

    // expand out @filename to the contents of that filename
    def expandedArguments = args.toList flatMap {
      case x if x startsWith "@"  => expandArg(x)
      case x                      => List(x)
    }

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

  /** Provide usage feedback on argument summary, assuming that all settings
   *  are already applied in context.
   *  @return  The list of files to compile.
   */
  def checkUsage(summary: ArgsSummary, sourcesRequired: Boolean)(implicit ctx: Context): List[String] = {
    val settings = ctx.settings

    /** Creates a help message for a subset of options based on cond */
    def availableOptionsMsg(cond: Setting[_] => Boolean): String = {
      val ss                  = (ctx.settings.allSettings filter cond).toList sortBy (_.name)
      val width               = (ss map (_.name.length)).max
      def format(s: String)   = ("%-" + width + "s") format s
      def helpStr(s: Setting[_]) = s"${format(s.name)} ${s.description}"
      ss map helpStr 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 + "\n" + availableOptionsMsg(cond)
    }

    def isStandard(s: Setting[_]): Boolean = !isAdvanced(s) && !isPrivate(s)
    def isAdvanced(s: Setting[_]): Boolean = s.name startsWith "-X"
    def isPrivate(s: Setting[_]) : Boolean = s.name startsWith "-Y"

    /** Messages explaining usage and options */
    def usageMessage    = createUsageMsg("where possible standard", shouldExplain = false, isStandard)
    def xusageMessage   = createUsageMsg("Possible advanced", shouldExplain = true, isAdvanced)
    def yusageMessage   = createUsageMsg("Possible private", shouldExplain = true, isPrivate)

    def shouldStopWithInfo = {
      import settings._
      Set(help, Xhelp, Yhelp) exists (_.value)
    }

    def infoMessage: String = {
      import settings._
      if (help.value) usageMessage
      else if (Xhelp.value) xusageMessage
      else if (Yhelp.value) yusageMessage
      else ""
    }

    // Print all warnings encountered during arguments parsing
    summary.warnings.foreach(ctx.warning(_))

    if (summary.errors.nonEmpty) {
      summary.errors foreach (ctx.error(_))
      ctx.echo("  dotc -help  gives more information")
      Nil
    }
    else if (settings.version.value) {
      ctx.echo(versionMsg)
      Nil
    }
    else if (shouldStopWithInfo) {
      ctx.echo(infoMessage)
      Nil
    } else {
      if (sourcesRequired && summary.arguments.isEmpty) ctx.echo(usageMessage)
      summary.arguments
    }
  }
}