ibex/util/ibex_config.py
2020-03-27 10:30:46 +00:00

207 lines
6.2 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
import argparse
import collections
import os
import shlex
import sys
import yaml
_DEFAULT_CONFIG_FILE = 'ibex_configs.yaml'
class ConfigException(Exception):
pass
def _verify_config(name, config_dict):
"""Checks a config_dict matches expectations.
A config_dict is the dictionary mapping parameters to values for a
particular config, it must obey the following rules:
- It's a mapping object e.g. OrderedDict
- Its values can only be strings, integers or booleans
Args:
name: The name of the config being checked (used to form useful error
messages)
config_dict: The config_dict to check, must be a mapping object
Returns:
Nothing, an exception is thrown if an issue is found
Raises:
ConfigException: An issue was found with config_dict
"""
if not isinstance(config_dict, collections.Mapping):
raise ConfigException('Config ' + name +
' must have dictionary giving parameters')
for k, v in config_dict.items():
if isinstance(v, int):
continue
if isinstance(v, str):
continue
if isinstance(v, bool):
continue
raise ConfigException('Parameter ' + k + ' for config ' + name +
' must be string, int or bool got ' +
str(type(v)))
def _verify_config_parameters(config_dicts):
"""Verifies all parameters across config_dicts match expectations.
Each configuration must obey the following fules:
- Each config has the same set of parameters specified
Args:
config_dicts: A dictionary of configurations, maps from configuration
name to a configuration (itself a dictionary)
Returns:
Nothing, an exception is thrown if an issue is found
Raises:
ConfigException: An issue was found with config_dicts
"""
parameters = set()
first = True
for name, config_dict in config_dicts.items():
parameters_this_config = set()
for parameter, value in config_dict.items():
if first:
parameters.add(parameter)
parameters_this_config.add(parameter)
if first:
first = False
else:
parameter_difference = parameters ^ parameters_this_config
if parameter_difference:
raise ConfigException('Config ' + name +
' has differing parameters ' +
','.join(parameter_difference))
def get_config_dicts(config_file):
"""Extracts a dictionary of configuration dictionaries from a file object
Given a file object parses YAML from it to obtain a dictionary of
configurations
Args:
config_file: A file object for a file containing the YAML configuration
file
Returns:
A dictionary of configurations, maps from a configuration name to a
configuration (itself a dictionary mapping parameters to values)
Raises:
ConfigException: An issue was found with the configuration file
"""
try:
config_yaml = yaml.load(config_file, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
raise ConfigException('Could not decode yaml:\n' + str(e))
for k, v in config_yaml.items():
_verify_config(k, v)
_verify_config_parameters(config_yaml)
return config_yaml
def _config_dict_to_fusesoc_opts(config_dict):
fusesoc_cmd = []
for parameter, value in config_dict.items():
if isinstance(value, bool):
# For fusesoc boolean parameter are set to true if given on the
# command line otherwise false, it doesn't support an explicit
# --param=True style
if value:
fusesoc_cmd.append(shlex.quote('--' + parameter))
else:
fusesoc_cmd.append(shlex.quote('--' + parameter + '=' + str(value)))
return ' '.join(fusesoc_cmd)
def get_config_file_location():
"""Returns the location of the config file
Default is _DEFAULT_CONFIG_FILE and the IBEX_CONFIG_FILE environment
variable overrides the default"""
return os.environ.get('IBEX_CONFIG_FILE', _DEFAULT_CONFIG_FILE)
def main():
config_outputs = {'fusesoc_opts': _config_dict_to_fusesoc_opts}
argparser = argparse.ArgumentParser(description=(
'Outputs Ibex configuration '
'parameters for a named config in a number of formats. If not '
'specified on the command line the config will be read from {0}. This '
'default can be overridden by setting the IBEX_CONFIG_FILE environment '
'variable').format(get_config_file_location()))
argparser.add_argument('config_name',
help=('The name of the Ibex '
'configuration to output'))
argparser.add_argument('output_type',
help=('Format to output the '
'configuration parameters in'),
choices=config_outputs.keys())
argparser.add_argument('--config_filename',
help='Config file to read',
default=get_config_file_location())
args = argparser.parse_args()
try:
config_file = open(args.config_filename)
config_dicts = get_config_dicts(config_file)
if args.config_name not in config_dicts:
print('ERROR: configuration',
args.config_name,
'not found in',
args.config_filename,
file=sys.stderr)
sys.exit(1)
print(config_outputs[args.output_type](config_dicts[args.config_name]))
except ConfigException as ce:
print('ERROR: failure to read configuration from',
args.config_filename,
ce,
file=sys.stderr)
sys.exit(1)
except FileNotFoundError:
print('ERROR: could not find configuration file',
args.config_filename,
file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()