# # # The Nimrod Compiler # (c) Copyright 2011 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 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-2011 by Andreas Rumpf\n" const Usage = """ Usage: nimrod command [options] inputfile [arguments] Command: compile, c compile project with default code generator (C) compileToC, cc compile project with C code generator doc generate the documentation for inputfile rst2html converts a reStructuredText file to HTML rst2tex converts a reStructuredText file to TeX Arguments: arguments are passed to the program being run (if --run option is selected) Options: -p, --path:PATH add path to search paths -o, --out:FILE set the output filename -d, --define:SYMBOL define a conditional symbol -u, --undef:SYMBOL undefine a conditional symbol -f, --forceBuild force rebuilding of all modules --stackTrace:on|off turn stack tracing on|off --lineTrace:on|off turn line tracing on|off --debugger:on|off turn Embedded Nimrod Debugger on|off -x, --checks:on|off turn all runtime checks on|off --objChecks:on|off turn obj conversion checks on|off --fieldChecks:on|off turn case variant field checks on|off --rangeChecks:on|off turn range checks on|off --boundChecks:on|off turn bound checks on|off --overflowChecks:on|off turn int over-/underflow checks on|off -a, --assertions:on|off turn assertions on|off --floatChecks:on|off turn all floating point (NaN/Inf) checks on|off --nanChecks:on|off turn NaN checks on|off --infChecks:on|off turn Inf checks on|off --deadCodeElim:on|off whole program dead code elimination on|off --opt:none|speed|size optimize not at all or for speed|size --app:console|gui|lib generate a console|GUI application|dynamic library -r, --run run the compiled program with given arguments --advanced show advanced command line switches -h, --help show this help """ AdvancedUsage = """ Advanced commands: compileToOC, oc compile project to Objective C code run run the project (with Tiny C backend; buggy!) pretty pretty print the inputfile genDepend generate a DOT file containing the module dependency graph listDef list all defined conditionals and exit check checks the project for syntax and semantic parse parses a single file (for debugging Nimrod) Advanced options: -w, --warnings:on|off turn all warnings on|off --warning[X]:on|off turn specific warning X on|off --hints:on|off turn all hints on|off --hint[X]:on|off turn specific hint X on|off --lib:PATH set the system library path -c, --compileOnly compile only; do not assemble or link --noLinking compile but do not link --noMain do not generate a main procedure --genScript generate a compile script (in the 'nimcache' subdirectory named 'compile_$project$scriptext') --os:SYMBOL set the target operating system (cross-compilation) --cpu:SYMBOL set the target processor (cross-compilation) --debuginfo enables debug information -t, --passc:OPTION pass an option to the C compiler -l, --passl:OPTION pass an option to the linker --genMapping generate a mapping file containing (Nimrod, mangled) identifier pairs --lineDir:on|off generation of #line directive on|off --checkpoints:on|off turn checkpoints on|off; for debugging Nimrod --skipCfg do not read the general configuration file --skipProjCfg do not read the project's configuration file --gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC --index:FILE use FILE to generate a documenation index file --putenv:key=value set an environment variable --listCmd list the commands used to execute external programs --parallelBuild=0|1|... perform a parallel build value = number of processors (0 for auto-detect) --verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default) -v, --version show detailed version information """ proc getCommandLineDesc(): string = result = `%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) & Usage var helpWritten: bool # BUGFIX 19 versionWritten: bool advHelpWritten: bool proc HelpOnError(pass: TCmdLinePass) = if (pass == passCmd1) and not helpWritten: # BUGFIX 19 MessageOut(getCommandLineDesc()) helpWritten = true quit(0) proc writeAdvancedUsage(pass: TCmdLinePass) = if (pass == passCmd1) and not advHelpWritten: # BUGFIX 19 MessageOut(`%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) & AdvancedUsage) advHelpWritten = true helpWritten = true quit(0) proc writeVersionInfo(pass: TCmdLinePass) = if (pass == passCmd1) and not versionWritten: versionWritten = true helpWritten = true messageOut(`%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name, cpu[platform.hostCPU].name])) quit(0) proc writeCommandLineUsage() = if not helpWritten: messageOut(getCommandLineDesc()) helpWritten = true proc InvalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = liMessage(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 = copy(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: liMessage(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: liMessage(info, errOnOrOffExpectedButXFound, arg) proc ExpectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg == "": liMessage(info, errCmdLineArgExpected, switch) proc ExpectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if arg != "": liMessage(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) + 0) and (arg[i] != ']'): add(id, arg[i]) inc(i) if (i < len(arg) + 0) and (arg[i] == ']'): inc(i) else: InvalidCmdLineOption(pass, arg, info) if (i < len(arg) + 0) 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(copy(arg, i)) of wOn: incl(gNotes, n) of wOff: excl(gNotes, n) else: liMessage(info, errOnOrOffExpectedButXFound, arg) proc processPath(path: string): string = result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath]) 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 whichKeyword(switch) of wGC: case whichKeyword(arg) of wBoehm: result = contains(gGlobalOptions, optBoehmGC) of wRefc: result = contains(gGlobalOptions, optRefcGC) of wNone: result = gGlobalOptions * {optBoehmGC, optRefcGC} == {} else: liMessage(info, errNoneBoehmRefcExpectedButXFound, arg) of wOpt: case whichKeyword(arg) of wSpeed: result = contains(gOptions, optOptimizeSpeed) of wSize: result = contains(gOptions, optOptimizeSize) of wNone: result = gOptions * {optOptimizeSpeed, optOptimizeSize} == {} else: liMessage(info, errNoneSpeedOrSizeExpectedButXFound, arg) else: InvalidCmdLineOption(passCmd1, switch, info) proc testCompileOption*(switch: string, info: TLineInfo): bool = case whichKeyword(switch) of wDebuginfo: result = contains(gGlobalOptions, optCDebug) of wCompileOnly, wC: result = contains(gGlobalOptions, optCompileOnly) of wNoLinking: result = contains(gGlobalOptions, optNoLinking) of wNoMain: result = contains(gGlobalOptions, optNoMain) of wForceBuild, wF: result = contains(gGlobalOptions, optForceFullMake) of wWarnings, wW: result = contains(gOptions, optWarns) of wHints: result = contains(gOptions, optHints) of wCheckpoints: result = contains(gOptions, optCheckpoints) of wStackTrace: result = contains(gOptions, optStackTrace) of wLineTrace: result = contains(gOptions, optLineTrace) of wDebugger: result = contains(gOptions, optEndb) of wProfiler: result = contains(gOptions, optProfiler) of wChecks, wX: result = gOptions * checksOptions == checksOptions of wFloatChecks: result = gOptions * {optNanCheck, optInfCheck} == {optNanCheck, optInfCheck} of wInfChecks: result = contains(gOptions, optInfCheck) of wNanChecks: result = contains(gOptions, optNanCheck) of wObjChecks: result = contains(gOptions, optObjCheck) of wFieldChecks: result = contains(gOptions, optFieldCheck) of wRangeChecks: result = contains(gOptions, optRangeCheck) of wBoundChecks: result = contains(gOptions, optBoundsCheck) of wOverflowChecks: result = contains(gOptions, optOverflowCheck) of wLineDir: result = contains(gOptions, optLineDir) of wAssertions, wA: result = contains(gOptions, optAssert) of wDeadCodeElim: result = contains(gGlobalOptions, optDeadCodeElim) of wRun, wR: result = contains(gGlobalOptions, optRun) of wSymbolFiles: result = contains(gGlobalOptions, optSymbolFiles) of wGenScript: result = contains(gGlobalOptions, optGenScript) else: InvalidCmdLineOption(passCmd1, switch, info) proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = var theOS: TSystemOS cpu: TSystemCPU key, val, path: string case whichKeyword(switch) of wPath, wP: expectArg(switch, arg, pass, info) path = processPath(arg) if not contains(options.searchPaths, path): lists.PrependStr(options.searchPaths, path) #discard lists.IncludeStr(options.searchPaths, path) of wOut, wO: expectArg(switch, arg, pass, info) options.outFile = arg of wDefine, wD: expectArg(switch, arg, pass, info) DefineSymbol(arg) of wUndef, wU: expectArg(switch, arg, pass, info) UndefSymbol(arg) of wCompile: expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: processCompile(arg) of wLink: expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: addFileToLink(arg) of wDebuginfo: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCDebug) of wCompileOnly, wC: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optCompileOnly) of wNoLinking: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoLinking) of wNoMain: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optNoMain) of wForceBuild, wF: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optForceFullMake) of wGC: expectArg(switch, arg, pass, info) case whichKeyword(arg) of wBoehm: incl(gGlobalOptions, optBoehmGC) excl(gGlobalOptions, optRefcGC) DefineSymbol("boehmgc") of wRefc: excl(gGlobalOptions, optBoehmGC) incl(gGlobalOptions, optRefcGC) of wNone: excl(gGlobalOptions, optRefcGC) excl(gGlobalOptions, optBoehmGC) defineSymbol("nogc") else: liMessage(info, errNoneBoehmRefcExpectedButXFound, arg) of wWarnings, wW: ProcessOnOffSwitch({optWarns}, arg, pass, info) of wWarning: ProcessSpecificNote(arg, wWarning, pass, info) of wHint: ProcessSpecificNote(arg, wHint, pass, info) of wHints: ProcessOnOffSwitch({optHints}, arg, pass, info) of wCheckpoints: ProcessOnOffSwitch({optCheckpoints}, arg, pass, info) of wStackTrace: ProcessOnOffSwitch({optStackTrace}, arg, pass, info) of wLineTrace: ProcessOnOffSwitch({optLineTrace}, arg, pass, info) of wDebugger: ProcessOnOffSwitch({optEndb}, arg, pass, info) if optEndb in gOptions: DefineSymbol("endb") else: UndefSymbol("endb") of wProfiler: ProcessOnOffSwitch({optProfiler}, arg, pass, info) if optProfiler in gOptions: DefineSymbol("profiler") else: UndefSymbol("profiler") of wChecks, wX: ProcessOnOffSwitch(checksOptions, arg, pass, info) of wFloatChecks: ProcessOnOffSwitch({optNanCheck, optInfCheck}, arg, pass, info) of wInfChecks: ProcessOnOffSwitch({optInfCheck}, arg, pass, info) of wNanChecks: ProcessOnOffSwitch({optNanCheck}, arg, pass, info) of wObjChecks: ProcessOnOffSwitch({optObjCheck}, arg, pass, info) of wFieldChecks: ProcessOnOffSwitch({optFieldCheck}, arg, pass, info) of wRangeChecks: ProcessOnOffSwitch({optRangeCheck}, arg, pass, info) of wBoundChecks: ProcessOnOffSwitch({optBoundsCheck}, arg, pass, info) of wOverflowChecks: ProcessOnOffSwitch({optOverflowCheck}, arg, pass, info) of wLineDir: ProcessOnOffSwitch({optLineDir}, arg, pass, info) of wAssertions, wA: ProcessOnOffSwitch({optAssert}, arg, pass, info) of wDeadCodeElim: ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info) of wOpt: expectArg(switch, arg, pass, info) case whichKeyword(arg) of wSpeed: incl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) of wSize: excl(gOptions, optOptimizeSpeed) incl(gOptions, optOptimizeSize) of wNone: excl(gOptions, optOptimizeSpeed) excl(gOptions, optOptimizeSize) else: liMessage(info, errNoneSpeedOrSizeExpectedButXFound, arg) of wApp: expectArg(switch, arg, pass, info) case whichKeyword(arg) of wGui: incl(gGlobalOptions, optGenGuiApp) defineSymbol("guiapp") of wConsole: excl(gGlobalOptions, optGenGuiApp) of wLib: incl(gGlobalOptions, optGenDynLib) excl(gGlobalOptions, optGenGuiApp) defineSymbol("library") else: liMessage(info, errGuiConsoleOrLibExpectedButXFound, arg) of wListDef: expectNoArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: condsyms.listSymbols() of wPassC, wT: expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addCompileOption(arg) of wPassL, wL: expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: extccomp.addLinkOption(arg) of wIndex: expectArg(switch, arg, pass, info) if pass in {passCmd2, passPP}: gIndexFile = arg of wImport: expectArg(switch, arg, pass, info) options.addImplicitMod(arg) of wListCmd: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optListCmd) of wGenMapping: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenMapping) of wOS: expectArg(switch, arg, pass, info) if (pass == passCmd1): theOS = platform.NameToOS(arg) if theOS == osNone: liMessage(info, errUnknownOS, arg) if theOS != platform.hostOS: setTarget(theOS, targetCPU) incl(gGlobalOptions, optCompileOnly) condsyms.InitDefines() of wCPU: expectArg(switch, arg, pass, info) if (pass == passCmd1): cpu = platform.NameToCPU(arg) if cpu == cpuNone: liMessage(info, errUnknownCPU, arg) if cpu != platform.hostCPU: setTarget(targetOS, cpu) incl(gGlobalOptions, optCompileOnly) condsyms.InitDefines() of wRun, wR: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optRun) of wVerbosity: expectArg(switch, arg, pass, info) gVerbosity = parseInt(arg) of wParallelBuild: expectArg(switch, arg, pass, info) gNumberOfProcessors = parseInt(arg) of wVersion, wV: expectNoArg(switch, arg, pass, info) writeVersionInfo(pass) of wAdvanced: expectNoArg(switch, arg, pass, info) writeAdvancedUsage(pass) of wHelp, wH: expectNoArg(switch, arg, pass, info) helpOnError(pass) of wSymbolFiles: ProcessOnOffSwitchG({optSymbolFiles}, arg, pass, info) of wSkipCfg: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipConfigFile) of wSkipProjCfg: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optSkipProjConfigFile) of wGenScript: expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenScript) of wLib: expectArg(switch, arg, pass, info) libpath = processPath(arg) of wPutEnv: expectArg(switch, arg, pass, info) splitSwitch(arg, key, val, pass, info) os.putEnv(key, val) of wCC: expectArg(switch, arg, pass, info) setCC(arg) 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 info: TLineInfo info = newLineInfo("command line", 1, 1) splitSwitch(switch, cmd, arg, pass, info) ProcessSwitch(cmd, arg, pass, info)