ext: testlib loading tests from multiple directories
[gem5.git] / ext / testlib / configuration.py
1 # Copyright (c) 2020-2021 ARM Limited
2 # All rights reserved
3 #
4 # The license below extends only to copyright in the software and shall
5 # not be construed as granting a license to any other intellectual
6 # property including but not limited to intellectual property relating
7 # to a hardware implementation of the functionality of the software
8 # licensed hereunder. You may use the software subject to the license
9 # terms below provided that you ensure that this notice is replicated
10 # unmodified and in its entirety in all distributions of the software,
11 # modified or unmodified, in source code or in binary form.
12 #
13 # Copyright (c) 2017 Mark D. Hill and David A. Wood
14 # All rights reserved.
15 #
16 # Redistribution and use in source and binary forms, with or without
17 # modification, are permitted provided that the following conditions are
18 # met: redistributions of source code must retain the above copyright
19 # notice, this list of conditions and the following disclaimer;
20 # redistributions in binary form must reproduce the above copyright
21 # notice, this list of conditions and the following disclaimer in the
22 # documentation and/or other materials provided with the distribution;
23 # neither the name of the copyright holders nor the names of its
24 # contributors may be used to endorse or promote products derived from
25 # this software without specific prior written permission.
26 #
27 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #
39 # Authors: Sean Wilson
40
41 '''
42 Global configuration module which exposes two types of configuration
43 variables:
44
45 1. config
46 2. constants (Also attached to the config variable as an attribute)
47
48 The main motivation for this module is to have a centralized location for
49 defaults and configuration by command line and files for the test framework.
50
51 A secondary goal is to reduce programming errors by providing common constant
52 strings and values as python attributes to simplify detection of typos.
53 A simple typo in a string can take a lot of debugging to uncover the issue,
54 attribute errors are easier to notice and most autocompletion systems detect
55 them.
56
57 The config variable is initialzed by calling :func:`initialize_config`.
58 Before this point only ``constants`` will be availaible. This is to ensure
59 that library function writers never accidentally get stale config attributes.
60
61 Program arguments/flag arguments are available from the config as attributes.
62 If an attribute was not set by the command line or the optional config file,
63 then it will fallback to the `_defaults` value, if still the value is not
64 found an AttributeError will be raised.
65
66 :func define_defaults:
67 Provided by the config if the attribute is not found in the config or
68 commandline. For instance, if we are using the list command fixtures might
69 not be able to count on the build_dir being provided since we aren't going
70 to build anything.
71
72 :var constants:
73 Values not directly exposed by the config, but are attached to the object
74 for centralized access. I.E. you can reach them with
75 :code:`config.constants.attribute`. These should be used for setting
76 common string names used across the test framework.
77 :code:`_defaults.build_dir = None` Once this module has been imported
78 constants should not be modified and their base attributes are frozen.
79 '''
80 import abc
81 import argparse
82 import copy
83 import os
84 import re
85
86 from pickle import HIGHEST_PROTOCOL as highest_pickle_protocol
87
88 from testlib.helper import absdirpath, AttrDict, FrozenAttrDict
89
90 class UninitialzedAttributeException(Exception):
91 '''
92 Signals that an attribute in the config file was not initialized.
93 '''
94 pass
95
96 class UninitializedConfigException(Exception):
97 '''
98 Signals that the config was not initialized before trying to access an
99 attribute.
100 '''
101 pass
102
103 class TagRegex(object):
104 def __init__(self, include, regex):
105 self.include = include
106 self.regex = re.compile(regex)
107
108 def __str__(self):
109 type_ = 'Include' if self.include else 'Remove'
110 return '%10s: %s' % (type_, self.regex.pattern)
111
112 class _Config(object):
113 _initialized = False
114
115 __shared_dict = {}
116
117 constants = AttrDict()
118 _defaults = AttrDict()
119 _config = {}
120
121 _cli_args = {}
122 _post_processors = {}
123
124 def __init__(self):
125 # This object will act as if it were a singleton.
126 self.__dict__ = self.__shared_dict
127
128 def _init(self, parser):
129 self._parse_commandline_args(parser)
130 self._run_post_processors()
131 self._initialized = True
132
133 def _add_post_processor(self, attr, post_processor):
134 '''
135 :param attr: Attribute to pass to and recieve from the
136 :func:`post_processor`.
137
138 :param post_processor: A callback functions called in a chain to
139 perform additional setup for a config argument. Should return a
140 tuple containing the new value for the config attr.
141 '''
142 if attr not in self._post_processors:
143 self._post_processors[attr] = []
144 self._post_processors[attr].append(post_processor)
145
146 def _set(self, name, value):
147 self._config[name] = value
148
149 def _parse_commandline_args(self, parser):
150 args = parser.parse_args()
151
152 self._config_file_args = {}
153
154 for attr in dir(args):
155 # Ignore non-argument attributes.
156 if not attr.startswith('_'):
157 self._config_file_args[attr] = getattr(args, attr)
158 self._config.update(self._config_file_args)
159
160 def _run_post_processors(self):
161 for attr, callbacks in self._post_processors.items():
162 newval = self._lookup_val(attr)
163 for callback in callbacks:
164 newval = callback(newval)
165 if newval is not None:
166 newval = newval[0]
167 self._set(attr, newval)
168
169
170 def _lookup_val(self, attr):
171 '''
172 Get the attribute from the config or fallback to defaults.
173
174 :returns: If the value is not stored return None. Otherwise a tuple
175 containing the value.
176 '''
177 if attr in self._config:
178 return (self._config[attr],)
179 elif hasattr(self._defaults, attr):
180 return (getattr(self._defaults, attr),)
181
182 def __getattr__(self, attr):
183 if attr in dir(super(_Config, self)):
184 return getattr(super(_Config, self), attr)
185 elif not self._initialized:
186 raise UninitializedConfigException(
187 'Cannot directly access elements from the config before it is'
188 ' initialized')
189 else:
190 val = self._lookup_val(attr)
191 if val is not None:
192 return val[0]
193 else:
194 raise UninitialzedAttributeException(
195 '%s was not initialzed in the config.' % attr)
196
197 def get_tags(self):
198 d = {typ: set(self.__getattr__(typ))
199 for typ in self.constants.supported_tags}
200 if any(map(lambda vals: bool(vals), d.values())):
201 return d
202 else:
203 return {}
204
205 def define_defaults(defaults):
206 '''
207 Defaults are provided by the config if the attribute is not found in the
208 config or commandline. For instance, if we are using the list command
209 fixtures might not be able to count on the build_dir being provided since
210 we aren't going to build anything.
211 '''
212 defaults.base_dir = os.path.abspath(os.path.join(absdirpath(__file__),
213 os.pardir,
214 os.pardir))
215 defaults.result_path = os.path.join(os.getcwd(), 'testing-results')
216 defaults.resource_url = 'http://dist.gem5.org/dist/develop'
217 defaults.resource_path = os.path.abspath(os.path.join(defaults.base_dir,
218 'tests',
219 'gem5',
220 'resources'))
221
222 def define_constants(constants):
223 '''
224 'constants' are values not directly exposed by the config, but are attached
225 to the object for centralized access. These should be used for setting
226 common string names used across the test framework. A simple typo in
227 a string can take a lot of debugging to uncover the issue, attribute errors
228 are easier to notice and most autocompletion systems detect them.
229 '''
230 constants.system_out_name = 'system-out'
231 constants.system_err_name = 'system-err'
232
233 constants.isa_tag_type = 'isa'
234 constants.x86_tag = 'X86'
235 constants.gcn3_x86_tag = 'GCN3_X86'
236 constants.sparc_tag = 'SPARC'
237 constants.riscv_tag = 'RISCV'
238 constants.arm_tag = 'ARM'
239 constants.mips_tag = 'MIPS'
240 constants.power_tag = 'POWER'
241 constants.null_tag = 'NULL'
242
243 constants.variant_tag_type = 'variant'
244 constants.opt_tag = 'opt'
245 constants.debug_tag = 'debug'
246 constants.fast_tag = 'fast'
247
248 constants.length_tag_type = 'length'
249 constants.quick_tag = 'quick'
250 constants.long_tag = 'long'
251
252 constants.host_isa_tag_type = 'host'
253 constants.host_x86_64_tag = 'x86_64'
254 constants.host_arm_tag = 'aarch64'
255
256 constants.supported_tags = {
257 constants.isa_tag_type : (
258 constants.x86_tag,
259 constants.gcn3_x86_tag,
260 constants.sparc_tag,
261 constants.riscv_tag,
262 constants.arm_tag,
263 constants.mips_tag,
264 constants.power_tag,
265 constants.null_tag,
266 ),
267 constants.variant_tag_type: (
268 constants.opt_tag,
269 constants.debug_tag,
270 constants.fast_tag,
271 ),
272 constants.length_tag_type: (
273 constants.quick_tag,
274 constants.long_tag,
275 ),
276 constants.host_isa_tag_type: (
277 constants.host_x86_64_tag,
278 constants.host_arm_tag,
279 ),
280 }
281
282 # Binding target ISA with host ISA. This is useful for the
283 # case where host ISA and target ISA need to coincide
284 constants.target_host = {
285 constants.arm_tag : (constants.host_arm_tag,),
286 constants.x86_tag : (constants.host_x86_64_tag,),
287 constants.gcn3_x86_tag : (constants.host_x86_64_tag,),
288 constants.sparc_tag : (constants.host_x86_64_tag,),
289 constants.riscv_tag : (constants.host_x86_64_tag,),
290 constants.mips_tag : (constants.host_x86_64_tag,),
291 constants.power_tag : (constants.host_x86_64_tag,),
292 constants.null_tag : (None,)
293 }
294
295 constants.supported_isas = constants.supported_tags['isa']
296 constants.supported_variants = constants.supported_tags['variant']
297 constants.supported_lengths = constants.supported_tags['length']
298 constants.supported_hosts = constants.supported_tags['host']
299
300 constants.tempdir_fixture_name = 'tempdir'
301 constants.gem5_simulation_stderr = 'simerr'
302 constants.gem5_simulation_stdout = 'simout'
303 constants.gem5_simulation_stats = 'stats.txt'
304 constants.gem5_simulation_config_ini = 'config.ini'
305 constants.gem5_simulation_config_json = 'config.json'
306 constants.gem5_returncode_fixture_name = 'gem5-returncode'
307 constants.gem5_binary_fixture_name = 'gem5'
308 constants.xml_filename = 'results.xml'
309 constants.pickle_filename = 'results.pickle'
310 constants.pickle_protocol = highest_pickle_protocol
311
312 # The root directory which all test names will be based off of.
313 constants.testing_base = absdirpath(os.path.join(absdirpath(__file__),
314 os.pardir))
315
316 def define_post_processors(config):
317 '''
318 post_processors are used to do final configuration of variables. This is
319 useful if there is a dynamically set default, or some function that needs
320 to be applied after parsing in order to set a configration value.
321
322 Post processors must accept a single argument that will either be a tuple
323 containing the already set config value or ``None`` if the config value
324 has not been set to anything. They must return the modified value in the
325 same format.
326 '''
327
328 def set_default_build_dir(build_dir):
329 '''
330 Post-processor to set the default build_dir based on the base_dir.
331
332 .. seealso :func:`~_Config._add_post_processor`
333 '''
334 if not build_dir or build_dir[0] is None:
335 base_dir = config._lookup_val('base_dir')[0]
336 build_dir = (os.path.join(base_dir, 'build'),)
337 return build_dir
338
339 def fix_verbosity_hack(verbose):
340 return (verbose[0].val,)
341
342 def threads_as_int(threads):
343 if threads is not None:
344 return (int(threads[0]),)
345
346 def test_threads_as_int(test_threads):
347 if test_threads is not None:
348 return (int(test_threads[0]),)
349
350 def default_isa(isa):
351 if not isa[0]:
352 return [constants.supported_tags[constants.isa_tag_type]]
353 else:
354 return isa
355
356 def default_variant(variant):
357 if not variant[0]:
358 # Default variant is only opt. No need to run tests with multiple
359 # different compilation targets
360 return [[constants.opt_tag]]
361 else:
362 return variant
363
364 def default_length(length):
365 if not length[0]:
366 return [[constants.quick_tag]]
367 else:
368 return length
369
370 def default_host(host):
371 if not host[0]:
372 try:
373 import platform
374 host_machine = platform.machine()
375 if host_machine not in constants.supported_hosts:
376 raise ValueError("Invalid host machine")
377 return [[host_machine]]
378 except:
379 return [[constants.host_x86_64_tag]]
380 else:
381 return host
382
383 def compile_tag_regex(positional_tags):
384 if not positional_tags:
385 return positional_tags
386 else:
387 new_positional_tags_list = []
388 positional_tags = positional_tags[0]
389
390 for flag, regex in positional_tags:
391 if flag == 'exclude_tags':
392 tag_regex = TagRegex(False, regex)
393 elif flag == 'include_tags':
394 tag_regex = TagRegex(True, regex)
395 else:
396 raise ValueError('Unsupported flag.')
397 new_positional_tags_list.append(tag_regex)
398
399 return (new_positional_tags_list,)
400
401 config._add_post_processor('build_dir', set_default_build_dir)
402 config._add_post_processor('verbose', fix_verbosity_hack)
403 config._add_post_processor('isa', default_isa)
404 config._add_post_processor('variant', default_variant)
405 config._add_post_processor('length', default_length)
406 config._add_post_processor('host', default_host)
407 config._add_post_processor('threads', threads_as_int)
408 config._add_post_processor('test_threads', test_threads_as_int)
409 config._add_post_processor(StorePositionalTagsAction.position_kword,
410 compile_tag_regex)
411 class Argument(object):
412 '''
413 Class represents a cli argument/flag for a argparse parser.
414
415 :attr name: The long name of this object that will be stored in the arg
416 output by the final parser.
417 '''
418 def __init__(self, *flags, **kwargs):
419 self.flags = flags
420 self.kwargs = kwargs
421
422 if len(flags) == 0:
423 raise ValueError("Need at least one argument.")
424 elif 'dest' in kwargs:
425 self.name = kwargs['dest']
426 elif len(flags) > 1 or flags[0].startswith('-'):
427 for flag in flags:
428 if not flag.startswith('-'):
429 raise ValueError("invalid option string %s: must start"
430 "with a character '-'" % flag)
431
432 if flag.startswith('--'):
433 if not hasattr(self, 'name'):
434 self.name = flag.lstrip('-')
435
436 if not hasattr(self, 'name'):
437 self.name = flags[0].lstrip('-')
438 self.name = self.name.replace('-', '_')
439
440 def add_to(self, parser):
441 '''Add this argument to the given parser.'''
442 parser.add_argument(*self.flags, **self.kwargs)
443
444 def copy(self):
445 '''Copy this argument so you might modify any of its kwargs.'''
446 return copy.deepcopy(self)
447
448
449 class _StickyInt:
450 '''
451 A class that is used to cheat the verbosity count incrementer by
452 pretending to be an int. This makes the int stay on the heap and eat other
453 real numbers when they are added to it.
454
455 We use this so we can allow the verbose flag to be provided before or after
456 the subcommand. This likely has no utility outside of this use case.
457 '''
458 def __init__(self, val=0):
459 self.val = val
460 self.type = int
461 def __add__(self, other):
462 self.val += other
463 return self
464
465 common_args = NotImplemented
466
467 class StorePositionAction(argparse.Action):
468 '''Base class for classes wishing to create namespaces where
469 arguments are stored in the order provided via the command line.
470 '''
471 position_kword = 'positional'
472
473 def __call__(self, parser, namespace, values, option_string=None):
474 if not self.position_kword in namespace:
475 setattr(namespace, self.position_kword, [])
476 previous = getattr(namespace, self.position_kword)
477 previous.append((self.dest, values))
478 setattr(namespace, self.position_kword, previous)
479
480 class StorePositionalTagsAction(StorePositionAction):
481 position_kword = 'tag_filters'
482
483 def define_common_args(config):
484 '''
485 Common args are arguments which are likely to be simular between different
486 subcommands, so they are available to all by placing their definitions
487 here.
488 '''
489 global common_args
490
491 parse_comma_separated_string = lambda st: st.split(',')
492
493 # A list of common arguments/flags used across cli parsers.
494 common_args = [
495 Argument(
496 'directories',
497 nargs='*',
498 default=[os.getcwd()],
499 help='Space separated list of directories to start searching '
500 'for tests in'),
501 Argument(
502 '--exclude-tags',
503 action=StorePositionalTagsAction,
504 help='A tag comparison used to select tests.'),
505 Argument(
506 '--include-tags',
507 action=StorePositionalTagsAction,
508 help='A tag comparison used to select tests.'),
509 Argument(
510 '--isa',
511 action='extend',
512 default=[],
513 type=parse_comma_separated_string,
514 help="Only tests that are valid with one of these ISAs. "
515 "Comma separated."),
516 Argument(
517 '--variant',
518 action='extend',
519 default=[],
520 type=parse_comma_separated_string,
521 help="Only tests that are valid with one of these binary variants"
522 "(e.g., opt, debug). Comma separated."),
523 Argument(
524 '--length',
525 action='extend',
526 default=[],
527 type=parse_comma_separated_string,
528 help="Only tests that are one of these lengths. Comma separated."),
529 Argument(
530 '--host',
531 action='append',
532 default=[],
533 help="Only tests that are meant to runnable on the selected host"),
534 Argument(
535 '--uid',
536 action='store',
537 default=None,
538 help='UID of a specific test item to run.'),
539 Argument(
540 '--build-dir',
541 action='store',
542 help='Build directory for SCons'),
543 Argument(
544 '--base-dir',
545 action='store',
546 default=config._defaults.base_dir,
547 help='Directory to change to in order to exec scons.'),
548 Argument(
549 '-j', '--threads',
550 action='store',
551 default=1,
552 help='Number of threads to run SCons with.'),
553 Argument(
554 '-t', '--test-threads',
555 action='store',
556 default=1,
557 help='Number of threads to spawn to run concurrent tests with.'),
558 Argument(
559 '-v',
560 action='count',
561 dest='verbose',
562 default=_StickyInt(),
563 help='Increase verbosity'),
564 Argument(
565 '--config-path',
566 action='store',
567 default=os.getcwd(),
568 help='Path to read a testing.ini config in'
569 ),
570 Argument(
571 '--skip-build',
572 action='store_true',
573 default=False,
574 help='Skip the building component of SCons targets.'
575 ),
576 Argument(
577 '--result-path',
578 action='store',
579 help='The path to store results in.'
580 ),
581 Argument(
582 '--bin-path',
583 action='store',
584 default=config._defaults.resource_path,
585 help='Path where resources are stored (downloaded if not present)'
586 ),
587 Argument(
588 '--resource-url',
589 action='store',
590 default=config._defaults.resource_url,
591 help='The URL where the resources reside.'
592 ),
593
594 ]
595
596 # NOTE: There is a limitation which arises due to this format. If you have
597 # multiple arguments with the same name only the final one in the list
598 # will be saved.
599 #
600 # e.g. if you have a -v argument which increments verbosity level and
601 # a separate --verbose flag which 'store's verbosity level. the final
602 # one in the list will be saved.
603 common_args = AttrDict({arg.name:arg for arg in common_args})
604
605 class ArgParser(object, metaclass=abc.ABCMeta):
606 class ExtendAction(argparse.Action):
607 def __call__(self, parser, namespace, values, option_string=None):
608 items = getattr(namespace, self.dest, [])
609 items.extend(values)
610 setattr(namespace, self.dest, items)
611
612 def __init__(self, parser):
613 # Copy public methods of the parser.
614 for attr in dir(parser):
615 if not attr.startswith('_'):
616 setattr(self, attr, getattr(parser, attr))
617 self.parser = parser
618 self.parser.register('action', 'extend', ArgParser.ExtendAction)
619 self.add_argument = self.parser.add_argument
620
621 # Argument will be added to all parsers and subparsers.
622 common_args.verbose.add_to(parser)
623
624
625 class CommandParser(ArgParser):
626 '''
627 Main parser which parses command strings and uses those to direct to
628 a subparser.
629 '''
630 def __init__(self):
631 parser = argparse.ArgumentParser()
632 super(CommandParser, self).__init__(parser)
633 self.subparser = self.add_subparsers(dest='command')
634
635
636 class RunParser(ArgParser):
637 '''
638 Parser for the \'run\' command.
639 '''
640 def __init__(self, subparser):
641 parser = subparser.add_parser(
642 'run',
643 help='''Run Tests.'''
644 )
645
646 super(RunParser, self).__init__(parser)
647
648 common_args.uid.add_to(parser)
649 common_args.skip_build.add_to(parser)
650 common_args.directories.add_to(parser)
651 common_args.build_dir.add_to(parser)
652 common_args.base_dir.add_to(parser)
653 common_args.bin_path.add_to(parser)
654 common_args.threads.add_to(parser)
655 common_args.test_threads.add_to(parser)
656 common_args.isa.add_to(parser)
657 common_args.variant.add_to(parser)
658 common_args.length.add_to(parser)
659 common_args.host.add_to(parser)
660 common_args.include_tags.add_to(parser)
661 common_args.exclude_tags.add_to(parser)
662
663
664 class ListParser(ArgParser):
665 '''
666 Parser for the \'list\' command.
667 '''
668 def __init__(self, subparser):
669 parser = subparser.add_parser(
670 'list',
671 help='''List and query test metadata.'''
672 )
673 super(ListParser, self).__init__(parser)
674
675 Argument(
676 '--suites',
677 action='store_true',
678 default=False,
679 help='List all test suites.'
680 ).add_to(parser)
681 Argument(
682 '--tests',
683 action='store_true',
684 default=False,
685 help='List all test cases.'
686 ).add_to(parser)
687 Argument(
688 '--fixtures',
689 action='store_true',
690 default=False,
691 help='List all fixtures.'
692 ).add_to(parser)
693 Argument(
694 '--all-tags',
695 action='store_true',
696 default=False,
697 help='List all tags.'
698 ).add_to(parser)
699 Argument(
700 '-q',
701 dest='quiet',
702 action='store_true',
703 default=False,
704 help='Quiet output (machine readable).'
705 ).add_to(parser)
706
707 common_args.directories.add_to(parser)
708 common_args.bin_path.add_to(parser)
709 common_args.isa.add_to(parser)
710 common_args.variant.add_to(parser)
711 common_args.length.add_to(parser)
712 common_args.host.add_to(parser)
713 common_args.include_tags.add_to(parser)
714 common_args.exclude_tags.add_to(parser)
715
716
717 class RerunParser(ArgParser):
718 def __init__(self, subparser):
719 parser = subparser.add_parser(
720 'rerun',
721 help='''Rerun failed tests.'''
722 )
723 super(RerunParser, self).__init__(parser)
724
725 common_args.skip_build.add_to(parser)
726 common_args.directories.add_to(parser)
727 common_args.build_dir.add_to(parser)
728 common_args.base_dir.add_to(parser)
729 common_args.bin_path.add_to(parser)
730 common_args.threads.add_to(parser)
731 common_args.test_threads.add_to(parser)
732 common_args.isa.add_to(parser)
733 common_args.variant.add_to(parser)
734 common_args.length.add_to(parser)
735 common_args.host.add_to(parser)
736
737 config = _Config()
738 define_constants(config.constants)
739
740 # Constants are directly exposed and available once this module is created.
741 # All constants MUST be defined before this point.
742 config.constants = FrozenAttrDict(config.constants.__dict__)
743 constants = config.constants
744
745 '''
746 This config object is the singleton config object available throughout the
747 framework.
748 '''
749 def initialize_config():
750 '''
751 Parse the commandline arguments and setup the config varibles.
752 '''
753 global config
754
755 # Setup constants and defaults
756 define_defaults(config._defaults)
757 define_post_processors(config)
758 define_common_args(config)
759
760 # Setup parser and subcommands
761 baseparser = CommandParser()
762 runparser = RunParser(baseparser.subparser)
763 listparser = ListParser(baseparser.subparser)
764 rerunparser = RerunParser(baseparser.subparser)
765
766 # Initialize the config by parsing args and running callbacks.
767 config._init(baseparser)