Changeset 1667

Show
Ignore:
Timestamp:
10/14/08 00:30:55 (3 months ago)
Author:
Chuck.Esterbrook
Message:

Added support for a new compiler directive 'args' that takes command line options and applies them to the compilation environment from that point on.

%% args -target:lib -embed-run-time:no

%% args -compile -reference:System.Windows.Forms -target:winexe

ticket:35
credit:hopscc

Location:
cobra/trunk
Files:
6 added
6 modified

Legend:

Unmodified
Added
Removed
  • cobra/trunk/Developer/IntermediateReleaseNotes.text

    r1664 r1667  
    11Post 0.8 
    22 
     3* Added support for a new compiler directive 'args' that takes command line options and applies them to the compilation environment from that point on. 
     4.code 
     5    %% args -target:lib -embed-run-time:no 
     6    %% args -compile -reference:System.Windows.Forms -target:winexe 
     7      
    38* Added support for "extended initializers" which allow you to set properties of the object in the same call being used to create the object: 
    49.code 
  • cobra/trunk/Source/CobraParser.cobra

    r1658 r1667  
    544544                                # TODO: throw AssertException(SourceSite sourceSite, object[] expressions, object thiss, object info) 
    545545                                assert false, IdentifierExpr(token, 'throw') 
    546                         on 'number' 
     546                        on 'number'   # TODO: fix this to work on a per-file basis 
    547547                                # number 'decimal' | 'float' | 'float32' | 'float64' 
    548548                                typeName = .grab 
     
    552552                                comp = .typeProvider to? Compiler 
    553553                                if comp, comp.numberTypeName = typeName.text 
     554                        on 'args'   # remaining tokens are cmdline args 
     555                                # args -ref:foo.dll -target:exe 
     556                                args = List<of String>() 
     557                                token = .grab 
     558                                argStr = token.text 
     559                                endLast = token.colNum + token.length 
     560                                while token.which <> 'EOL' 
     561                                        token = .grab 
     562                                        if endLast < token.colNum or token.which == 'EOL' 
     563                                                args.add(argStr) 
     564                                                argStr = token.text 
     565                                        else 
     566                                                argStr += token.text 
     567                                        endLast = token.colNum + token.length 
     568                                         
     569                                if not args.count  
     570                                        .throwError('args directive needs at least one arg following.') 
     571                                         
     572                                comp = .typeProvider to? Compiler 
     573                                # trace args 
     574                                if comp 
     575                                        options = ArgParser.parseToOptions(args) 
     576                                        comp.augmentOptions(options) 
    554577                        else 
    555578                                .throwError('Unknown compiler directive.') 
  • cobra/trunk/Source/CommandLine.cobra

    r1648 r1667  
    3434                return '0.8.0 post-release' 
    3535 
    36         var _optionSpecs as List<of Dictionary<of String, Object>> 
    37  
    3836        var _rawOptionSpecs = [ 
    3937                { 
     
    202200                { 
    203201                        'name': 'reference', 
    204                         'synonyms': ['r'], 
     202                        'synonyms': ['r'], # TODO: lose 'r' ->'Ref', 'R', something else 
    205203                        'isAccumulator': true, 
    206204                        'description': 'Add a DLL reference.', 
     
    221219#               }, 
    222220                { 
    223                         'name': 'run', 
     221                        'name': 'run',  # TODO: Synonym 'r' after change 'reference' switch 
    224222                        'description': 'Runs the Cobra program. This is the default behavior if specify any Cobra source files.', 
    225223                        'type': 'main', 
     
    282280                }, 
    283281        ] 
     282                 
    284283 
    285284        var _startTime as DateTime 
     
    291290 
    292291        var _compiler as Compiler? 
     292        var _argParser as ArgParser 
    293293 
    294294        def init 
    295295                _startTime = DateTime.now 
    296                 # prep the option specs 
    297                 _optionSpecs = List<of Dictionary<of String, Object>>() 
    298                 for specObj in _rawOptionSpecs 
    299                         # since some _optionSpecs are Dictionary<of String, Object> and others are 
    300                         # Dictionary<of String, String> then _optionSpecs ends up being 
    301                         # Dictionary<of String, Object> 
    302  
    303                         if specObj inherits Dictionary<of String, Object> 
    304                                 d = specObj 
    305                         else if specObj inherits Dictionary<of String, String> 
    306                                 d = Dictionary<of String, Object>() 
    307                                 for key in specObj.keys 
    308                                         d[key] = specObj[key] 
    309                         else 
    310                                 throw FallThroughException(specObj.getType) 
    311                         _optionSpecs.add(d) 
     296                _argParser = ArgParser(.versionString, _rawOptionSpecs) 
    312297 
    313298        get compiler from var 
     
    325310                return _verbosity 
    326311 
     312        def parseArgs(args as IList<of String>, options as out Options?, paths as out List<of String>?) 
     313                _argParser.parseArgs(args, out options, out paths) 
     314                _verbosity = _argParser.verbosity 
     315                CobraMain.willTimeIt = _argParser.willTimeIt 
     316                 
    327317        def run 
    328318                """ 
     
    340330                        return 
    341331                .parseArgs(args, out _options, out _pathList) 
     332 
    342333                if _options.boolValue('output-html') 
    343334                        _htmlWriter = HtmlWriter(Console.out) 
     
    390381                        _htmlWriter.writeHtml('</body></html>[_htmlWriter.newLine]') 
    391382 
    392         def isOptionSpecRestrictionViolated(optionSpec as Dictionary<of String, Object>) as bool 
    393                 """ 
    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 String 
    398                                 on 'mono-only' 
    399                                         return not CobraCore.isRunningOnMono 
    400                 return false 
    401  
    402         def parseArgs(args as IList<of String>, options as out Options?, paths as out List<of String>?) 
    403                 """ 
    404                 Parse command line arguments. 
    405                 The `args` should include only the arguments and not the executable/program name. 
    406                 """ 
    407                 ensure 
    408                         options 
    409                         paths 
    410                 body 
    411                         optionPrefix = '-' 
    412                         valuePrefix = c':' 
    413                         if not args.count 
    414                                 options = Options() 
    415                                 options.add('help', true) 
    416                                 paths = List<of String>() 
    417                                 return 
    418  
    419                         specDict = Dictionary<of String, Dictionary<of String, Object>>() 
    420                                 # ^ will contain keys for all spec names and their synonyms 
    421                         synToName = Dictionary<of String, String>() 
    422                                 # ^ maps synonyms to their full names 
    423                         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') 
    437  
    438                         # set up initial valueDict 
    439                         valueDict = Dictionary<of String, Object>() 
    440                         if Utils.isDevMachine 
    441                                 valueDict['reveal-internal-exceptions'] = true  # this is a specially computed default, but can still be overridden on the command line 
    442  
    443                         fileList = List<of String>() 
    444                         value = 'no-value' to dynamic 
    445                         mainOptions = List<of String>() 
    446                         didSpecify = Dictionary<of String, bool>()  # CC: could just be a Set 
    447                         for arg in args 
    448                                 if arg.trim.length == 0 
    449                                         continue 
    450                                 if arg.startsWith(optionPrefix) 
    451                                         isOption = true 
    452                                         while arg.startsWith(optionPrefix) 
    453                                                 arg = arg[1:] 
    454                                 else 
    455                                         isOption = false 
    456                                 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) 
    479                                         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 
    514                                 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) 
    526  
    527                         # handle synonyms 
    528                         for syn in synList 
    529                                 if valueDict.containsKey(syn) 
    530                                         valueDict[synToName[syn]] = valueDict[syn] 
    531                                         valueDict.remove(syn) 
    532  
    533                         # add in defaults 
    534                         for d in _optionSpecs 
    535                                 defaultName = d['name'] to String 
    536                                 if not valueDict.containsKey(defaultName) and d.containsKey('default') 
    537                                         defaultValue = .interpretValue(d['default'] to String, d) to ! 
    538                                         if .verbosity 
    539                                                 print 'Setting option "[defaultName]" to default value [defaultValue].' 
    540                                         valueDict[defaultName] = defaultValue 
    541  
    542                         # TODO: make the option names case-insensitive 
    543  
    544                         # check for more than one main option 
    545                         if mainOptions.count > 1 
    546                                 .error('Cannot have these main options at the same time: [Utils.join(", ", mainOptions)]') 
    547  
    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]".') 
    578  
    579                         # set the out parameters 
    580                         options = Options(valueDict) 
    581                         for name in didSpecify.keys 
    582                                 options.didSpecify(name) 
    583                         paths = fileList 
    584  
    585                         .computeArgImplications(options to !) 
    586  
    587  
    588         def computeArgImplications(options as Options) 
    589                 if options.getDefault('target', '') == 'lib' and not options.isSpecified('compile') 
    590                         options['compile'] = true 
    591                 if options.getDefault('debug', '') not in ['', '0', '-'] and not options.isSpecified('debugging-tips') 
    592                         options['debugging-tips'] = false 
    593                 if options.boolValue('turbo') 
    594                         options['contracts'] = 'none' 
    595                         options['include-asserts'] = false 
    596                         options['include-nil-checks'] = false 
    597                         options['include-tests'] = false 
    598                         options['optimize'] = true 
    599  
    600         def interpretValue(valueStr as String, spec as Dictionary<of String, Object>) as dynamic? 
    601                 value as dynamic? 
    602                 branch spec['type'] to String 
    603                         on 'main' 
    604                                 throw InvalidOperationException('This method does not handle the main type.') 
    605                         on 'bool' 
    606                                 try 
    607                                         value = .boolForString(valueStr) 
    608                                 catch FormatException 
    609                                         cannotProcess = true 
    610                         on 'int' 
    611                                 if valueStr == 'on'  # set internally when there is no value 
    612                                         valueStr = '1' 
    613                                 try 
    614                                         value = int.parse(valueStr) 
    615                                 catch FormatException 
    616                                         cannotProcess = true 
    617                                 catch OverflowException 
    618                                         cannotProcess = true 
    619                                 # TODO: check min and max 
    620                         on 'string' 
    621                                 value = valueStr 
    622                         on 'menu' 
    623                                 if valueStr.length==0 
    624                                         cannotProcess = true 
    625                                 if not (spec['choices'] to System.Collections.IList).contains(valueStr) 
    626                                         cannotProcess = true 
    627                                 else 
    628                                         value = valueStr 
    629                 r = if(cannotProcess, nil, value) 
    630                 return r 
    631  
    632383        def doCompile(paths as List<of String>) as Compiler 
    633384                return .doCompile(paths, true, false, false) 
    634385 
    635         def doCompile(paths as List<of String>, willPrintSuccessMsg as bool, writeTestInvocation as bool, stopAfterBindInt as bool) as Compiler 
     386        def doCompile(paths as List<of String>, willPrintSuccessMsg as bool,  
     387                                        writeTestInvocation as bool, stopAfterBindInt as bool) as Compiler 
    636388                oldPaths = List<of String>(paths) 
    637389                paths.clear 
     
    651403                try 
    652404                        c.compileFilesNamed(paths, writeTestInvocation, stopAfterBindInt) 
     405                        if _verbosity <> c.verbosity, _verbosity = c.verbosity 
    653406                catch StopCompilation 
    654407                        # Each phase of the compiler may throw an exception to stop compilation. 
     
    729482                if c.errors.count 
    730483                        print 'Not running due to errors above.' 
     484                        return 
     485                if .options.boolValue('compile')  # maybe changed by compiler directive 
     486                        return 
     487 
     488                p = c.runProcess 
     489                if _verbosity >= 1 
     490                        print 'Running: [p.startInfo.fileName] [p.startInfo.arguments]' 
     491                        print .verboseLineSeparator 
     492                p.startInfo.useShellExecute = false 
     493                p.start 
     494                p.waitForExit  # TODO: is this necessary? 
     495 
     496        def doHelp 
     497                _argParser.doHelp   # CC: multiline string 
     498 
     499        def doAbout 
     500                _argParser.doAbout 
     501 
     502        def doVersion 
     503                print .versionString 
     504 
     505        def error(msg as String) 
     506                if msg.length 
     507                        print 'cobra: error: [msg]' 
     508                        print 'Run Cobra without options to get full usage information.' 
     509                Environment.exit(1) 
     510 
     511 
     512        ## Build Standard Library 
     513 
     514        def doBuildStandardLibrary 
     515                v = .verbosity 
     516                if v, print 'Building standard library' 
     517                dllInfo = FileInfo('Cobra.Lang.dll') 
     518                if dllInfo.exists 
     519                        if v, print 'Renaming Cobra.Lang.dll to Cobra.Lang-previous.dll' 
     520                        prevInfo = FileInfo('Cobra.Lang-previous.dll') 
     521                        if prevInfo.exists, prevInfo.delete 
     522                        FileInfo('Cobra.Lang.dll').moveTo('Cobra.Lang-previous.dll') 
     523                _options['target'] = 'lib' 
     524                _options['include-tests'] = false  # TODO: including tests in a DLL tends to cause problems. it might be because tests are triggered by type initializers. this needs investigation 
     525                _options['embed-run-time'] = true  # because the runtime is what we're building! 
     526                .doCompile(List<of String>(), true, false, false) 
     527 
     528 
     529        ## Testify 
     530 
     531        def doTestify(paths as List<of String>) 
     532                """ 
     533                Used internally for testing cobra during development. 
     534                Why not just 'test'? because that is reserved for regular developers to run true unit tests. 
     535                """ 
     536                TestifyRunner(_startTime, this, paths).run 
     537                 
     538         
     539 
     540class ArgParser 
     541        """ 
     542        Class for handling parsing of cmdline string arguments into a set of recognised  
     543        Options and a list of paths. 
     544        Provides documentation display (help, about) printed output as well 
     545        """ 
     546 
     547        var _self as ArgParser? is shared 
     548        var _versionString as String 
     549        var _verbosity = 0 
     550        var _willTimeIt = false 
     551        var _optsOnly = false 
     552         
     553        get versionString from var 
     554        get verbosity from var 
     555        get willTimeIt from var 
     556 
     557        var _optionSpecs as List<of Dictionary<of String, Object>> 
     558 
     559        var     _specDict as Dictionary<of String, Dictionary<of String, Object>> 
     560                # will contain keys for all spec names and their synonyms 
     561 
     562        var _synToName as Dictionary<of String, String> 
     563                # maps synonyms to their full names 
     564 
     565        var _synList as List<of String> 
     566 
     567        def init(version as String, rawOptionSpecs) 
     568                _versionString = version 
     569                # prep the option specs 
     570                _optionSpecs = List<of Dictionary<of String, Object>>() 
     571                for specObj in rawOptionSpecs 
     572                        # since some _optionSpecs are Dictionary<of String, Object> and others are 
     573                        # Dictionary<of String, String> then _optionSpecs ends up being 
     574                        # Dictionary<of String, Object> 
     575 
     576                        if specObj inherits Dictionary<of String, Object> 
     577                                d = specObj 
     578                        else if specObj inherits Dictionary<of String, String> 
     579                                d = Dictionary<of String, Object>() 
     580                                for key in specObj.keys 
     581                                        d[key] = specObj[key] 
     582                        else 
     583                                throw FallThroughException(specObj.getType) 
     584                        _optionSpecs.add(d) 
     585 
     586                _specDict = Dictionary<of String, Dictionary<of String, Object>>() 
     587                _synToName = Dictionary<of String, String>() 
     588                _synList = List<of String>() 
     589                _initSynonyms 
     590                _self = this 
     591