Ticket #12: cmdLnArgs1.patch
File cmdLnArgs1.patch, 23.0 KB (added by hopscc, 16 years ago) |
---|
compined patch of above plus tests |
-
Source/Compiler.cobra
393 393 assert modules.count 394 394 _modules.addRange(modules) 395 395 return modules 396 396 397 # default invocation of runProcess - no explicit exeName and no argList 397 398 def runProcess as Process 399 return .runProcess(nil, nil) 400 401 def runProcess(exeName as String?, argList as IList<of String>?) as Process 398 402 """ 403 Run a Process passing optional explicit exeName and argList 399 404 Returns a new Process with startInfo.fileName and p.startInfo.arguments set appropriately 400 for the produced executable and the current options .405 for the produced executable and the current options and any provided arglist. 401 406 """ 402 # TODO: support args to the program with a - or -- separator403 407 p = Process() 408 if exeName 409 baseExeFileName = exeName 410 fullExeFileName = exeName 411 else 412 baseExeFileName = .baseExeFileName 413 fullExeFileName = .fullExeFileName 404 414 branch .platform 405 415 on PlatformEnum.Microsoft 406 p.startInfo.fileName = .baseExeFileName416 p.startInfo.fileName = baseExeFileName 407 417 on PlatformEnum.Novell 418 # fixup fullExeFileName 419 if not '.' in fullExeFileName 420 fullExeFileName += '.exe' 408 421 p.startInfo.fileName = 'mono' 409 422 args = '' 410 423 # mono also needs --debug when running 411 424 if .options.getDefault('debug', '') not in ['', '-'] 412 425 args += '--debug ' 413 args += '"[ .fullExeFileName]"'426 args += '"[fullExeFileName]"' 414 427 p.startInfo.arguments = args 415 428 else 416 429 throw FallThroughException(.platform) 430 431 if argList 432 args = p.startInfo.arguments ? '' 433 args += Utils.join(' ', argList) 434 p.startInfo.arguments = args 435 417 436 return p 418 437 419 438 ## -
Source/CommandLine.cobra
201 201 }, 202 202 { 203 203 'name': 'reference', 204 'synonyms': ['r'], 204 'synonyms': ['r'], # TODO: lose 'r' ->'ref'. 205 205 'isAccumulator': true, 206 206 'description': 'Add a DLL reference.', 207 207 'args': 'Some.dll', … … 220 220 # 'args': ':Qualified.Type.Name', 221 221 # }, 222 222 { 223 'name': 'run', 223 'name': 'run', # TODO: add Synonym 'r' after change 'reference' switch 224 224 'description': 'Runs the Cobra program. This is the default behavior if specify any Cobra source files.', 225 225 'type': 'main', 226 226 }, 227 228 { # TODO: Merge this into the above (-run) 229 'name': 'exec-args', # TODO: -> 'run' after add 'r' synonym to 'run'. 230 'synonyms': ['X'], 231 'description': 'Remaining args are filename to execute and args to pass to it.', 232 'eg': '-X echo arg1 arg2 argThree', 233 'type': 'exe-args-list', #'main' 234 }, 235 236 { 237 'name': 'run-args', 238 'synonyms': ['-'], #'--' 239 'description': 'Remaining args are to be passed to executable.', 240 'eg': '-- arg1 arg2 argThree', 241 'type': 'args-list', 242 }, 243 227 244 { 228 245 'name': 'sharp-compiler', 229 246 'description': 'Specify the path to the backend C# compiler.', … … 287 304 288 305 var _options = Options() 289 306 var _pathList as List<of String>? 307 var _runArgs as IList<of String>? # nil or args set for running exe 308 var _exeFileName as String? # Name specified for exe to run 290 309 var _htmlWriter as HtmlWriter? 291 310 292 311 var _compiler as Compiler? … … 389 408 if _htmlWriter 390 409 _htmlWriter.writeHtml('</body></html>[_htmlWriter.newLine]') 391 410 392 def isOptionSpecRestrictionViolated(optionSpec as Dictionary<of String, Object>) as bool393 """394 Returns true if the option spec has a 'restriction' key and the check against that restriction is true.395 """396 if optionSpec.containsKey('restriction')397 branch optionSpec['restriction'] to String398 on 'mono-only'399 return not CobraCore.isRunningOnMono400 return false401 402 411 def parseArgs(args as IList<of String>, options as out Options?, paths as out List<of String>?) 403 412 """ 404 413 Parse command line arguments. … … 407 416 ensure 408 417 options 409 418 paths 410 body 419 body 411 420 optionPrefix = '-' 412 valuePrefix = c':'413 421 if not args.count 414 422 options = Options() 415 423 options.add('help', true) … … 421 429 synToName = Dictionary<of String, String>() 422 430 # ^ maps synonyms to their full names 423 431 synList = List<of String>() 424 for d in _optionSpecs 425 if .isOptionSpecRestrictionViolated(d) 426 continue 427 specDict[d['name'] to String] = d 428 if d.containsKey('synonyms') 429 syns = d['synonyms'] to System.Collections.IList 430 for syn as String in syns 431 assert not specDict.containsKey(syn) 432 specDict[syn] = d 433 synToName[syn] = d['name'] to String 434 synList.add(syn) 435 if not d.containsKey('type') 436 d.add('type', 'string') 432 .initSynonyms( specDict, synToName, synList) 437 433 438 434 # set up initial valueDict 439 435 valueDict = Dictionary<of String, Object>() 440 436 if Utils.isDevMachine 441 437 valueDict['reveal-internal-exceptions'] = true # this is a specially computed default, but can still be overridden on the command line 442 438 439 value = 'no-value' to dynamic 440 valueStr = 'no-value' 443 441 fileList = List<of String>() 444 value = 'no-value' to dynamic445 442 mainOptions = List<of String>() 446 443 didSpecify = Dictionary<of String, bool>() # CC: could just be a Set 444 argn=0 447 445 for arg in args 446 argn += 1 # next arg after current 448 447 if arg.trim.length == 0 449 448 continue 450 if arg.startsWith(optionPrefix) 451 isOption = true 452 while arg.startsWith(optionPrefix) 453 arg = arg[1:] 454 else 455 isOption = false 449 450 isOption = arg.startsWith(optionPrefix) 456 451 if isOption 457 parts = arg.split(@[valuePrefix], 2) 458 if parts.length == 1 459 name = parts[0] 460 if name.endsWith('+') 461 name = name[:-1] 462 valueStr = 'on' 463 else if name.endsWith('-') 464 name = name[:-1] 465 valueStr = 'off' 466 else 467 valueStr = 'on' 468 else 469 assert parts.length == 2 470 name = parts[0] 471 valueStr = parts[1] 472 assert name.length, parts 473 name = Utils.getSS(synToName to passthrough, name, name) to ! 474 if not specDict.containsKey(name) 475 msg = 'No such option "[name]".' 476 if name.contains('=') 477 msg += ' If you meant to specify an option value, use colon (:) instead of equals (=).' 478 .error(msg) 452 name = .getOptionParts(arg, optionPrefix, synToName, specDict, out valueStr) 479 453 spec = specDict[name] 480 if Utils.getSB(spec to passthrough, 'isAccumulator', false) 481 # accumulators are always treated as strings. TODO: assert that 482 if valueDict.containsKey(name) 483 (valueDict[name] to System.Collections.IList).add(valueStr to passthrough) 484 else 485 valueDict[name] = [valueStr] 486 didSpecify[name] = true 487 else 488 cannotProcess = false 489 if name=='debug' 490 # special case 491 if valueStr=='pdbonly' or valueStr=='full' 492 value = valueStr 493 else 494 try 495 value = .boolForString(valueStr) 496 catch FormatException 497 cannotProcess = true 498 success 499 value = if(value, '+', '-') 500 else 501 if spec['type'] == 'main' 502 mainOptions.add(name) 503 value = true 504 else 505 possible = .interpretValue(valueStr, spec) 506 if possible is not nil 507 value = possible 508 else 509 cannotProcess = true 510 if cannotProcess 511 .error('Cannot process value "[valueStr]" for option "[name]".') 512 valueDict[name] = value 513 didSpecify[name] = true 454 455 if .isAccumulatorOption(spec) 456 .accumulateOptionValue(name, valueStr, valueDict, didSpecify) 457 continue 458 459 cannotProcess = argsDone = false 460 value = .processToValue(name, valueStr, spec, mainOptions, args, argn, out cannotProcess, out argsDone ) 461 if argsDone 462 break # slurpt up rest of args 463 if cannotProcess 464 .error('Cannot process value "[valueStr]" for option "[name]".') 465 valueDict[name] = value 466 didSpecify[name] = true 514 467 else # not isOption 515 if File.exists(arg) 516 fileList.add(arg) 517 else if File.exists(arg+'.cobra') 518 fileList.add(arg+'.cobra') 519 else if Directory.exists(arg) 520 fileList.add(arg) 521 else 522 msg = 'Cannot find "[arg]" as a file.' 523 if arg.startsWith('/') 524 msg += ' If you meant to specify an option, use dash (-) instead of slash (/).' 525 .error(msg) 468 if arg.startsWith('/') 469 errHint = ' If you meant to specify an option, use dash (-) instead of slash (/).' 470 .processAsFile(arg, fileList, errHint) 526 471 527 # handle synonyms 528 for syn in synList 529 if valueDict.containsKey(syn) 530 valueDict[synToName[syn]] = valueDict[syn] 531 valueDict.remove(syn) 472 .handleSynonyms(synList, synToName, valueDict) 473 .addInDefaults(valueDict) 532 474 533 # add in defaults534 for d in _optionSpecs535 defaultName = d['name'] to String536 if not valueDict.containsKey(defaultName) and d.containsKey('default')537 defaultValue = .interpretValue(d['default'] to String, d) to !538 if .verbosity539 print 'Setting option "[defaultName]" to default value [defaultValue].'540 valueDict[defaultName] = defaultValue541 542 475 # TODO: make the option names case-insensitive 543 476 544 477 # check for more than one main option 545 478 if mainOptions.count > 1 546 479 .error('Cannot have these main options at the same time: [Utils.join(", ", mainOptions)]') 547 480 548 # unpack certain options into specific class fields 549 if valueDict.containsKey('verbosity') 550 _verbosity = valueDict['verbosity'] to int 551 if not valueDict.containsKey('timeit') and valueDict.containsKey('testify') 552 valueDict['timeit'] = true 553 if valueDict.containsKey('timeit') 554 CobraMain.willTimeIt = valueDict['timeit'] to bool 555 if valueDict.containsKey('files') 556 for fileName as String in valueDict['files'] to System.Collections.IList 557 try 558 for line in File.readAllLines(fileName) 559 line = line.trim 560 if line.length==0 or line.startsWith('#') 561 continue 562 # TODO: dup'ed above 563 arg = line 564 if File.exists(arg) 565 fileList.add(arg) 566 else if File.exists(arg+'.cobra') 567 fileList.add(arg+'.cobra') 568 else if Directory.exists(arg) 569 fileList.add(arg) 570 else 571 msg = 'Cannot find "[arg]" as a file.' 572 #if arg.startsWith('/') 573 # msg += ' If you meant to specify an option, use dash (-) instead of slash (/).' 574 .error(msg) 575 # end dup 576 catch IOException 577 .error('Cannot open file "[fileName]".') 481 .unpackOptions(valueDict, fileList) 578 482 579 483 # set the out parameters 580 484 options = Options(valueDict) … … 584 488 585 489 .computeArgImplications(options to !) 586 490 491 492 def getOptionParts(arg as String, _ 493 optionPrefix as String, _ 494 synToName as Dictionary<of String, String>, _ 495 specDict as Dictionary<of String, Dictionary<of String, Object>>, _ 496 valueStr as out String) as String 497 arg = .fixOptionArg(arg, optionPrefix) 498 # [name, valueStr] = .splitOption(arg) 499 l = .splitOption(arg) 500 name = l[0] 501 valueStr = l[1] 502 name = .validateOptionName(name, synToName, specDict) 503 return name 504 505 506 def fixOptionArg(arg as String, optionPrefix as String) as String 507 """ 508 Strip any leading optionPrefix chars and possibly adjust arg 509 """ 510 while arg.startsWith(optionPrefix) 511 arg = arg[1:] 512 if not arg.length # '--' 513 arg='run-args' 514 return arg 515 516 def splitOption(arg as String) as IList<of String> 517 """ 518 Split option into name and valueStr returned as a 2 element array 519 """ 520 valuePrefix = c':' 521 parts = arg.split(@[valuePrefix], 2) 522 if parts.length == 1 523 name = parts[0] 524 if name.endsWith('+') 525 name = name[:-1] 526 valueStr = 'on' 527 else if name.endsWith('-') 528 name = name[:-1] 529 valueStr = 'off' 530 else 531 valueStr = 'on' 532 else 533 assert parts.length == 2 534 name = parts[0] 535 valueStr = parts[1] 536 assert parts, name.length 537 #assert valueStr.length 538 return [name, valueStr] 539 540 def isOptionSpecRestrictionViolated(optionSpec as Dictionary<of String, Object>) as bool 541 """ 542 Returns true if the option spec has a 'restriction' key and the check against 543 that restriction is true. 544 """ 545 if optionSpec.containsKey('restriction') 546 branch optionSpec['restriction'] to String 547 on 'mono-only' 548 return not CobraCore.isRunningOnMono 549 return false 550 551 def initSynonyms(specDict as Dictionary<of String, Dictionary<of String, Object>>, _ 552 synToName as Dictionary<of String, String>, _ 553 synList as List<of String>) 554 """ 555 Initialise supporting data structures for handling option synonyms 556 """ 557 for d in _optionSpecs 558 if .isOptionSpecRestrictionViolated(d) 559 continue 560 specDict[d['name'] to String] = d 561 if d.containsKey('synonyms') 562 syns = d['synonyms'] to System.Collections.IList 563 for syn as String in syns 564 assert not specDict.containsKey(syn) 565 specDict[syn] = d 566 synToName[syn] = d['name'] to String 567 synList.add(syn) 568 if not d.containsKey('type') 569 d.add('type', 'string') 570 571 def validateOptionName( name as String, _ 572 synToName as Dictionary<of String, String>, _ 573 specDict as Dictionary<of String, Dictionary<of String, Object>> _ 574 ) as String 575 """ 576 Ensure the given name exists as an option name or synonym mappableto an option name; 577 return the canonical name for the option/synonym 578 """ 579 name = Utils.getSS(synToName to passthrough, name, name) to ! 580 if not specDict.containsKey(name) 581 msg = 'No such option "[name]".' 582 if name.contains('=') 583 msg += ' If you meant to specify an option value, use colon (:) instead of equals (=).' 584 .error(msg) 585 return name 586 587 def isAccumulatorOption(spec as Dictionary<of String,Object>) as bool 588 return Utils.getSB(spec to passthrough, 'isAccumulator', false) 589 590 def accumulateOptionValue(name as String, valueStr as String, _ 591 valueDict as Dictionary<of String, Object>, _ 592 didSpecify as Dictionary<of String, bool>) 593 # accumulators are always treated as strings. TODO: assert that 594 if valueDict.containsKey(name) 595 (valueDict[name] to System.Collections.IList).add(valueStr to passthrough) 596 else 597 valueDict[name] = [valueStr] 598 didSpecify[name] = true 599 600 601 def fixupDebug(valueStr as String, cannotProcess as out bool) as String 602 cannotProcess = false 603 if valueStr == 'pdbonly' or valueStr == 'full' 604 return valueStr 605 606 value = 'no-value' 607 try 608 b = .boolForString(valueStr) 609 catch FormatException 610 cannotProcess = true 611 success 612 value = if(b, '+', '-') 613 return value 614 615 def processToValue(name as String, _ 616 valueStr as String, _ 617 spec as Dictionary<of String, Object>,_ 618 mainOptions as List<of String>, _ 619 argList as IList<of String>, _ 620 argn as int, _ 621 cannotProcess as out bool, _ 622 argsDone as out bool ) as dynamic 623 value as dynamic = 'no-value' 624 argsDone = false 625 cannotProcess = false 626 627 if name == 'debug' # special case 628 return .fixupDebug(valueStr, out cannotProcess ) 629 630 t = spec['type'] to String 631 branch t 632 on 'main' 633 mainOptions.add(name) 634 value = true 635 on 'args-list' 636 # remainder of args are for execution of exe file 637 _runArgs = argList[argn:] 638 argsDone = true 639 on 'exe-args-list' 640 # remainder of args are exe file and then args for it 641 if argList.count <= argn 642 .error("exe-args option must have at least one arg following") 643 # TODO: fallback to default - use compile name as exe and run with no args 644 _exeFileName = argList[argn] 645 _runArgs = argList[argn+1:] 646 argsDone = true 647 else 648 possible = .interpretValue(valueStr, spec) 649 if possible is not nil 650 value = possible 651 else 652 cannotProcess = true 653 return value 654 587 655 656 def handleSynonyms(synList as List<of String>, _ 657 synToName as Dictionary<of String, String>, _ 658 valueDict as Dictionary<of String, Object>) 659 for syn in synList 660 if valueDict.containsKey(syn) 661 valueDict[synToName[syn]] = valueDict[syn] 662 valueDict.remove(syn) 663 664 def addInDefaults(valueDict as Dictionary<of String, Object>) 665 for d in _optionSpecs 666 defaultName = d['name'] to String 667 if not valueDict.containsKey(defaultName) and d.containsKey('default') 668 defaultValue = .interpretValue(d['default'] to String, d) to ! 669 if .verbosity 670 print 'Setting option "[defaultName]" to default value [defaultValue].' 671 valueDict[defaultName] = defaultValue 672 673 def unpackOptions(valueDict as Dictionary<of String, Object>, _ 674 fileList as List<of String>) 675 """ 676 Unpack certain magic options into specific class fields 677 """ 678 if valueDict.containsKey('verbosity') 679 _verbosity = valueDict['verbosity'] to int 680 681 if not valueDict.containsKey('timeit') and valueDict.containsKey('testify') 682 valueDict['timeit'] = true 683 if valueDict.containsKey('timeit') 684 CobraMain.willTimeIt = valueDict['timeit'] to bool 685 686 if valueDict.containsKey('files') 687 fileNamesList = valueDict['files'] to System.Collections.IList 688 .processFilesFile( fileNamesList, fileList) 689 690 691 def processFilesFile(fileNamesList as IList, fileList as List<of String>) 692 """ 693 Treat entries in fileNamesList as names of files containing file 694 names to compile, read and validate names and add into fileList 695 """ 696 for fileName as String in fileNamesList 697 try 698 for line in File.readAllLines(fileName) 699 line = line.trim 700 if line.length==0 or line.startsWith('#') 701 continue 702 .processAsFile(line, fileList, nil) 703 catch IOException 704 .error('Cannot open file "[fileName]".') 705 706 def processAsFile(arg as String, fileList as List<of String>, _ 707 errHint as String?) 708 """ 709 Validate arg as filename and on success add into fileList 710 """ 711 if File.exists(arg) 712 fileList.add(arg) 713 else if File.exists(arg+'.cobra') 714 fileList.add(arg+'.cobra') 715 else if Directory.exists(arg) 716 fileList.add(arg) 717 else 718 msg = 'Cannot find "[arg]" as a file.' 719 if errHint 720 msg += errHint 721 .error(msg) 722 588 723 def computeArgImplications(options as Options) 589 724 if options.getDefault('target', '') == 'lib' and not options.isSpecified('compile') 590 725 options['compile'] = true … … 596 731 options['include-nil-checks'] = false 597 732 options['include-tests'] = false 598 733 options['optimize'] = true 599 734 600 735 def interpretValue(valueStr as String, spec as Dictionary<of String, Object>) as dynamic? 601 736 value as dynamic? 602 737 branch spec['type'] to String … … 729 864 if c.errors.count 730 865 print 'Not running due to errors above.' 731 866 else 732 p = c.runProcess 867 p = c.runProcess(_exeFileName, _runArgs) 733 868 if _verbosity >= 1 734 869 print 'Running: [p.startInfo.fileName] [p.startInfo.arguments]' 735 870 print .verboseLineSeparator … … 841 976 first = false 842 977 else 843 978 print ' Example: -[spec["name"]]:[spec["example"]]' 979 if spec.containsKey('eg') #Verbatim example line 980 print ' e.g. [spec["eg"]]' 844 981 845 982 def doAbout 846 983 # CC: multiline string -
Tests/700-command-line/700-run-args.cobra
1 # Test compile+run execution specifying args to run compiled prog with 2 class Test 3 def main is shared 4 p as System.Diagnostics.Process? 5 cobraPath = CobraCore.findCobraExe 6 assert cobraPath 7 if cobraPath 8 cmdln = "702-echo -- argA argB argC" 9 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 10 #print output 11 assert 'Unhandled Exception' not in output 12 assert 'argA argB argC' in output 13 assert p.exitCode == 0 14 15 cmdln1 = "702-echo -run-args arg1A arg1B arg1C" 16 output = CobraCore.runCobraExe(cobraPath, cmdln1, out p) 17 #print output 18 assert 'Unhandled Exception' not in output 19 assert 'arg1A arg1B arg1C' in output 20 assert p.exitCode == 0 21 -
Tests/700-command-line/702-echo.cobra
1 # .skip. 2 3 # used by 700-run-args.cobra 4 5 class Echo 6 7 def main is shared 8 args = CobraCore.commandLineArgs 9 sep='' 10 for a in args[1:] 11 print '[sep][a]' stop 12 sep=' ' 13 print -
Tests/700-command-line/703-exec-args.cobra
1 # Test execution specifying cmdln args giving exe and args after compile 2 # currently -exec-args/X Fixup when change cmdline handling 3 class ExecArgs 4 def main is shared 5 p as System.Diagnostics.Process? 6 cobraPath = CobraCore.findCobraExe 7 assert cobraPath 8 if cobraPath 9 cmdln = "702-echo -exec-args 702-echo argA argB argC" 10 # chg -exec to -r/-run when change cmdline handling 11 output = CobraCore.runCobraExe(cobraPath, cmdln, out p) 12 #print output 13 assert 'Unhandled Exception' not in output 14 assert 'argA argB argC' in output 15 assert p.exitCode == 0 16 17 cmdln1 = "702-echo -X 110-hello AnyOldArgs AnyOldargs anyAnyOldArgs" 18 output = CobraCore.runCobraExe(cobraPath, cmdln1, out p) 19 #print output 20 assert 'Unhandled Exception' not in output 21 assert 'Hello' in output 22 assert 'AnyOld' not in output 23 assert p.exitCode == 0 24 -
Developer/IntermediateReleaseNotes.text
2 2 3 3 * Added a new built-in doc tool accessible via "cobra -doc ...". The documentation is generated to local HTML files using your declarations, doc strings, contracts, etc. 4 4 5 * Added support for commandline option specifying args to pass to compiled executable (-run-args) 6 and option (-exec-args currently) to explicitly specify both executable and args to run (ticket#12) 7 8 * Added a new built-in doc tool accessible via "cobra -doc ...". The documentation is generated to local HTML files using your declarations, doc strings, contracts, etc. 9 5 10 * Add support for specifying unsigned integers as Hex literals 6 11 e.g. 0x7f 0x7f_8 0x7Fu16 0x7F_u32 7 12