summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2020-11-26 15:55:56 -0800
committerGitHub <noreply@github.com>2020-11-26 15:55:56 -0800
commit52829fc8d1324c8192bc1674ff47e504648675fa (patch)
tree83919f869c1a8d2d257e4543ac2125fa23c545f4
parent9f1c5f64c5a9a3de5f4f39040b3df484de649ef2 (diff)
downloadNim-52829fc8d1324c8192bc1674ff47e504648675fa.tar.gz
cmdline: improve command processing (#16056)
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/cmdlinehelper.nim16
-rw-r--r--compiler/commands.nim57
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/main.nim99
-rw-r--r--compiler/nim.nim8
-rw-r--r--compiler/nimconf.nim2
-rw-r--r--compiler/nimeval.nim4
-rw-r--r--compiler/nimfix/nimfix.nim2
-rw-r--r--compiler/options.nim64
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/scriptconfig.nim3
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--drnim/drnim.nim6
-rw-r--r--nimsuggest/nimsuggest.nim4
-rw-r--r--tools/nimfind.nim2
18 files changed, 144 insertions, 137 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 2e710fd21..fbad9e44e 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -765,7 +765,7 @@ type
     locOther                  # location is something other
   TLocFlag* = enum
     lfIndirect,               # backend introduced a pointer
-    lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates
+    lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
       # that the symbol has been imported via 'importc: "fullname"' and
       # no format string.
     lfNoDeepCopy,             # no need for a deep copy
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 15b85097c..a3d3425e3 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1986,7 +1986,7 @@ proc writeModule(m: BModule, pending: bool) =
     var code = genModule(m, cf)
     if code != nil or m.config.symbolFiles != disabledSf:
       when hasTinyCBackend:
-        if m.config.cmd == cmdRun:
+        if m.config.cmd == cmdTcc:
           tccgen.compileCCode($code, m.config)
           onExit()
           return
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
index 4f1ede5d1..c6a0f200a 100644
--- a/compiler/cmdlinehelper.nim
+++ b/compiler/cmdlinehelper.nim
@@ -13,8 +13,6 @@ import
   options, idents, nimconf, extccomp, commands, msgs,
   lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
 
-from strutils import normalize
-
 proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
   when defined(unix):
     if os.isAbsolute(f.string): result = f
@@ -60,8 +58,8 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
 proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
                                    graph: ModuleGraph): bool =
   if self.suggestMode:
-    conf.command = "nimsuggest"
-  if conf.command == "e":
+    conf.setCmd cmdIdeTools
+  if conf.cmd == cmdNimscript:
     incl(conf.globalOptions, optWasNimscript)
   loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
 
@@ -69,17 +67,13 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
     let scriptFile = conf.projectFull.changeFileExt("nims")
     # 'nim foo.nims' means to just run the NimScript file and do nothing more:
     if fileExists(scriptFile) and scriptFile == conf.projectFull:
-      if conf.command == "":
-        conf.command = "e"
-        return false
-      elif conf.command.normalize == "e":
-        return false
-
+      if conf.cmd == cmdNone: conf.setCmd cmdNimscript
+      if conf.cmd == cmdNimscript: return false
   # now process command line arguments again, because some options in the
   # command line can overwrite the config file's settings
   extccomp.initVars(conf)
   self.processCmdLine(passCmd2, "", conf)
-  if conf.command == "":
+  if conf.cmd == cmdNone:
     rawMessage(conf, errGenerated, "command missing")
 
   graph.suggestMode = self.suggestMode
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 1ad5ec64c..916dfd66b 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -398,6 +398,48 @@ proc handleCmdInput*(conf: ConfigRef) =
   conf.projectName = "cmdfile"
   handleStdinOrCmdInput()
 
+proc parseCommand*(command: string): Command =
+  case command.normalize
+  of "c", "cc", "compile", "compiletoc": cmdCompileToC
+  of "cpp", "compiletocpp": cmdCompileToCpp
+  of "objc", "compiletooc": cmdCompileToOC
+  of "js", "compiletojs": cmdCompileToJS
+  of "r": cmdCrun
+  of "run": cmdTcc
+  of "check": cmdCheck
+  of "e": cmdNimscript
+  of "doc0": cmdDoc0
+  of "doc2", "doc": cmdDoc2
+  of "rst2html": cmdRst2html
+  of "rst2tex": cmdRst2tex
+  of "jsondoc0": cmdJsondoc0
+  of "jsondoc2", "jsondoc": cmdJsondoc
+  of "ctags": cmdCtags
+  of "buildindex": cmdBuildindex
+  of "gendepend": cmdGendepend
+  of "dump": cmdDump
+  of "parse": cmdParse
+  of "scan": cmdScan
+  of "secret": cmdInteractive
+  of "nop", "help": cmdNop
+  of "jsonscript": cmdJsonscript
+  else: cmdUnknown
+
+proc setCmd*(conf: ConfigRef, cmd: Command) =
+  ## sets cmd, backend so subsequent flags can query it (e.g. so --gc:arc can be ignored for backendJs)
+  # Note that `--backend` can override the backend, so the logic here must remain reversible.
+  conf.cmd = cmd
+  case cmd
+  of cmdCompileToC, cmdCrun, cmdTcc: conf.backend = backendC
+  of cmdCompileToCpp: conf.backend = backendCpp
+  of cmdCompileToOC: conf.backend = backendObjc
+  of cmdCompileToJS: conf.backend = backendJs
+  else: discard
+
+proc setCommandEarly*(conf: ConfigRef, command: string) =
+  conf.command = command
+  setCmd(conf, command.parseCommand)
+
 proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
                     conf: ConfigRef) =
   var
@@ -407,8 +449,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectArg(conf, switch, arg, pass, info)
     conf.projectIsCmd = true
     conf.cmdInput = arg # can be empty (a nim file with empty content is valid too)
-    if conf.command == "":
-      conf.command = "e" # better than "r" as a default
+    if conf.cmd == cmdNone:
+      conf.setCmd cmdNimscript # better than `cmdCrun` as a default
       conf.implicitCmd = true
   of "path", "p":
     expectArg(conf, switch, arg, pass, info)
@@ -499,11 +541,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "project":
     processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
   of "gc":
-    if conf.backend == backendJs or conf.command == "js":
-      # for: bug #16033
-      # This might still be imperfect, in rarse corner cases
-      # (where command is reset in nimscript, maybe).
-      return
+    if conf.backend == backendJs: return # for: bug #16033
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd2, passPP}:
       case arg.normalize
@@ -955,13 +993,12 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
   if argsCount == 0:
     # nim filename.nims  is the same as "nim e filename.nims":
     if p.key.endsWith(".nims"):
-      config.command = "e"
+      config.setCmd cmdNimscript
       incl(config.globalOptions, optWasNimscript)
       config.projectName = unixToNativePath(p.key)
       config.arguments = cmdLineRest(p)
       result = true
-    elif pass != passCmd2:
-      config.command = p.key
+    elif pass != passCmd2: setCommandEarly(config, p.key)
   else:
     if pass == passCmd1: config.commandArgs.add p.key
     if argsCount == 1:
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 9225706b5..0c624425d 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -85,7 +85,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
     result = s
   else:
     result = s.owner
-    if conf.cmd == cmdPretty:
+    if conf.cmd == cmdNimfix:
       prettybase.replaceDeprecated(conf, n.info, s, result)
     else:
       message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
diff --git a/compiler/main.nim b/compiler/main.nim
index f9b0dd413..6bdbb9efd 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -87,7 +87,7 @@ proc commandCompileToC(graph: ModuleGraph) =
   if graph.config.errorCounter > 0:
     return # issue #9933
   cgenWriteModules(graph.backend, conf)
-  if conf.cmd != cmdRun:
+  if conf.cmd != cmdTcc:
     extccomp.callCCompiler(conf)
     # for now we do not support writing out a .json file with the build instructions when HCR is on
     if not conf.hcrOn:
@@ -195,12 +195,8 @@ proc mainCommand*(graph: ModuleGraph) =
     if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp:
       conf.exc = excGoto
 
-  var commandAlreadyProcessed = false
-
-  proc compileToBackend(backend: TBackend, cmd = cmdCompileToBackend) =
-    commandAlreadyProcessed = true
-    conf.cmd = cmd
-    customizeForBackend(backend)
+  proc compileToBackend() =
+    customizeForBackend(conf.backend)
     case conf.backend
     of backendC: commandCompileToC(graph)
     of backendCpp: commandCompileToC(graph)
@@ -213,18 +209,13 @@ proc mainCommand*(graph: ModuleGraph) =
       quit "compiler wasn't built with documentation generator"
     else:
       wantMainModule(conf)
-      conf.cmd = cmdDoc
       loadConfigs(DocConfig, cache, conf, graph.idgen)
       defineSymbol(conf.symbols, "nimdoc")
       body
 
   block: ## command prepass
-    var docLikeCmd2 = false # includes what calls `docLikeCmd` + some more
-    case conf.command.normalize
-    of "r": conf.globalOptions.incl {optRun, optUseNimcache}
-    of "doc0",  "doc2", "doc", "rst2html", "rst2tex", "jsondoc0", "jsondoc2",
-      "jsondoc", "ctags", "buildindex": docLikeCmd2 = true
-    else: discard
+    if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache}
+    if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC)
     if conf.outDir.isEmpty:
       # doc like commands can generate a lot of files (especially with --project)
       # so by default should not end up in $PWD nor in $projectPath.
@@ -232,30 +223,22 @@ proc mainCommand*(graph: ModuleGraph) =
         var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
                   else: conf.projectPath
         doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
-        if docLikeCmd2: ret = ret / htmldocsDir
+        if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir
         ret
 
-  ## process all backend commands
-  case conf.command.normalize
-  of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently
-  of "cpp", "compiletocpp": compileToBackend(backendCpp)
-  of "objc", "compiletooc": compileToBackend(backendObjc)
-  of "js", "compiletojs": compileToBackend(backendJs)
-  of "r": compileToBackend(backendC) # different from `"run"`!
-  of "run":
+  ## process all commands
+  case conf.cmd
+  of cmdBackends: compileToBackend()
+  of cmdTcc:
     when hasTinyCBackend:
       extccomp.setCC(conf, "tcc", unknownLineInfo)
-      if conf.backend notin {backendC, backendInvalid}:
+      if conf.backend != backendC:
         rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend)
-      compileToBackend(backendC, cmd = cmdRun)
+      compileToBackend()
     else:
       rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
-  else: customizeForBackend(backendC) # fallback for other commands
-
-  ## process all other commands
-  case conf.command.normalize # synchronize with `cmdUsingHtmlDocs`
-  of "doc0": docLikeCmd commandDoc(cache, conf)
-  of "doc2", "doc":
+  of cmdDoc0: docLikeCmd commandDoc(cache, conf)
+  of cmdDoc2:
     docLikeCmd():
       conf.setNoteDefaults(warnLockLevel, false) # issue #13218
       conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218
@@ -266,30 +249,25 @@ proc mainCommand*(graph: ModuleGraph) =
       commandDoc2(graph, false)
       if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
         commandBuildIndex(conf, $conf.outDir)
-  of "rst2html":
+  of cmdRst2html:
     conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
     when defined(leanCompiler):
       quit "compiler wasn't built with documentation generator"
     else:
-      conf.cmd = cmdRst2html
       loadConfigs(DocConfig, cache, conf, graph.idgen)
       commandRst2Html(cache, conf)
-  of "rst2tex":
+  of cmdRst2tex:
     when defined(leanCompiler):
       quit "compiler wasn't built with documentation generator"
     else:
-      conf.cmd = cmdRst2tex
       loadConfigs(DocTexConfig, cache, conf, graph.idgen)
       commandRst2TeX(cache, conf)
-  of "jsondoc0": docLikeCmd commandJson(cache, conf)
-  of "jsondoc2", "jsondoc": docLikeCmd commandDoc2(graph, true)
-  of "ctags": docLikeCmd commandTags(cache, conf)
-  of "buildindex": docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
-  of "gendepend":
-    conf.cmd = cmdGenDepend
-    commandGenDepend(graph)
-  of "dump":
-    conf.cmd = cmdDump
+  of cmdJsondoc0: docLikeCmd commandJson(cache, conf)
+  of cmdJsondoc: docLikeCmd commandDoc2(graph, true)
+  of cmdCtags: docLikeCmd commandTags(cache, conf)
+  of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
+  of cmdGendepend: commandGenDepend(graph)
+  of cmdDump:
     if getConfigVar(conf, "dump.format") == "json":
       wantMainModule(conf)
 
@@ -332,41 +310,30 @@ proc mainCommand*(graph: ModuleGraph) =
       msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
 
       for it in conf.searchPaths: msgWriteln(conf, it.string)
-  of "check":
-    conf.cmd = cmdCheck
-    commandCheck(graph)
-  of "parse":
-    conf.cmd = cmdParse
+  of cmdCheck: commandCheck(graph)
+  of cmdParse:
     wantMainModule(conf)
     discard parseFile(conf.projectMainIdx, cache, conf)
-  of "scan":
-    conf.cmd = cmdScan
+  of cmdScan:
     wantMainModule(conf)
     commandScan(cache, conf)
     msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
-  of "secret":
-    conf.cmd = cmdInteractive
-    commandInteractive(graph)
-  of "e":
+  of cmdInteractive: commandInteractive(graph)
+  of cmdNimscript:
     if conf.projectIsCmd or conf.projectIsStdin: discard
     elif not fileExists(conf.projectFull):
       rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string)
     elif not conf.projectFull.string.endsWith(".nims"):
       rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string)
     # main NimScript logic handled in `loadConfigs`.
-  of "nop", "help":
-    # prevent the "success" message:
-    conf.cmd = cmdDump
-  of "jsonscript":
-    conf.cmd = cmdJsonScript
+  of cmdNop: discard
+  of cmdJsonscript:
     setOutFile(graph.config)
     commandJsonScript(graph)
-  elif commandAlreadyProcessed: discard # already handled
-  else:
+  of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
-  if conf.errorCounter == 0 and
-     conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
+  if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
     let mem =
       when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
       else: formatSize(getTotalMem()) & " totmem"
@@ -378,9 +345,9 @@ proc mainCommand*(graph: ModuleGraph) =
     let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName
 
     var output: string
-    if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonScript:
+    if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript:
       output = $conf.jsonBuildFile
-    elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonScript, cmdCompileToBackend, cmdDoc}:
+    elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
       # for some cmd we expect a valid absOutFile
       output = "unknownOutput"
     else:
diff --git a/compiler/nim.nim b/compiler/nim.nim
index e3725f803..46654e352 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -64,7 +64,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
       if processArgument(pass, p, argsCount, config): break
   if pass == passCmd2:
     if {optRun, optWasNimscript} * config.globalOptions == {} and
-        config.arguments.len > 0 and config.command.normalize notin ["run", "e", "r"]:
+        config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}:
       rawMessage(config, errGenerated, errArgsNeedRunOption)
 
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
@@ -86,19 +86,19 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   #echo(GC_getStatistics())
   if conf.errorCounter != 0: return
   when hasTinyCBackend:
-    if conf.cmd == cmdRun:
+    if conf.cmd == cmdTcc:
       tccgen.run(conf, conf.arguments)
   if optRun in conf.globalOptions:
     let output = conf.absOutFile
     case conf.cmd
-    of cmdCompileToBackend:
+    of cmdBackends, cmdTcc:
       var cmdPrefix = ""
       case conf.backend
       of backendC, backendCpp, backendObjc: discard
       of backendJs: cmdPrefix = findNodeJs() & " "
       else: doAssert false, $conf.backend
       execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments)
-    of cmdDoc, cmdRst2html:
+    of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing)
       if conf.arguments.len > 0:
         # reserved for future use
         rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd])
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index b6ec1bbc8..01a79c1e3 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -301,7 +301,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
   if scriptIsProj:
     showHintConf()
     configFiles.setLen 0
-  if conf.command != "nimsuggest":
+  if conf.cmd != cmdIdeTools:
     runNimScriptIfExists(scriptFile, isMain = true)
   else:
     if not scriptIsProj:
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 3ffe347c2..b93129852 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -10,7 +10,7 @@
 ## exposes the Nim VM to clients.
 import
   ast, astalgo, modules, passes, condsyms,
-  options, sem, semdata, llstream, lineinfos, vm,
+  options, sem, llstream, lineinfos, vm,
   vmdef, modulegraphs, idents, os, pathutils,
   passaux, scriptconfig
 
@@ -153,7 +153,7 @@ proc runRepl*(r: TLLRepl;
     conf.searchPaths.add(AbsoluteDir p)
     if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
 
-  conf.cmd = cmdInteractive
+  conf.cmd = cmdInteractive # see also `setCmd`
   conf.setErrorMaxHighMaybe
   initDefines(conf.symbols)
   defineSymbol(conf.symbols, "nimscript")
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index 96007477f..4d93279c9 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -38,7 +38,7 @@ In addition, all command line options of Nim are supported.
 proc mainCommand =
   registerPass verbosePass
   registerPass semPass
-  conf.cmd = cmdPretty
+  conf.setCmd cmdNimfix
   searchPaths.add options.libpath
   if gProjectFull.len != 0:
     # current path is always looked first for modules
diff --git a/compiler/options.nim b/compiler/options.nim
index 9fce5f830..42a907777 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -47,7 +47,7 @@ type                          # please make sure we have under 32 options
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
     gloptNone, optForceFullMake,
-    optWasNimscript,
+    optWasNimscript,          # redundant with `cmdNimscript`, could be removed
     optListCmd, optCompileOnly, optNoLinking,
     optCDebug,                # turn on debugging information
     optGenDynLib,             # generate a dynamic library
@@ -110,34 +110,44 @@ type
   TBackend* = enum
     backendInvalid = "" # for parseEnum
     backendC = "c"
-    backendCpp = "cpp"  # was cmdCompileToCpp
-    backendJs = "js" # was cmdCompileToJS
-    backendObjc = "objc" # was cmdCompileToOC
+    backendCpp = "cpp"
+    backendJs = "js"
+    backendObjc = "objc"
     # backendNimscript = "nimscript" # this could actually work
     # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM
 
+  Command* = enum  ## Nim's commands
+    cmdNone # not yet processed command
+    cmdUnknown # command unmapped
+    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
+    cmdCrun # compile and run in nimache
+    cmdTcc # run the project via TCC backend
+    cmdCheck # semantic checking for whole project
+    cmdParse # parse a single file (for debugging)
+    cmdScan # scan a single file (for debugging)
+    cmdIdeTools # ide tools (e.g. nimsuggest)
+    cmdNimscript # evaluate nimscript
+    cmdDoc0
+    cmdDoc2
+    cmdRst2html # convert a reStructuredText file to HTML
+    cmdRst2tex # convert a reStructuredText file to TeX
+    cmdJsondoc0
+    cmdJsondoc
+    cmdCtags
+    cmdBuildindex
+    cmdGendepend
+    cmdDump
+    cmdInteractive # start interactive session
+    cmdNop
+    cmdJsonscript # compile a .json build file
+    cmdNimfix
+    # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
+
+const
+  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
+  cmdDocLike* = {cmdDoc0, cmdDoc2, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex}
+
 type
-  TCommands* = enum           # Nim's commands
-                              # **keep binary compatible**
-    cmdNone,
-    cmdCompileToC,            # deadcode
-    cmdCompileToCpp,          # deadcode
-    cmdCompileToOC,           # deadcode
-    cmdCompileToJS,           # deadcode
-    cmdCompileToLLVM,         # deadcode
-    cmdInterpret, cmdPretty, cmdDoc,
-    cmdGenDepend, cmdDump,
-    cmdCheck,                 # semantic checking for whole project
-    cmdParse,                 # parse a single file (for debugging)
-    cmdScan,                  # scan a single file (for debugging)
-    cmdIdeTools,              # ide tools
-    cmdDef,                   # def feature (find definition for IDEs)
-    cmdRst2html,              # convert a reStructuredText file to HTML
-    cmdRst2tex,               # convert a reStructuredText file to TeX
-    cmdInteractive,           # start interactive session
-    cmdRun,                   # run the project via TCC backend
-    cmdJsonScript             # compile a .json build file
-    cmdCompileToBackend,      # compile to backend in TBackend
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
     gcUnselected, gcNone, gcBoehm, gcRegions, gcArc, gcOrc,
@@ -245,7 +255,7 @@ type
     evalTemplateCounter*: int
     evalMacroCounter*: int
     exitcode*: int8
-    cmd*: TCommands  # the command
+    cmd*: Command  # raw command parsed as enum
     cmdInput*: string  # input command
     projectIsCmd*: bool # whether we're compiling from a command input
     implicitCmd*: bool # whether some flag triggered an implicit `command`
@@ -531,7 +541,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
                             osDragonfly, osMacosx}
     else: discard
 
-proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
+proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd notin cmdDocLike + {cmdIdeTools}
 proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
 
 template compilationCachePresent*(conf: ConfigRef): untyped =
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 1ec0aa112..5ee3dc414 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -155,7 +155,7 @@ proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
       localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
   when hasFFI:
     s.cname = $s.loc.r
-  if c.config.cmd == cmdPretty and '$' notin extname:
+  if c.config.cmd == cmdNimfix and '$' notin extname:
     # note that '{.importc.}' is transformed into '{.importc: "$1".}'
     s.loc.flags.incl(lfFullExternalName)
 
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index a353278d6..ef1ceb12b 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -150,7 +150,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf cmpIgnoreCase:
     setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
   cbconf setCommand:
-    conf.command = a.getString 0
+    conf.setCommandEarly(a.getString 0)
+    # xxx move remaining logic to commands.nim or other
     let arg = a.getString 1
     incl(conf.globalOptions, optWasNimscript)
     if arg.len > 0:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 608a82715..8728af27c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -2280,7 +2280,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         result = magicsAfterOverloadResolution(c, result, flags)
   of mRunnableExamples:
     markUsed(c, n.info, s)
-    if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
+    if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList:
       when false:
         # some of this dead code was moved to `prepareExamples`
         if sfMainModule in c.module.flags:
@@ -2724,7 +2724,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
     var s = qualifiedLookUp(c, n[0], mode)
     if s != nil:
-      #if c.config.cmd == cmdPretty and n[0].kind == nkDotExpr:
+      #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr:
       #  pretty.checkUse(n[0][1].info, s)
       case s.kind
       of skMacro, skTemplate:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 0ec7140e0..a9d00d2ee 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -134,7 +134,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     e.position = int(counter)
     let symNode = newSymNode(e)
     if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil and
-        c.config.cmd != cmdDoc: # A hack to produce documentation for enum fields.
+        c.config.cmd notin cmdDocLike: # A hack to produce documentation for enum fields.
       identToReplace[] = symNode
     if e.position == 0: hasNull = true
     if result.sym != nil and sfExported in result.sym.flags:
diff --git a/drnim/drnim.nim b/drnim/drnim.nim
index d68175c83..f2a20fa62 100644
--- a/drnim/drnim.nim
+++ b/drnim/drnim.nim
@@ -1223,9 +1223,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
   var argsCount = 1
 
   config.commandLine.setLen 0
-  config.command = "check"
-  config.cmd = cmdCheck
-
+  config.setCmd cmdCheck
   while true:
     parseopt.next(p)
     case p.kind
@@ -1253,7 +1251,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
       if processArgument(pass, p, argsCount, config): break
   if pass == passCmd2:
     if {optRun, optWasNimscript} * config.globalOptions == {} and
-        config.arguments.len > 0 and config.command.normalize notin ["run", "e"]:
+        config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript}:
       rawMessage(config, errGenerated, errArgsNeedRunOption)
 
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index d438e663c..2e28a09ee 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -517,7 +517,7 @@ proc mainCommand(graph: ModuleGraph) =
   clearPasses(graph)
   registerPass graph, verbosePass
   registerPass graph, semPass
-  conf.cmd = cmdIdeTools
+  conf.setCmd cmdIdeTools
   wantMainModule(conf)
 
   if not fileExists(conf.projectFull):
@@ -666,7 +666,7 @@ else:
       clearPasses(graph)
       registerPass graph, verbosePass
       registerPass graph, semPass
-      conf.cmd = cmdIdeTools
+      conf.setCmd cmdIdeTools
       wantMainModule(conf)
 
       if not fileExists(conf.projectFull):
diff --git a/tools/nimfind.nim b/tools/nimfind.nim
index b120ccd1f..9c8247606 100644
--- a/tools/nimfind.nim
+++ b/tools/nimfind.nim
@@ -158,7 +158,7 @@ proc mainCommand(graph: ModuleGraph) =
     clearPasses(graph)
     registerPass graph, verbosePass
     registerPass graph, semPass
-    conf.cmd = cmdIdeTools
+    conf.setCmd cmdIdeTools
     wantMainModule(conf)
     setupDb(graph, dbfile)