summary refs log blame commit diff stats
path: root/rod/commands.nim
blob: 2be58e8c8bfc7eefb660534f46a50e153066a6ce (plain) (tree)
1
2
3
4


                               
                                         






















                                                                              
     

                                                                               


             
      
                                                
        












                                                                             

                                                     
                                                                   










                                                                           







                                                                              
                  
                                                               
                                                                         






                                                                       



                                                          








                                                                               
                                                                



                                                              

                                                                         




















































                                                                                 
                                                   


                                                                           
          
           


                                                 



                                                                  
                               







                                                                       
                                                          





                                                                              
                                                          

                                                                           
                                                               

                                                                             
                                                                 


                                                                               


                                  







                                                          
                                        


                                               
                                           




                                               
                                                          

                                        
                                
                                  

                                          

                                                         






                                                                         
                                                                  




                                                                           
                                                                    































                                                                                
                                                            
                                                    




                                                                             
                                              

                                               
 
                                               
                     
                                           
                             
                                    

                                               
                                  

                                                



                                                                               
                    


                                      
                                   


                                      

                          











































                                                      
                                                                  















                                                                        



                                                                   







                                                                             
                                                                 











                                      
                                                                    











                                        
                                                                    
























                                                                 

                                                             






                                            

                                                             




















































                                                                         
#
#
#           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
  --threads:on|off          turn support for multi-threading 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
  --debugger:on|off         turn Embedded Nimrod Debugger on|off
  -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) = 
  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 = 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: 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) + 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: 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 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: LocalError(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: LocalError(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)
  of wThreads: result = contains(gGlobalOptions, optThreads)
  else: InvalidCmdLineOption(passCmd1, switch, info)
  
proc processPath(path: string): string = 
  result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath,
    "home", removeTrailingDirSep(os.getHomeDir())])

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 processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = 
  var 
    theOS: TSystemOS
    cpu: TSystemCPU
    key, val: string
  case whichKeyword(switch)
  of wPath, wP: 
    expectArg(switch, arg, pass, info)
    addPath(processPath(arg), info)
  of wRecursivePath:
    expectArg(switch, arg, pass, info)
    var path = processPath(arg)
    addPathRec(path, info)
    addPath(path, info)
  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: LocalError(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 wThreads: ProcessOnOffSwitchG({optThreads}, 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: LocalError(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: LocalError(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: LocalError(info, errUnknownOS, arg)
      elif 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: LocalError(info, errUnknownCPU, arg)
      elif 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)