112882Sspwilson2@wisc.edu# Copyright (c) 2017 Mark D. Hill and David A. Wood 212882Sspwilson2@wisc.edu# All rights reserved. 312882Sspwilson2@wisc.edu# 412882Sspwilson2@wisc.edu# Redistribution and use in source and binary forms, with or without 512882Sspwilson2@wisc.edu# modification, are permitted provided that the following conditions are 612882Sspwilson2@wisc.edu# met: redistributions of source code must retain the above copyright 712882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer; 812882Sspwilson2@wisc.edu# redistributions in binary form must reproduce the above copyright 912882Sspwilson2@wisc.edu# notice, this list of conditions and the following disclaimer in the 1012882Sspwilson2@wisc.edu# documentation and/or other materials provided with the distribution; 1112882Sspwilson2@wisc.edu# neither the name of the copyright holders nor the names of its 1212882Sspwilson2@wisc.edu# contributors may be used to endorse or promote products derived from 1312882Sspwilson2@wisc.edu# this software without specific prior written permission. 1412882Sspwilson2@wisc.edu# 1512882Sspwilson2@wisc.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1612882Sspwilson2@wisc.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1712882Sspwilson2@wisc.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1812882Sspwilson2@wisc.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1912882Sspwilson2@wisc.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2012882Sspwilson2@wisc.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2112882Sspwilson2@wisc.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2212882Sspwilson2@wisc.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2312882Sspwilson2@wisc.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2412882Sspwilson2@wisc.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2512882Sspwilson2@wisc.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2612882Sspwilson2@wisc.edu# 2712882Sspwilson2@wisc.edu# Authors: Sean Wilson 2812882Sspwilson2@wisc.edu 2912882Sspwilson2@wisc.edu''' 3012882Sspwilson2@wisc.eduGlobal configuration module which exposes two types of configuration 3112882Sspwilson2@wisc.eduvariables: 3212882Sspwilson2@wisc.edu 3312882Sspwilson2@wisc.edu1. config 3412882Sspwilson2@wisc.edu2. constants (Also attached to the config variable as an attribute) 3512882Sspwilson2@wisc.edu 3612882Sspwilson2@wisc.eduThe main motivation for this module is to have a centralized location for 3712882Sspwilson2@wisc.edudefaults and configuration by command line and files for the test framework. 3812882Sspwilson2@wisc.edu 3912882Sspwilson2@wisc.eduA secondary goal is to reduce programming errors by providing common constant 4012882Sspwilson2@wisc.edustrings and values as python attributes to simplify detection of typos. 4112882Sspwilson2@wisc.eduA simple typo in a string can take a lot of debugging to uncover the issue, 4212882Sspwilson2@wisc.eduattribute errors are easier to notice and most autocompletion systems detect 4312882Sspwilson2@wisc.eduthem. 4412882Sspwilson2@wisc.edu 4512882Sspwilson2@wisc.eduThe config variable is initialzed by calling :func:`initialize_config`. 4612882Sspwilson2@wisc.eduBefore this point only ``constants`` will be availaible. This is to ensure 4712882Sspwilson2@wisc.eduthat library function writers never accidentally get stale config attributes. 4812882Sspwilson2@wisc.edu 4912882Sspwilson2@wisc.eduProgram arguments/flag arguments are available from the config as attributes. 5012882Sspwilson2@wisc.eduIf an attribute was not set by the command line or the optional config file, 5112882Sspwilson2@wisc.eduthen it will fallback to the `_defaults` value, if still the value is not 5212882Sspwilson2@wisc.edufound an AttributeError will be raised. 5312882Sspwilson2@wisc.edu 5412882Sspwilson2@wisc.edu:func define_defaults: 5512882Sspwilson2@wisc.edu Provided by the config if the attribute is not found in the config or 5612882Sspwilson2@wisc.edu commandline. For instance, if we are using the list command fixtures might 5712882Sspwilson2@wisc.edu not be able to count on the build_dir being provided since we aren't going 5812882Sspwilson2@wisc.edu to build anything. 5912882Sspwilson2@wisc.edu 6012882Sspwilson2@wisc.edu:var constants: 6112882Sspwilson2@wisc.edu Values not directly exposed by the config, but are attached to the object 6212882Sspwilson2@wisc.edu for centralized access. I.E. you can reach them with 6312882Sspwilson2@wisc.edu :code:`config.constants.attribute`. These should be used for setting 6412882Sspwilson2@wisc.edu common string names used across the test framework. 6512882Sspwilson2@wisc.edu :code:`_defaults.build_dir = None` Once this module has been imported 6612882Sspwilson2@wisc.edu constants should not be modified and their base attributes are frozen. 6712882Sspwilson2@wisc.edu''' 6812882Sspwilson2@wisc.eduimport abc 6912882Sspwilson2@wisc.eduimport argparse 7012882Sspwilson2@wisc.eduimport copy 7112882Sspwilson2@wisc.eduimport os 7212882Sspwilson2@wisc.eduimport re 7312882Sspwilson2@wisc.edu 7412882Sspwilson2@wisc.edufrom ConfigParser import ConfigParser 7512882Sspwilson2@wisc.edufrom pickle import HIGHEST_PROTOCOL as highest_pickle_protocol 7612882Sspwilson2@wisc.edu 7712882Sspwilson2@wisc.edufrom helper import absdirpath, AttrDict, FrozenAttrDict 7812882Sspwilson2@wisc.edu 7912882Sspwilson2@wisc.educlass UninitialzedAttributeException(Exception): 8012882Sspwilson2@wisc.edu ''' 8112882Sspwilson2@wisc.edu Signals that an attribute in the config file was not initialized. 8212882Sspwilson2@wisc.edu ''' 8312882Sspwilson2@wisc.edu pass 8412882Sspwilson2@wisc.edu 8512882Sspwilson2@wisc.educlass UninitializedConfigException(Exception): 8612882Sspwilson2@wisc.edu ''' 8712882Sspwilson2@wisc.edu Signals that the config was not initialized before trying to access an 8812882Sspwilson2@wisc.edu attribute. 8912882Sspwilson2@wisc.edu ''' 9012882Sspwilson2@wisc.edu pass 9112882Sspwilson2@wisc.edu 9212882Sspwilson2@wisc.educlass TagRegex(object): 9312882Sspwilson2@wisc.edu def __init__(self, include, regex): 9412882Sspwilson2@wisc.edu self.include = include 9512882Sspwilson2@wisc.edu self.regex = re.compile(regex) 9612882Sspwilson2@wisc.edu 9712882Sspwilson2@wisc.edu def __str__(self): 9812882Sspwilson2@wisc.edu type_ = 'Include' if self.include else 'Remove' 9912882Sspwilson2@wisc.edu return '%10s: %s' % (type_, self.regex.pattern) 10012882Sspwilson2@wisc.edu 10112882Sspwilson2@wisc.educlass _Config(object): 10212882Sspwilson2@wisc.edu _initialized = False 10312882Sspwilson2@wisc.edu 10412882Sspwilson2@wisc.edu __shared_dict = {} 10512882Sspwilson2@wisc.edu 10612882Sspwilson2@wisc.edu constants = AttrDict() 10712882Sspwilson2@wisc.edu _defaults = AttrDict() 10812882Sspwilson2@wisc.edu _config = {} 10912882Sspwilson2@wisc.edu 11012882Sspwilson2@wisc.edu _cli_args = {} 11112882Sspwilson2@wisc.edu _post_processors = {} 11212882Sspwilson2@wisc.edu 11312882Sspwilson2@wisc.edu def __init__(self): 11412882Sspwilson2@wisc.edu # This object will act as if it were a singleton. 11512882Sspwilson2@wisc.edu self.__dict__ = self.__shared_dict 11612882Sspwilson2@wisc.edu 11712882Sspwilson2@wisc.edu def _init(self, parser): 11812882Sspwilson2@wisc.edu self._parse_commandline_args(parser) 11912882Sspwilson2@wisc.edu self._run_post_processors() 12012882Sspwilson2@wisc.edu self._initialized = True 12112882Sspwilson2@wisc.edu 12212882Sspwilson2@wisc.edu def _init_with_dicts(self, config, defaults): 12312882Sspwilson2@wisc.edu self._config = config 12412882Sspwilson2@wisc.edu self._defaults = defaults 12512882Sspwilson2@wisc.edu self._initialized = True 12612882Sspwilson2@wisc.edu 12712882Sspwilson2@wisc.edu def _add_post_processor(self, attr, post_processor): 12812882Sspwilson2@wisc.edu ''' 12912882Sspwilson2@wisc.edu :param attr: Attribute to pass to and recieve from the 13012882Sspwilson2@wisc.edu :func:`post_processor`. 13112882Sspwilson2@wisc.edu 13212882Sspwilson2@wisc.edu :param post_processor: A callback functions called in a chain to 13312882Sspwilson2@wisc.edu perform additional setup for a config argument. Should return a 13412882Sspwilson2@wisc.edu tuple containing the new value for the config attr. 13512882Sspwilson2@wisc.edu ''' 13612882Sspwilson2@wisc.edu if attr not in self._post_processors: 13712882Sspwilson2@wisc.edu self._post_processors[attr] = [] 13812882Sspwilson2@wisc.edu self._post_processors[attr].append(post_processor) 13912882Sspwilson2@wisc.edu 14012882Sspwilson2@wisc.edu def _set(self, name, value): 14112882Sspwilson2@wisc.edu self._config[name] = value 14212882Sspwilson2@wisc.edu 14312882Sspwilson2@wisc.edu def _parse_commandline_args(self, parser): 14412882Sspwilson2@wisc.edu args = parser.parse_args() 14512882Sspwilson2@wisc.edu 14612882Sspwilson2@wisc.edu self._config_file_args = {} 14712882Sspwilson2@wisc.edu 14812882Sspwilson2@wisc.edu for attr in dir(args): 14912882Sspwilson2@wisc.edu # Ignore non-argument attributes. 15012882Sspwilson2@wisc.edu if not attr.startswith('_'): 15112882Sspwilson2@wisc.edu self._config_file_args[attr] = getattr(args, attr) 15212882Sspwilson2@wisc.edu self._config.update(self._config_file_args) 15312882Sspwilson2@wisc.edu 15412882Sspwilson2@wisc.edu def _run_post_processors(self): 15512882Sspwilson2@wisc.edu for attr, callbacks in self._post_processors.items(): 15612882Sspwilson2@wisc.edu newval = self._lookup_val(attr) 15712882Sspwilson2@wisc.edu for callback in callbacks: 15812882Sspwilson2@wisc.edu newval = callback(newval) 15912882Sspwilson2@wisc.edu if newval is not None: 16012882Sspwilson2@wisc.edu newval = newval[0] 16112882Sspwilson2@wisc.edu self._set(attr, newval) 16212882Sspwilson2@wisc.edu 16312882Sspwilson2@wisc.edu 16412882Sspwilson2@wisc.edu def _lookup_val(self, attr): 16512882Sspwilson2@wisc.edu ''' 16612882Sspwilson2@wisc.edu Get the attribute from the config or fallback to defaults. 16712882Sspwilson2@wisc.edu 16812882Sspwilson2@wisc.edu :returns: If the value is not stored return None. Otherwise a tuple 16912882Sspwilson2@wisc.edu containing the value. 17012882Sspwilson2@wisc.edu ''' 17112882Sspwilson2@wisc.edu if attr in self._config: 17212882Sspwilson2@wisc.edu return (self._config[attr],) 17312882Sspwilson2@wisc.edu elif hasattr(self._defaults, attr): 17412882Sspwilson2@wisc.edu return (getattr(self._defaults, attr),) 17512882Sspwilson2@wisc.edu 17612882Sspwilson2@wisc.edu def __getattr__(self, attr): 17712882Sspwilson2@wisc.edu if attr in dir(super(_Config, self)): 17812882Sspwilson2@wisc.edu return getattr(super(_Config, self), attr) 17912882Sspwilson2@wisc.edu elif not self._initialized: 18012882Sspwilson2@wisc.edu raise UninitializedConfigException( 18112882Sspwilson2@wisc.edu 'Cannot directly access elements from the config before it is' 18212882Sspwilson2@wisc.edu ' initialized') 18312882Sspwilson2@wisc.edu else: 18412882Sspwilson2@wisc.edu val = self._lookup_val(attr) 18512882Sspwilson2@wisc.edu if val is not None: 18612882Sspwilson2@wisc.edu return val[0] 18712882Sspwilson2@wisc.edu else: 18812882Sspwilson2@wisc.edu raise UninitialzedAttributeException( 18912882Sspwilson2@wisc.edu '%s was not initialzed in the config.' % attr) 19012882Sspwilson2@wisc.edu 19112882Sspwilson2@wisc.edu def get_tags(self): 19212882Sspwilson2@wisc.edu d = {typ: set(self.__getattr__(typ)) 19312882Sspwilson2@wisc.edu for typ in self.constants.supported_tags} 19412882Sspwilson2@wisc.edu if any(map(lambda vals: bool(vals), d.values())): 19512882Sspwilson2@wisc.edu return d 19612882Sspwilson2@wisc.edu else: 19712882Sspwilson2@wisc.edu return {} 19812882Sspwilson2@wisc.edu 19912882Sspwilson2@wisc.edudef define_defaults(defaults): 20012882Sspwilson2@wisc.edu ''' 20112882Sspwilson2@wisc.edu Defaults are provided by the config if the attribute is not found in the 20212882Sspwilson2@wisc.edu config or commandline. For instance, if we are using the list command 20312882Sspwilson2@wisc.edu fixtures might not be able to count on the build_dir being provided since 20412882Sspwilson2@wisc.edu we aren't going to build anything. 20512882Sspwilson2@wisc.edu ''' 20612882Sspwilson2@wisc.edu defaults.base_dir = os.path.abspath(os.path.join(absdirpath(__file__), 20712882Sspwilson2@wisc.edu os.pardir, 20812882Sspwilson2@wisc.edu os.pardir)) 20912882Sspwilson2@wisc.edu defaults.result_path = os.path.join(os.getcwd(), '.testing-results') 21012882Sspwilson2@wisc.edu defaults.list_only_failed = False 21112882Sspwilson2@wisc.edu 21212882Sspwilson2@wisc.edudef define_constants(constants): 21312882Sspwilson2@wisc.edu ''' 21412882Sspwilson2@wisc.edu 'constants' are values not directly exposed by the config, but are attached 21512882Sspwilson2@wisc.edu to the object for centralized access. These should be used for setting 21612882Sspwilson2@wisc.edu common string names used across the test framework. A simple typo in 21712882Sspwilson2@wisc.edu a string can take a lot of debugging to uncover the issue, attribute errors 21812882Sspwilson2@wisc.edu are easier to notice and most autocompletion systems detect them. 21912882Sspwilson2@wisc.edu ''' 22012882Sspwilson2@wisc.edu constants.system_out_name = 'system-out' 22112882Sspwilson2@wisc.edu constants.system_err_name = 'system-err' 22212882Sspwilson2@wisc.edu 22312882Sspwilson2@wisc.edu constants.isa_tag_type = 'isa' 22412882Sspwilson2@wisc.edu constants.x86_tag = 'X86' 22512882Sspwilson2@wisc.edu constants.sparc_tag = 'SPARC' 22612882Sspwilson2@wisc.edu constants.alpha_tag = 'ALPHA' 22712882Sspwilson2@wisc.edu constants.riscv_tag = 'RISCV' 22812882Sspwilson2@wisc.edu constants.arm_tag = 'ARM' 22912882Sspwilson2@wisc.edu constants.mips_tag = 'MIPS' 23012882Sspwilson2@wisc.edu constants.power_tag = 'POWER' 23112882Sspwilson2@wisc.edu constants.null_tag = 'NULL' 23212882Sspwilson2@wisc.edu 23312882Sspwilson2@wisc.edu constants.variant_tag_type = 'variant' 23412882Sspwilson2@wisc.edu constants.opt_tag = 'opt' 23512882Sspwilson2@wisc.edu constants.debug_tag = 'debug' 23612882Sspwilson2@wisc.edu constants.fast_tag = 'fast' 23712882Sspwilson2@wisc.edu 23812882Sspwilson2@wisc.edu constants.length_tag_type = 'length' 23912882Sspwilson2@wisc.edu constants.quick_tag = 'quick' 24012882Sspwilson2@wisc.edu constants.long_tag = 'long' 24112882Sspwilson2@wisc.edu 24212882Sspwilson2@wisc.edu constants.supported_tags = { 24312882Sspwilson2@wisc.edu constants.isa_tag_type : ( 24412882Sspwilson2@wisc.edu constants.x86_tag, 24512882Sspwilson2@wisc.edu constants.sparc_tag, 24612882Sspwilson2@wisc.edu constants.alpha_tag, 24712882Sspwilson2@wisc.edu constants.riscv_tag, 24812882Sspwilson2@wisc.edu constants.arm_tag, 24912882Sspwilson2@wisc.edu constants.mips_tag, 25012882Sspwilson2@wisc.edu constants.power_tag, 25112882Sspwilson2@wisc.edu constants.null_tag, 25212882Sspwilson2@wisc.edu ), 25312882Sspwilson2@wisc.edu constants.variant_tag_type: ( 25412882Sspwilson2@wisc.edu constants.opt_tag, 25512882Sspwilson2@wisc.edu constants.debug_tag, 25612882Sspwilson2@wisc.edu constants.fast_tag, 25712882Sspwilson2@wisc.edu ), 25812882Sspwilson2@wisc.edu constants.length_tag_type: ( 25912882Sspwilson2@wisc.edu constants.quick_tag, 26012882Sspwilson2@wisc.edu constants.long_tag, 26112882Sspwilson2@wisc.edu ), 26212882Sspwilson2@wisc.edu } 26312882Sspwilson2@wisc.edu 26412882Sspwilson2@wisc.edu constants.supported_isas = constants.supported_tags['isa'] 26512882Sspwilson2@wisc.edu constants.supported_variants = constants.supported_tags['variant'] 26612882Sspwilson2@wisc.edu constants.supported_lengths = constants.supported_tags['length'] 26712882Sspwilson2@wisc.edu 26812882Sspwilson2@wisc.edu constants.tempdir_fixture_name = 'tempdir' 26912882Sspwilson2@wisc.edu constants.gem5_simulation_stderr = 'simerr' 27012882Sspwilson2@wisc.edu constants.gem5_simulation_stdout = 'simout' 27112882Sspwilson2@wisc.edu constants.gem5_simulation_stats = 'stats.txt' 27212882Sspwilson2@wisc.edu constants.gem5_simulation_config_ini = 'config.ini' 27312882Sspwilson2@wisc.edu constants.gem5_simulation_config_json = 'config.json' 27412882Sspwilson2@wisc.edu constants.gem5_returncode_fixture_name = 'gem5-returncode' 27512882Sspwilson2@wisc.edu constants.gem5_binary_fixture_name = 'gem5' 27612882Sspwilson2@wisc.edu constants.xml_filename = 'results.xml' 27712882Sspwilson2@wisc.edu constants.pickle_filename = 'results.pickle' 27812882Sspwilson2@wisc.edu constants.pickle_protocol = highest_pickle_protocol 27912882Sspwilson2@wisc.edu 28012882Sspwilson2@wisc.edu # The root directory which all test names will be based off of. 28112882Sspwilson2@wisc.edu constants.testing_base = absdirpath(os.path.join(absdirpath(__file__), 28212882Sspwilson2@wisc.edu os.pardir)) 28312882Sspwilson2@wisc.edu 28412882Sspwilson2@wisc.edudef define_post_processors(config): 28512882Sspwilson2@wisc.edu ''' 28612882Sspwilson2@wisc.edu post_processors are used to do final configuration of variables. This is 28712882Sspwilson2@wisc.edu useful if there is a dynamically set default, or some function that needs 28812882Sspwilson2@wisc.edu to be applied after parsing in order to set a configration value. 28912882Sspwilson2@wisc.edu 29012882Sspwilson2@wisc.edu Post processors must accept a single argument that will either be a tuple 29112882Sspwilson2@wisc.edu containing the already set config value or ``None`` if the config value 29212882Sspwilson2@wisc.edu has not been set to anything. They must return the modified value in the 29312882Sspwilson2@wisc.edu same format. 29412882Sspwilson2@wisc.edu ''' 29512882Sspwilson2@wisc.edu 29612882Sspwilson2@wisc.edu def set_default_build_dir(build_dir): 29712882Sspwilson2@wisc.edu ''' 29812882Sspwilson2@wisc.edu Post-processor to set the default build_dir based on the base_dir. 29912882Sspwilson2@wisc.edu 30012882Sspwilson2@wisc.edu .. seealso :func:`~_Config._add_post_processor` 30112882Sspwilson2@wisc.edu ''' 30212882Sspwilson2@wisc.edu if not build_dir or build_dir[0] is None: 30312882Sspwilson2@wisc.edu base_dir = config._lookup_val('base_dir')[0] 30412882Sspwilson2@wisc.edu build_dir = (os.path.join(base_dir, 'build'),) 30512882Sspwilson2@wisc.edu return build_dir 30612882Sspwilson2@wisc.edu 30712882Sspwilson2@wisc.edu def fix_verbosity_hack(verbose): 30812882Sspwilson2@wisc.edu return (verbose[0].val,) 30912882Sspwilson2@wisc.edu 31012882Sspwilson2@wisc.edu def threads_as_int(threads): 31112882Sspwilson2@wisc.edu if threads is not None: 31212882Sspwilson2@wisc.edu return (int(threads[0]),) 31312882Sspwilson2@wisc.edu 31412882Sspwilson2@wisc.edu def test_threads_as_int(test_threads): 31512882Sspwilson2@wisc.edu if test_threads is not None: 31612882Sspwilson2@wisc.edu return (int(test_threads[0]),) 31712882Sspwilson2@wisc.edu 31812882Sspwilson2@wisc.edu def default_isa(isa): 31912882Sspwilson2@wisc.edu if not isa[0]: 32012882Sspwilson2@wisc.edu return [constants.supported_tags[constants.isa_tag_type]] 32112882Sspwilson2@wisc.edu else: 32212882Sspwilson2@wisc.edu return isa 32312882Sspwilson2@wisc.edu 32412882Sspwilson2@wisc.edu def default_variant(variant): 32512882Sspwilson2@wisc.edu if not variant[0]: 32612882Sspwilson2@wisc.edu # Default variant is only opt. No need to run tests with multiple 32712882Sspwilson2@wisc.edu # different compilation targets 32812882Sspwilson2@wisc.edu return [[constants.opt_tag]] 32912882Sspwilson2@wisc.edu else: 33012882Sspwilson2@wisc.edu return variant 33112882Sspwilson2@wisc.edu 33212882Sspwilson2@wisc.edu def default_length(length): 33312882Sspwilson2@wisc.edu if not length[0]: 33412882Sspwilson2@wisc.edu return [[constants.quick_tag]] 33512882Sspwilson2@wisc.edu else: 33612882Sspwilson2@wisc.edu return length 33712882Sspwilson2@wisc.edu 33812882Sspwilson2@wisc.edu def compile_tag_regex(positional_tags): 33912882Sspwilson2@wisc.edu if not positional_tags: 34012882Sspwilson2@wisc.edu return positional_tags 34112882Sspwilson2@wisc.edu else: 34212882Sspwilson2@wisc.edu new_positional_tags_list = [] 34312882Sspwilson2@wisc.edu positional_tags = positional_tags[0] 34412882Sspwilson2@wisc.edu 34512882Sspwilson2@wisc.edu for flag, regex in positional_tags: 34612882Sspwilson2@wisc.edu if flag == 'exclude_tags': 34712882Sspwilson2@wisc.edu tag_regex = TagRegex(False, regex) 34812882Sspwilson2@wisc.edu elif flag == 'include_tags': 34912882Sspwilson2@wisc.edu tag_regex = TagRegex(True, regex) 35012882Sspwilson2@wisc.edu else: 35112882Sspwilson2@wisc.edu raise ValueError('Unsupported flag.') 35212882Sspwilson2@wisc.edu new_positional_tags_list.append(tag_regex) 35312882Sspwilson2@wisc.edu 35412882Sspwilson2@wisc.edu return (new_positional_tags_list,) 35512882Sspwilson2@wisc.edu 35612882Sspwilson2@wisc.edu config._add_post_processor('build_dir', set_default_build_dir) 35712882Sspwilson2@wisc.edu config._add_post_processor('verbose', fix_verbosity_hack) 35812882Sspwilson2@wisc.edu config._add_post_processor('isa', default_isa) 35912882Sspwilson2@wisc.edu config._add_post_processor('variant', default_variant) 36012882Sspwilson2@wisc.edu config._add_post_processor('length', default_length) 36112882Sspwilson2@wisc.edu config._add_post_processor('threads', threads_as_int) 36212882Sspwilson2@wisc.edu config._add_post_processor('test_threads', test_threads_as_int) 36312882Sspwilson2@wisc.edu config._add_post_processor(StorePositionalTagsAction.position_kword, 36412882Sspwilson2@wisc.edu compile_tag_regex) 36512882Sspwilson2@wisc.educlass Argument(object): 36612882Sspwilson2@wisc.edu ''' 36712882Sspwilson2@wisc.edu Class represents a cli argument/flag for a argparse parser. 36812882Sspwilson2@wisc.edu 36912882Sspwilson2@wisc.edu :attr name: The long name of this object that will be stored in the arg 37012882Sspwilson2@wisc.edu output by the final parser. 37112882Sspwilson2@wisc.edu ''' 37212882Sspwilson2@wisc.edu def __init__(self, *flags, **kwargs): 37312882Sspwilson2@wisc.edu self.flags = flags 37412882Sspwilson2@wisc.edu self.kwargs = kwargs 37512882Sspwilson2@wisc.edu 37612882Sspwilson2@wisc.edu if len(flags) == 0: 37712882Sspwilson2@wisc.edu raise ValueError("Need at least one argument.") 37812882Sspwilson2@wisc.edu elif 'dest' in kwargs: 37912882Sspwilson2@wisc.edu self.name = kwargs['dest'] 38012882Sspwilson2@wisc.edu elif len(flags) > 1 or flags[0].startswith('-'): 38112882Sspwilson2@wisc.edu for flag in flags: 38212882Sspwilson2@wisc.edu if not flag.startswith('-'): 38312882Sspwilson2@wisc.edu raise ValueError("invalid option string %s: must start" 38412882Sspwilson2@wisc.edu "with a character '-'" % flag) 38512882Sspwilson2@wisc.edu 38612882Sspwilson2@wisc.edu if flag.startswith('--'): 38712882Sspwilson2@wisc.edu if not hasattr(self, 'name'): 38812882Sspwilson2@wisc.edu self.name = flag.lstrip('-') 38912882Sspwilson2@wisc.edu 39012882Sspwilson2@wisc.edu if not hasattr(self, 'name'): 39112882Sspwilson2@wisc.edu self.name = flags[0].lstrip('-') 39212882Sspwilson2@wisc.edu self.name = self.name.replace('-', '_') 39312882Sspwilson2@wisc.edu 39412882Sspwilson2@wisc.edu def add_to(self, parser): 39512882Sspwilson2@wisc.edu '''Add this argument to the given parser.''' 39612882Sspwilson2@wisc.edu parser.add_argument(*self.flags, **self.kwargs) 39712882Sspwilson2@wisc.edu 39812882Sspwilson2@wisc.edu def copy(self): 39912882Sspwilson2@wisc.edu '''Copy this argument so you might modify any of its kwargs.''' 40012882Sspwilson2@wisc.edu return copy.deepcopy(self) 40112882Sspwilson2@wisc.edu 40212882Sspwilson2@wisc.edu 40312882Sspwilson2@wisc.educlass _StickyInt: 40412882Sspwilson2@wisc.edu ''' 40512882Sspwilson2@wisc.edu A class that is used to cheat the verbosity count incrementer by 40612882Sspwilson2@wisc.edu pretending to be an int. This makes the int stay on the heap and eat other 40712882Sspwilson2@wisc.edu real numbers when they are added to it. 40812882Sspwilson2@wisc.edu 40912882Sspwilson2@wisc.edu We use this so we can allow the verbose flag to be provided before or after 41012882Sspwilson2@wisc.edu the subcommand. This likely has no utility outside of this use case. 41112882Sspwilson2@wisc.edu ''' 41212882Sspwilson2@wisc.edu def __init__(self, val=0): 41312882Sspwilson2@wisc.edu self.val = val 41412882Sspwilson2@wisc.edu self.type = int 41512882Sspwilson2@wisc.edu def __add__(self, other): 41612882Sspwilson2@wisc.edu self.val += other 41712882Sspwilson2@wisc.edu return self 41812882Sspwilson2@wisc.edu 41912882Sspwilson2@wisc.educommon_args = NotImplemented 42012882Sspwilson2@wisc.edu 42112882Sspwilson2@wisc.educlass StorePositionAction(argparse.Action): 42212882Sspwilson2@wisc.edu '''Base class for classes wishing to create namespaces where 42312882Sspwilson2@wisc.edu arguments are stored in the order provided via the command line. 42412882Sspwilson2@wisc.edu ''' 42512882Sspwilson2@wisc.edu position_kword = 'positional' 42612882Sspwilson2@wisc.edu 42712882Sspwilson2@wisc.edu def __call__(self, parser, namespace, values, option_string=None): 42812882Sspwilson2@wisc.edu if not self.position_kword in namespace: 42912882Sspwilson2@wisc.edu setattr(namespace, self.position_kword, []) 43012882Sspwilson2@wisc.edu previous = getattr(namespace, self.position_kword) 43112882Sspwilson2@wisc.edu previous.append((self.dest, values)) 43212882Sspwilson2@wisc.edu setattr(namespace, self.position_kword, previous) 43312882Sspwilson2@wisc.edu 43412882Sspwilson2@wisc.educlass StorePositionalTagsAction(StorePositionAction): 43512882Sspwilson2@wisc.edu position_kword = 'tag_filters' 43612882Sspwilson2@wisc.edu 43712882Sspwilson2@wisc.edudef define_common_args(config): 43812882Sspwilson2@wisc.edu ''' 43912882Sspwilson2@wisc.edu Common args are arguments which are likely to be simular between different 44012882Sspwilson2@wisc.edu subcommands, so they are available to all by placing their definitions 44112882Sspwilson2@wisc.edu here. 44212882Sspwilson2@wisc.edu ''' 44312882Sspwilson2@wisc.edu global common_args 44412882Sspwilson2@wisc.edu 44512882Sspwilson2@wisc.edu # A list of common arguments/flags used across cli parsers. 44612882Sspwilson2@wisc.edu common_args = [ 44712882Sspwilson2@wisc.edu Argument( 44812882Sspwilson2@wisc.edu 'directory', 44912882Sspwilson2@wisc.edu nargs='?', 45012882Sspwilson2@wisc.edu default=os.getcwd(), 45112882Sspwilson2@wisc.edu help='Directory to start searching for tests in'), 45212882Sspwilson2@wisc.edu Argument( 45312882Sspwilson2@wisc.edu '--exclude-tags', 45412882Sspwilson2@wisc.edu action=StorePositionalTagsAction, 45512882Sspwilson2@wisc.edu help='A tag comparison used to select tests.'), 45612882Sspwilson2@wisc.edu Argument( 45712882Sspwilson2@wisc.edu '--include-tags', 45812882Sspwilson2@wisc.edu action=StorePositionalTagsAction, 45912882Sspwilson2@wisc.edu help='A tag comparison used to select tests.'), 46012882Sspwilson2@wisc.edu Argument( 46112882Sspwilson2@wisc.edu '--isa', 46212882Sspwilson2@wisc.edu action='append', 46312882Sspwilson2@wisc.edu default=[], 46412882Sspwilson2@wisc.edu help="Only tests that are valid with one of these ISAs. " 46512882Sspwilson2@wisc.edu "Comma separated."), 46612882Sspwilson2@wisc.edu Argument( 46712882Sspwilson2@wisc.edu '--variant', 46812882Sspwilson2@wisc.edu action='append', 46912882Sspwilson2@wisc.edu default=[], 47012882Sspwilson2@wisc.edu help="Only tests that are valid with one of these binary variants" 47112882Sspwilson2@wisc.edu "(e.g., opt, debug). Comma separated."), 47212882Sspwilson2@wisc.edu Argument( 47312882Sspwilson2@wisc.edu '--length', 47412882Sspwilson2@wisc.edu action='append', 47512882Sspwilson2@wisc.edu default=[], 47612882Sspwilson2@wisc.edu help="Only tests that are one of these lengths. Comma separated."), 47712882Sspwilson2@wisc.edu Argument( 47812882Sspwilson2@wisc.edu '--uid', 47912882Sspwilson2@wisc.edu action='store', 48012882Sspwilson2@wisc.edu default=None, 48112882Sspwilson2@wisc.edu help='UID of a specific test item to run.'), 48212882Sspwilson2@wisc.edu Argument( 48312882Sspwilson2@wisc.edu '--build-dir', 48412882Sspwilson2@wisc.edu action='store', 48512882Sspwilson2@wisc.edu help='Build directory for SCons'), 48612882Sspwilson2@wisc.edu Argument( 48712882Sspwilson2@wisc.edu '--base-dir', 48812882Sspwilson2@wisc.edu action='store', 48912882Sspwilson2@wisc.edu default=config._defaults.base_dir, 49012882Sspwilson2@wisc.edu help='Directory to change to in order to exec scons.'), 49112882Sspwilson2@wisc.edu Argument( 49212882Sspwilson2@wisc.edu '-j', '--threads', 49312882Sspwilson2@wisc.edu action='store', 49412882Sspwilson2@wisc.edu default=1, 49512882Sspwilson2@wisc.edu help='Number of threads to run SCons with.'), 49612882Sspwilson2@wisc.edu Argument( 49712882Sspwilson2@wisc.edu '-t', '--test-threads', 49812882Sspwilson2@wisc.edu action='store', 49912882Sspwilson2@wisc.edu default=1, 50012882Sspwilson2@wisc.edu help='Number of threads to spawn to run concurrent tests with.'), 50112882Sspwilson2@wisc.edu Argument( 50212882Sspwilson2@wisc.edu '-v', 50312882Sspwilson2@wisc.edu action='count', 50412882Sspwilson2@wisc.edu dest='verbose', 50512882Sspwilson2@wisc.edu default=_StickyInt(), 50612882Sspwilson2@wisc.edu help='Increase verbosity'), 50712882Sspwilson2@wisc.edu Argument( 50812882Sspwilson2@wisc.edu '--config-path', 50912882Sspwilson2@wisc.edu action='store', 51012882Sspwilson2@wisc.edu default=os.getcwd(), 51112882Sspwilson2@wisc.edu help='Path to read a testing.ini config in' 51212882Sspwilson2@wisc.edu ), 51312882Sspwilson2@wisc.edu Argument( 51412882Sspwilson2@wisc.edu '--skip-build', 51512882Sspwilson2@wisc.edu action='store_true', 51612882Sspwilson2@wisc.edu default=False, 51712882Sspwilson2@wisc.edu help='Skip the building component of SCons targets.' 51812882Sspwilson2@wisc.edu ), 51912882Sspwilson2@wisc.edu Argument( 52012882Sspwilson2@wisc.edu '--result-path', 52112882Sspwilson2@wisc.edu action='store', 52212882Sspwilson2@wisc.edu help='The path to store results in.' 52312882Sspwilson2@wisc.edu ), 52412882Sspwilson2@wisc.edu ] 52512882Sspwilson2@wisc.edu 52612882Sspwilson2@wisc.edu # NOTE: There is a limitation which arises due to this format. If you have 52712882Sspwilson2@wisc.edu # multiple arguments with the same name only the final one in the list 52812882Sspwilson2@wisc.edu # will be saved. 52912882Sspwilson2@wisc.edu # 53012882Sspwilson2@wisc.edu # e.g. if you have a -v argument which increments verbosity level and 53112882Sspwilson2@wisc.edu # a separate --verbose flag which 'store's verbosity level. the final 53212882Sspwilson2@wisc.edu # one in the list will be saved. 53312882Sspwilson2@wisc.edu common_args = AttrDict({arg.name:arg for arg in common_args}) 53412882Sspwilson2@wisc.edu 53512882Sspwilson2@wisc.edu 53612882Sspwilson2@wisc.educlass ArgParser(object): 53712882Sspwilson2@wisc.edu __metaclass__ = abc.ABCMeta 53812882Sspwilson2@wisc.edu 53912882Sspwilson2@wisc.edu def __init__(self, parser): 54012882Sspwilson2@wisc.edu # Copy public methods of the parser. 54112882Sspwilson2@wisc.edu for attr in dir(parser): 54212882Sspwilson2@wisc.edu if not attr.startswith('_'): 54312882Sspwilson2@wisc.edu setattr(self, attr, getattr(parser, attr)) 54412882Sspwilson2@wisc.edu self.parser = parser 54512882Sspwilson2@wisc.edu self.add_argument = self.parser.add_argument 54612882Sspwilson2@wisc.edu 54712882Sspwilson2@wisc.edu # Argument will be added to all parsers and subparsers. 54812882Sspwilson2@wisc.edu common_args.verbose.add_to(parser) 54912882Sspwilson2@wisc.edu 55012882Sspwilson2@wisc.edu 55112882Sspwilson2@wisc.educlass CommandParser(ArgParser): 55212882Sspwilson2@wisc.edu ''' 55312882Sspwilson2@wisc.edu Main parser which parses command strings and uses those to direct to 55412882Sspwilson2@wisc.edu a subparser. 55512882Sspwilson2@wisc.edu ''' 55612882Sspwilson2@wisc.edu def __init__(self): 55712882Sspwilson2@wisc.edu parser = argparse.ArgumentParser() 55812882Sspwilson2@wisc.edu super(CommandParser, self).__init__(parser) 55912882Sspwilson2@wisc.edu self.subparser = self.add_subparsers(dest='command') 56012882Sspwilson2@wisc.edu 56112882Sspwilson2@wisc.edu 56212882Sspwilson2@wisc.educlass RunParser(ArgParser): 56312882Sspwilson2@wisc.edu ''' 56412882Sspwilson2@wisc.edu Parser for the \'run\' command. 56512882Sspwilson2@wisc.edu ''' 56612882Sspwilson2@wisc.edu def __init__(self, subparser): 56712882Sspwilson2@wisc.edu parser = subparser.add_parser( 56812882Sspwilson2@wisc.edu 'run', 56912882Sspwilson2@wisc.edu help='''Run Tests.''' 57012882Sspwilson2@wisc.edu ) 57112882Sspwilson2@wisc.edu 57212882Sspwilson2@wisc.edu super(RunParser, self).__init__(parser) 57312882Sspwilson2@wisc.edu 57412882Sspwilson2@wisc.edu common_args.uid.add_to(parser) 57512882Sspwilson2@wisc.edu common_args.skip_build.add_to(parser) 57612882Sspwilson2@wisc.edu common_args.directory.add_to(parser) 57712882Sspwilson2@wisc.edu common_args.build_dir.add_to(parser) 57812882Sspwilson2@wisc.edu common_args.base_dir.add_to(parser) 57912882Sspwilson2@wisc.edu common_args.threads.add_to(parser) 58012882Sspwilson2@wisc.edu common_args.test_threads.add_to(parser) 58112882Sspwilson2@wisc.edu common_args.isa.add_to(parser) 58212882Sspwilson2@wisc.edu common_args.variant.add_to(parser) 58312882Sspwilson2@wisc.edu common_args.length.add_to(parser) 58412882Sspwilson2@wisc.edu common_args.include_tags.add_to(parser) 58512882Sspwilson2@wisc.edu common_args.exclude_tags.add_to(parser) 58612882Sspwilson2@wisc.edu 58712882Sspwilson2@wisc.edu 58812882Sspwilson2@wisc.educlass ListParser(ArgParser): 58912882Sspwilson2@wisc.edu ''' 59012882Sspwilson2@wisc.edu Parser for the \'list\' command. 59112882Sspwilson2@wisc.edu ''' 59212882Sspwilson2@wisc.edu def __init__(self, subparser): 59312882Sspwilson2@wisc.edu parser = subparser.add_parser( 59412882Sspwilson2@wisc.edu 'list', 59512882Sspwilson2@wisc.edu help='''List and query test metadata.''' 59612882Sspwilson2@wisc.edu ) 59712882Sspwilson2@wisc.edu super(ListParser, self).__init__(parser) 59812882Sspwilson2@wisc.edu 59912882Sspwilson2@wisc.edu Argument( 60012882Sspwilson2@wisc.edu '--suites', 60112882Sspwilson2@wisc.edu action='store_true', 60212882Sspwilson2@wisc.edu default=False, 60312882Sspwilson2@wisc.edu help='List all test suites.' 60412882Sspwilson2@wisc.edu ).add_to(parser) 60512882Sspwilson2@wisc.edu Argument( 60612882Sspwilson2@wisc.edu '--tests', 60712882Sspwilson2@wisc.edu action='store_true', 60812882Sspwilson2@wisc.edu default=False, 60912882Sspwilson2@wisc.edu help='List all test cases.' 61012882Sspwilson2@wisc.edu ).add_to(parser) 61112882Sspwilson2@wisc.edu Argument( 61212882Sspwilson2@wisc.edu '--fixtures', 61312882Sspwilson2@wisc.edu action='store_true', 61412882Sspwilson2@wisc.edu default=False, 61512882Sspwilson2@wisc.edu help='List all fixtures.' 61612882Sspwilson2@wisc.edu ).add_to(parser) 61712882Sspwilson2@wisc.edu Argument( 61812882Sspwilson2@wisc.edu '--all-tags', 61912882Sspwilson2@wisc.edu action='store_true', 62012882Sspwilson2@wisc.edu default=False, 62112882Sspwilson2@wisc.edu help='List all tags.' 62212882Sspwilson2@wisc.edu ).add_to(parser) 62312882Sspwilson2@wisc.edu Argument( 62412882Sspwilson2@wisc.edu '-q', 62512882Sspwilson2@wisc.edu dest='quiet', 62612882Sspwilson2@wisc.edu action='store_true', 62712882Sspwilson2@wisc.edu default=False, 62812882Sspwilson2@wisc.edu help='Quiet output (machine readable).' 62912882Sspwilson2@wisc.edu ).add_to(parser) 63012882Sspwilson2@wisc.edu 63112882Sspwilson2@wisc.edu common_args.directory.add_to(parser) 63212882Sspwilson2@wisc.edu common_args.isa.add_to(parser) 63312882Sspwilson2@wisc.edu common_args.variant.add_to(parser) 63412882Sspwilson2@wisc.edu common_args.length.add_to(parser) 63512882Sspwilson2@wisc.edu common_args.include_tags.add_to(parser) 63612882Sspwilson2@wisc.edu common_args.exclude_tags.add_to(parser) 63712882Sspwilson2@wisc.edu 63812882Sspwilson2@wisc.edu 63912882Sspwilson2@wisc.educlass RerunParser(ArgParser): 64012882Sspwilson2@wisc.edu def __init__(self, subparser): 64112882Sspwilson2@wisc.edu parser = subparser.add_parser( 64212882Sspwilson2@wisc.edu 'rerun', 64312882Sspwilson2@wisc.edu help='''Rerun failed tests.''' 64412882Sspwilson2@wisc.edu ) 64512882Sspwilson2@wisc.edu super(RerunParser, self).__init__(parser) 64612882Sspwilson2@wisc.edu 64712882Sspwilson2@wisc.edu common_args.skip_build.add_to(parser) 64812882Sspwilson2@wisc.edu common_args.directory.add_to(parser) 64912882Sspwilson2@wisc.edu common_args.build_dir.add_to(parser) 65012882Sspwilson2@wisc.edu common_args.base_dir.add_to(parser) 65112882Sspwilson2@wisc.edu common_args.threads.add_to(parser) 65212882Sspwilson2@wisc.edu common_args.test_threads.add_to(parser) 65312882Sspwilson2@wisc.edu common_args.isa.add_to(parser) 65412882Sspwilson2@wisc.edu common_args.variant.add_to(parser) 65512882Sspwilson2@wisc.edu common_args.length.add_to(parser) 65612882Sspwilson2@wisc.edu 65712882Sspwilson2@wisc.educonfig = _Config() 65812882Sspwilson2@wisc.edudefine_constants(config.constants) 65912882Sspwilson2@wisc.edu 66012882Sspwilson2@wisc.edu# Constants are directly exposed and available once this module is created. 66112882Sspwilson2@wisc.edu# All constants MUST be defined before this point. 66212882Sspwilson2@wisc.educonfig.constants = FrozenAttrDict(config.constants.__dict__) 66312882Sspwilson2@wisc.educonstants = config.constants 66412882Sspwilson2@wisc.edu 66512882Sspwilson2@wisc.edu''' 66612882Sspwilson2@wisc.eduThis config object is the singleton config object available throughout the 66712882Sspwilson2@wisc.eduframework. 66812882Sspwilson2@wisc.edu''' 66912882Sspwilson2@wisc.edudef initialize_config(): 67012882Sspwilson2@wisc.edu ''' 67112882Sspwilson2@wisc.edu Parse the commandline arguments and setup the config varibles. 67212882Sspwilson2@wisc.edu ''' 67312882Sspwilson2@wisc.edu global config 67412882Sspwilson2@wisc.edu 67512882Sspwilson2@wisc.edu # Setup constants and defaults 67612882Sspwilson2@wisc.edu define_defaults(config._defaults) 67712882Sspwilson2@wisc.edu define_post_processors(config) 67812882Sspwilson2@wisc.edu define_common_args(config) 67912882Sspwilson2@wisc.edu 68012882Sspwilson2@wisc.edu # Setup parser and subcommands 68112882Sspwilson2@wisc.edu baseparser = CommandParser() 68212882Sspwilson2@wisc.edu runparser = RunParser(baseparser.subparser) 68312882Sspwilson2@wisc.edu listparser = ListParser(baseparser.subparser) 68412882Sspwilson2@wisc.edu rerunparser = RerunParser(baseparser.subparser) 68512882Sspwilson2@wisc.edu 68612882Sspwilson2@wisc.edu # Initialize the config by parsing args and running callbacks. 68712882Sspwilson2@wisc.edu config._init(baseparser) 688