Skip to content

Commit

Permalink
cmdline: improve command processing (#16056)
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour authored Nov 26, 2020
1 parent 9f1c5f6 commit 52829fc
Show file tree
Hide file tree
Showing 18 changed files with 144 additions and 137 deletions.
2 changes: 1 addition & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 5 additions & 11 deletions compiler/cmdlinehelper.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -60,26 +58,22 @@ 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

if not self.suggestMode:
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
Expand Down
57 changes: 47 additions & 10 deletions compiler/commands.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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; " &
Expand Down
99 changes: 33 additions & 66 deletions compiler/main.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -213,49 +209,36 @@ 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.
conf.outDir = block:
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
Expand All @@ -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)

Expand Down Expand Up @@ -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"
Expand All @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions compiler/nim.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand All @@ -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])
Expand Down
2 changes: 1 addition & 1 deletion compiler/nimconf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 52829fc

Please sign in to comment.