# # # The Nimrod Compiler # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module handles the parsing of command line arguments. import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, wordrecg, parseutils proc writeCommandLineUsage*() type TCmdLinePass* = enum passCmd1, # first pass over the command line passCmd2, # second pass over the command line passPP # preprocessor called ProcessCommand() proc ProcessCommand*(switch: string, pass: TCmdLinePass) proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) # implementation const HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]\n" & "Copyright (c) 2004-2012 by Andreas Rumpf\n" const Usage = slurp"doc/basicopt.txt".replace("//", "") AdvancedUsage = slurp"doc/advopt.txt".replace("//", "") proc getCommandLineDesc(): string = result = (HelpMessage % [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) & Usage proc HelpOnError(pass: TCmdLinePass) = if pass == passCmd1: MsgWriteln(getCommandLineDesc()) quit(0) proc writeAdvancedUsage(pass: TCmdLinePass) = if pass == passCmd1: MsgWriteln(`%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) & AdvancedUsage) quit(0) proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: MsgWriteln(`%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name])) quit(0) var helpWritten: bool proc writeCommandLineUsage() = if not helpWritten: MsgWriteln(getCommandLineDesc()) helpWritten = true proc InvalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = LocalError(info, errInvalidCmdLineOption, switch) proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, info: TLineInfo) = cmd = "" var i = 0 if i < len(switch) and switch[i] == '-': inc(i) if i < len(switch) and switch[i] == '-': inc(i) while i < len(switch): case switch[i] of 'a'..'z', 'A'..'Z', '0'..'9', '_', '.': add(cmd, switch[i]) else: break inc(i) if i >= len(switch): arg = "" elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) else: InvalidCmdLineOption(pass, switch, info) proc ProcessOnOffSwitch(op: TOptions, arg: string, pass: TCmdlinePass, info: TLineInfo) = case whichKeyword(arg) of wOn: gOptions = gOptions + op of wOff: gOptions = gOptions - op else: LocalError(info, errOnOrOffExpectedButXFound, arg) proc ProcessOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdlinePass, info: TLineInfo) = case whichKeyword(arg) of wOn: gGlobalOptions = gGlobalOptions + op of wOff: gGlobalOptions = gGlobalOptions - op else: LocalError(info, errOnOrOffExpectedButXFound, arg) proc ExpectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg == "": LocalError(info, errCmdLineArgExpected, switch) proc ExpectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg != "": LocalError(info, errCmdLineNoArgExpected, switch) proc ProcessSpecificNote(arg: string, state: TSpecialWord, pass: TCmdlinePass, info: TLineInfo) = var id = "" # arg = "X]:on|off" var i = 0 var n = hintMin while i < len(arg) and (arg[i] != ']'): add(id, arg[i]) inc(i) if i < len(arg) and (arg[i] == ']'): inc(i) else: InvalidCmdLineOption(pass, arg, info) if i < len(arg) and (arg[i] in {':', '='}): inc(i) else: InvalidCmdLineOption(pass, arg, info) if state == wHint: var x = findStr(msgs.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) else: InvalidCmdLineOption(pass, arg, info) else: var x = findStr(msgs.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) else: InvalidCmdLineOption(pass, arg, info) case whichKeyword(substr(arg, i)) of wOn: incl(gNotes, n) of wOff: excl(gNotes, n) else: LocalError(info, errOnOrOffExpectedButXFound, arg) proc processCompile(filename: string) = var found = findFile(filename) if found == "": found = filename var trunc = changeFileExt(found, "") extccomp.addExternalFileToCompile(found) extccomp.addFileToLink(completeCFilePath(trunc, false)) proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = case switch.normalize of "gc": case arg.normalize of "boehm": result = contains(gGlobalOptions, optBoehmGC) of "refc": result = contains(gGlobalOptions, optRefcGC) of "none": result = gGlobalOptions * {optBoehmGC, optRefcGC} == {} else: LocalError(info, errNoneBoehmRefcExpectedButXFound, arg) of "opt": case arg.normalize of "speed": result = contains(gOptions, optOptimizeSpeed) of "size": result = contains(gOptions, optOptimizeSize) of "none": result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {} else: LocalError(info, errNoneSpeedOrSizeExpectedButXFound, arg) else: InvalidCmdLineOption(passCmd1, switch, info) proc testCompileOption*(switch: string, info: TLineInfo): bool = case switch.normalize of "debuginfo": result = contains(gGlobalOptions, optCDebug) of "compileonly", "c": result = contains(gGlobalOptions, optCompileOnly) of "nolinking": result = contains(gGlobalOptions, optNoLinking) of "nomain": result = contains(gGlobalOptions, optNoMain) of "forcebuild", "f": result = contains(gGlobalOptions, optForceFullMake) of "warnings", "w": result = contains(gOptions, optWarns) of "hints": result = contains(gOptions, optHints) of "threadanalysis": result = contains(gGlobalOptions, optThreadAnalysis) of "stacktrace": result = contains(gOptions, optStackTrace) of "linetrace": result = contains(gOptions, optLineTrace) of "debugger": result = contains(gOptions, optEndb) of "profiler": result = contains(gOptions, optProfiler) of "checks", "x": result = gOptions * checksOptions == checksOptions of "floatchecks": result = gOptions * {optNanCheck, optInfCheck} == {optNanCheck, optInfCheck} of "infchecks": result = contains(gOptions, optInfCheck) of "nanchecks": result = contains(gOptions, optNanCheck) of "objchecks": result = contains(gOptions, optObjCheck) of "fieldchecks": result = contains(gOptions, optFieldCheck) of "rangechecks": result = contains(gOptions, optRangeCheck) of "boundchecks": result = contains(gOptions, optBoundsCheck) of "overflowchecks": result = contains(gOptions, optOverflowCheck) of "linedir": result = contains(gOptions, optLineDir) of "assertions", "a": result = contains(gOptions, optAssert) of "deadcodeelim": result = contains(gGlobalOptions, optDeadCodeElim) of "run", "r": result = contains(gGlobalOptions, optRun) of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles) of "genscript": result = contains(gGlobalOptions, optGenScript) of "threads": result = contains(gGlobalOptions, optThreads) of "taintmode": result = contains(gGlobalOptions, optTaintMode) of "tlsemulation": result = contains(gGlobalOptions, optTlsEmulation) of "implicitstatic": result = contains(gOptions, optImplicitStatic) of "patterns": result = contains(gOptions, optPatterns) else: InvalidCmdLineOption(passCmd1, switch, info) proc processPath(path: string): string = result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath, "home", removeTrailingDirSep(os.getHomeDir()), "projectname", options.gProjectName, "projectpath", options.gProjectPath]) proc addPath(path: string, info: TLineInfo) = if not contains(options.searchPaths, path): lists.PrependStr(options.searchPaths, path) proc addPathRec(dir: string, info: TLineInfo) = var pos = dir.len-1 if dir[pos] in {DirSep, AltSep}: inc(pos) for k,p in os.walkDir(dir): if k == pcDir and p[pos] != '.': addPathRec(p, info) if not contains(options.searchPaths, p): Message(info, hintPath, p) lists.PrependStr(options.searchPaths, p) proc track(arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN") var line, column: int if parseUtils.parseInt(a[1], line) <= 0: LocalError(info, errInvalidNumber, a[1]) if parseUtils.parseInt(a[2], column) <= 0: LocalError(info, errInvalidNumber, a[2]) msgs.addCheckpoint(newLineInfo(a[0], line, column)) proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = var theOS: TSystemOS cpu: TSystemCPU key, val: string case switch.normalize of "path", "p": expectArg(switch, arg, pass, info) addPath(processPath(arg), info) of "recursivepath": expectArg(switch, arg, pass, info) var path = processPath(arg) addPathRec(path, info) addPath(path, info) of "nimcache": expectArg(switch, arg, pass, info) options.nimcacheDir = processPath(arg) of "out", "o": expectArg(switch, arg, pass, info) options.outFile = arg of "mainmodule", "m": expectArg(switch, arg, pass, info) gProjectName = arg gProjectFull = gProjectPath / gProjectName of "define", "d": expectArg(switch, arg, pass, info) DefineSymbol(arg) of "undef", "u": expectArg(switch, arg, pass, info) UndefSymbol(arg) of "compile": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: processCompile(arg) of "link": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: addFileToLink(arg) of "debuginfo": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCDebug) of "compileonly", "c": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCompileOnly) of "nolinking": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoLinking) of "nomain": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoMain) of "forcebuild", "f": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optForceFullMake) of "gc": expectArg(switch, arg, pass, info) case arg.normalize of "boehm": incl(gGlobalOptions, optBoehmGC) excl(gGlobalOptions, optRefcGC) DefineSymbol("boehmgc") of "refc": excl(gGlobalOptions, optBoehmGC) incl(gGlobalOptions, optRefcGC) of "none": excl(gGlobalOptions, optRefcGC) excl(gGlobalOptions, optBoehmGC) defineSymbol("nogc") else: LocalError(info, errNoneBoehmRefcExpectedButXFound, arg) of "warnings", "w": ProcessOnOffSwitch({optWarns}, arg, pass, info) of "warning": ProcessSpecificNote(arg, wWarning, pass, info) of "hint": ProcessSpecificNote(arg, wHint, pass, info) of "hints": ProcessOnOffSwitch({optHints}, arg, pass, info) of "threadanalysis": ProcessOnOffSwitchG({optThreadAnalysis}, arg, pass, info) of "stacktrace": ProcessOnOffSwitch({optStackTrace}, arg, pass, info) of "linetrace": ProcessOnOffSwitch({optLineTrace}, arg, pass, info) of "debugger": ProcessOnOffSwitch({optEndb}, arg, pass, info) if optEndb in gOptions: DefineSymbol("endb") else: UndefSymbol("endb") of "profiler": ProcessOnOffSwitch({optProfiler}, arg, pass, info) if optProfiler in gOptions: DefineSymbol("profiler") else: UndefSymbol("profiler") of "checks", "x": ProcessOnOffSwitch(checksOptions, arg, pass, info) of "floatchecks": ProcessOnOffSwitch({optNanCheck, optInfCheck}, arg, pass, info) of "infchecks": ProcessOnOffSwitch({optInfCheck}, arg, pass, info) of "nanchecks": ProcessOnOffSwitch({optNanCheck}, arg, pass, info) of "objchecks": ProcessOnOffSwitch({optObjCheck}, arg, pass, info) of "fieldchecks": ProcessOnOffSwitch({optFieldCheck}, arg, pass, info) of "rangechecks": ProcessOnOffSwitch({optRangeCheck}, arg, pass, info) of "boundchecks": ProcessOnOffSwitch({optBoundsCheck}, arg, pass, info) of "overflowchecks": ProcessOnOffSwitch({optOverflowCheck}, arg, pass, info) of "linedir": ProcessOnOffSwitch({optLineDir}, arg, pass, info) of "assertions", "a": ProcessOnOffSwitch({optAssert}, arg, pass, info) of "deadcodeelim": ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info) of "tlsemulation": ProcessOnOffSwitchG({optTlsEmulation}, arg, pass, info) of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info) of "implicitstatic": ProcessOnOffSwitch({optImplicitStatic}, arg, pass, info) of "patterns": ProcessOnOffSwitch({optPatterns}, arg, pass, info) of "opt": expectArg(switch, arg, pass, info) case arg.normalize of "speed": incl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) of "size": excl(gOptions, optOptimizeSpeed) incl(gOptions, optOptimizeSize) of "none": excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) else: LocalError(info, errNoneSpeedOrSizeExpectedButXFound, arg) of "app": expectArg(switch, arg, pass, info) case arg.normalize of "gui": incl(gGlobalOptions, optGenGuiApp) defineSymbol("executable") defineSymbol("guiapp") of "console": excl(gGlobalOptions, optGenGuiApp) defineSymbol("executable") defineSymbol("consoleapp") of "lib": incl(gGlobalOptions, optGenDynLib) excl(gGlobalOptions, optGenGuiApp) defineSymbol("library") defineSymbol("dll") of "staticlib": incl(gGlobalOptions, optGenStaticLib) excl(gGlobalOptions, optGenGuiApp) defineSymbol("library") defineSymbol("staticlib") else: LocalError(info, errGuiConsoleOrLibExpectedButXFound, arg) of "passc", "t": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg) of "passl", "l": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of "cincludes": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: cIncludes.add arg of "clibdir": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: cLibs.add arg of "clib": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: cLinkedLibs.add arg of "header": headerFile = arg incl(gGlobalOptions, optGenIndex) of "index": ProcessOnOffSwitchG({optGenIndex}, arg, pass, info) of "import": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: implicitImports.add arg of "include": expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: implicitIncludes.add arg of "listcmd": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optListCmd) of "genmapping": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenMapping) of "os": expectArg(switch, arg, pass, info) if (pass == passCmd1): theOS = platform.NameToOS(arg) if theOS == osNone: LocalError(info, errUnknownOS, arg) elif theOS != platform.hostOS: setTarget(theOS, targetCPU) condsyms.InitDefines() of "cpu": expectArg(switch, arg, pass, info) if (pass == passCmd1): cpu = platform.NameToCPU(arg) if cpu == cpuNone: LocalError(info, errUnknownCPU, arg) elif cpu != platform.hostCPU: setTarget(targetOS, cpu) condsyms.InitDefines() of "run", "r": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optRun) of "verbosity": expectArg(switch, arg, pass, info) gVerbosity = parseInt(arg) of "parallelbuild": expectArg(switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) of "version", "v": expectNoArg(switch, arg, pass, info) writeVersionInfo(pass) of "advanced": expectNoArg(switch, arg, pass, info) writeAdvancedUsage(pass) of "help", "h": expectNoArg(switch, arg, pass, info) helpOnError(pass) of "symbolfiles": ProcessOnOffSwitchG({optSymbolFiles}, arg, pass, info) of "skipcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipConfigFile) of "skipprojcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipProjConfigFile) of "skipusercfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipUserConfigFile) of "skipparentcfg": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipParentConfigFiles) of "genscript": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenScript) of "lib": expectArg(switch, arg, pass, info) libpath = processPath(arg) of "putenv": expectArg(switch, arg, pass, info) splitSwitch(arg, key, val, pass, info) os.putEnv(key, val) of "cc": expectArg(switch, arg, pass, info) setCC(arg) of "track": expectArg(switch, arg, pass, info) track(arg, info) of "suggest": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSuggest) of "def": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optDef) of "context": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optContext) of "usages": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optUsages) of "stdout": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optStdout) else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: InvalidCmdLineOption(pass, switch, info) proc ProcessCommand(switch: string, pass: TCmdLinePass) = var cmd, arg: string splitSwitch(switch, cmd, arg, pass, gCmdLineInfo) processSwitch(cmd, arg, pass, gCmdLineInfo)