mirror of
https://github.com/chipsalliance/f4pga.git
synced 2025-01-03 03:43:37 -05:00
cd2ad7144c
Signed-off-by: Krzysztof Boronski <kboronski@antmicro.com>
360 lines
No EOL
12 KiB
Python
360 lines
No EOL
12 KiB
Python
import os
|
|
import json
|
|
|
|
from sf_common import file_noext, ResolutionEnv, deep
|
|
from sf_stage import Stage
|
|
from copy import copy
|
|
|
|
_realpath_deep = deep(os.path.realpath)
|
|
|
|
def open_flow_cfg(path: str) -> dict:
|
|
flow_cfg_json: str
|
|
with open(path, 'r') as flow_cfg_file:
|
|
flow_cfg_json = flow_cfg_file.read()
|
|
return json.loads(flow_cfg_json)
|
|
|
|
def save_flow_cfg(flow: dict, path: str):
|
|
flow_cfg_json = json.dumps(flow, indent=4)
|
|
with open(path, 'w') as flow_cfg_file:
|
|
flow_cfg_file.write(flow_cfg_json)
|
|
|
|
def _get_lazy_dict(parent: dict, name: str):
|
|
d = parent.get(name)
|
|
if d is None:
|
|
d = {}
|
|
parent[name] = d
|
|
return d
|
|
|
|
def _get_ov_dict(dname: str, flow: dict,
|
|
platform: 'str | None' = None, stage: 'str | None' = None):
|
|
d: dict
|
|
if platform:
|
|
platform_dict: dict = flow[platform]
|
|
if stage:
|
|
stage_dict: dict = _get_lazy_dict(platform_dict, stage)
|
|
d = _get_lazy_dict(stage_dict, dname)
|
|
else:
|
|
d = _get_lazy_dict(platform_dict, dname)
|
|
else:
|
|
d = _get_lazy_dict(flow, dname)
|
|
|
|
return d
|
|
|
|
def _get_dep_dict(flow: dict,
|
|
platform: 'str | None' = None, stage: 'str | None' = None):
|
|
return _get_ov_dict('dependencies', flow, platform, stage)
|
|
|
|
def _get_vals_dict(flow: dict,
|
|
platform: 'str | None' = None, stage: 'str | None' = None):
|
|
return _get_ov_dict('values', flow, platform, stage)
|
|
|
|
def _add_ov(ov_dict_getter, failstr_constr, flow_cfg: dict, name: str,
|
|
values: list, platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
d = ov_dict_getter(flow_cfg, platform, stage)
|
|
|
|
deps = d.get(name)
|
|
if type(deps) is list:
|
|
deps += values
|
|
elif deps is None:
|
|
d[name] = values
|
|
else:
|
|
print(failstr_constr(name))
|
|
return False
|
|
|
|
return True
|
|
|
|
def _rm_ov_by_values(ov_dict_getter, notset_str_constr, notlist_str_constr,
|
|
flow: dict, name: str, vals: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
values_to_remove = set(vals)
|
|
d = ov_dict_getter(flow, platform, stage)
|
|
|
|
vallist: list = d.get(name)
|
|
if type(vallist) is list:
|
|
d[name] = [val for val in vallist if val not in values_to_remove]
|
|
elif type(vallist) is None:
|
|
print(notset_str_constr(name))
|
|
return False
|
|
else:
|
|
print(notlist_str_constr(name))
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def _rm_ov_by_idx(ov_dict_getter, notset_str_constr, notlist_str_constr,
|
|
flow: dict, name: str, idcs: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
idcs.sort(reverse=True)
|
|
|
|
if len(idcs) == 0:
|
|
print(f'Index list is emtpy!')
|
|
return False
|
|
|
|
d = ov_dict_getter(flow, platform, stage)
|
|
vallist: list = d.get(name)
|
|
if type(vallist) is list:
|
|
if idcs[0] >= len(vallist) or idcs[len(idcs) - 1] < 0:
|
|
print(f'Index out of range (max: {len(vallist)}!')
|
|
return False
|
|
|
|
for idx in idcs:
|
|
vallist.pop(idx)
|
|
elif vallist is None:
|
|
print(notset_str_constr(name))
|
|
return False
|
|
else:
|
|
print(notlist_str_constr(name))
|
|
return False
|
|
|
|
return True
|
|
|
|
def _get_ovs_raw(dict_name: str, flow_cfg,
|
|
platform: 'str | None', stage: 'str | None'):
|
|
vals = flow_cfg.get(dict_name)
|
|
if vals is None:
|
|
vals = {}
|
|
if platform is not None:
|
|
platform_vals= flow_cfg[platform].get(dict_name)
|
|
if platform_vals is not None:
|
|
vals.update(platform_vals)
|
|
if stage is not None:
|
|
stage_deps = flow_cfg[platform][stage].get(dict_name)
|
|
if stage_deps is not None:
|
|
vals.update(stage_deps)
|
|
|
|
return vals
|
|
|
|
def _remove_dependencies_by_values(flow: dict, name: str, deps: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
def notset_str_constr(dname):
|
|
return f'Dependency `{dname}` is not set. Nothing to remove.'
|
|
def notlist_str_constr(dname):
|
|
return f'Dependency `{dname}` is not a list! Use unsetd instead.'
|
|
return _rm_ov_by_values(_get_dep_dict, notset_str_constr, notlist_str_constr,
|
|
flow, name, deps, platform, stage)
|
|
|
|
def _remove_dependencies_by_idx(flow: dict, name: str, idcs: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
def notset_str_constr(dname):
|
|
return f'Dependency `{dname}` is not set. Nothing to remove.'
|
|
def notlist_str_constr(dname):
|
|
return f'Dependency `{dname}` is not a list! Use unsetd instead.'
|
|
return _rm_ov_by_idx(_get_dep_dict, notset_str_constr, notlist_str_constr,
|
|
flow, name, idcs, platform, stage)
|
|
|
|
def _remove_values_by_values(flow: dict, name: str, deps: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
def notset_str_constr(vname):
|
|
return f'Value `{vname}` is not set. Nothing to remove.'
|
|
def notlist_str_constr(vname):
|
|
return f'Value `{vname}` is not a list! Use unsetv instead.'
|
|
return _rm_ov_by_values(_get_vals_dict, notset_str_constr, notlist_str_constr,
|
|
flow, name, deps, platform, stage)
|
|
|
|
def _remove_values_by_idx(flow: dict, name: str, idcs: list,
|
|
platform: 'str | None' = None,
|
|
stage: 'str | None' = None) -> bool:
|
|
def notset_str_constr(dname):
|
|
return f'Dependency `{dname}` is not set. Nothing to remove.'
|
|
def notlist_str_constr(dname):
|
|
return f'Dependency `{dname}` is not a list! Use unsetv instead.'
|
|
return _rm_ov_by_idx(_get_vals_dict, notset_str_constr, notlist_str_constr,
|
|
flow, name, idcs, platform, stage)
|
|
|
|
def unset_dependency(flow: dict, name: str,
|
|
platform: 'str | None', stage: 'str | None'):
|
|
d = _get_dep_dict(flow, platform, stage)
|
|
if d.get(name) is None:
|
|
print(f'Dependency `{name}` is not set!')
|
|
return False
|
|
d.pop(name)
|
|
return True
|
|
|
|
def verify_platform_name(platform: str, mypath: str):
|
|
for plat_def_filename in os.listdir(os.path.join(mypath, 'platforms')):
|
|
platform_name = file_noext(plat_def_filename)
|
|
if platform == platform_name:
|
|
return True
|
|
return False
|
|
|
|
def verify_stage(platform: str, stage: str, mypath: str):
|
|
# TODO: Verify stage
|
|
return True
|
|
|
|
def _is_kword(w: str):
|
|
return \
|
|
(w == 'dependencies') | (w == 'values') | \
|
|
(w == 'default_platform') | (w == 'default_target')
|
|
|
|
class FlowDefinition:
|
|
# stage name -> module path mapping
|
|
stages: 'dict[str, Stage]'
|
|
r_env: ResolutionEnv
|
|
|
|
def __init__(self, flow_def: dict, r_env: ResolutionEnv):
|
|
self.flow_def = flow_def
|
|
self.r_env = r_env
|
|
self.stages = {}
|
|
|
|
global_vals = flow_def.get('values')
|
|
if global_vals is not None:
|
|
self.r_env.add_values(global_vals)
|
|
|
|
stages_d = flow_def['stages']
|
|
modopts_d = flow_def.get('stage_options')
|
|
if modopts_d is None:
|
|
modopts_d = {}
|
|
|
|
for stage_name, modstr in stages_d.items():
|
|
opts = modopts_d.get(stage_name)
|
|
self.stages[stage_name] = Stage(stage_name, modstr, opts)
|
|
|
|
def stage_names(self):
|
|
return self.stages.keys()
|
|
|
|
def get_stage_r_env(self, stage_name: 'str') -> ResolutionEnv:
|
|
stage = self.stages[stage_name]
|
|
r_env = copy(self.r_env)
|
|
r_env.add_values(stage.value_overrides)
|
|
return r_env
|
|
|
|
class ProjectFlowConfig:
|
|
flow_cfg: dict
|
|
# r_env: ResolutionEnv
|
|
path: str
|
|
# platform_r_envs: 'dict[str, ResolutionEnv]'
|
|
|
|
def __init__(self, path: str):
|
|
self.flow_cfg = {}
|
|
self.path = copy(path)
|
|
# self.r_env = ResolutionEnv({})
|
|
# self.platform_r_envs = {}
|
|
|
|
def platforms(self):
|
|
for platform, _ in self.flow_cfg.items():
|
|
if not _is_kword(platform):
|
|
yield platform
|
|
|
|
def add_platform(self, device: str) -> bool:
|
|
d = self.flow_cfg.get(device)
|
|
if d:
|
|
print(f'Device {device} already exists')
|
|
return False
|
|
|
|
self.flow_cfg[device] = {}
|
|
return True
|
|
|
|
def set_default_platform(self, device: str) -> bool:
|
|
self.flow_cfg['default_platform'] = device
|
|
return True
|
|
|
|
def set_default_target(self, platform: str, target: str) -> bool:
|
|
self.flow_cfg[platform]['default_target'] = target
|
|
return True
|
|
|
|
def get_default_platform(self) -> 'str | None':
|
|
return self.flow_cfg.get('default_platform')
|
|
|
|
def get_default_target(self, platform: str) -> 'str | None':
|
|
return self.flow_cfg[platform].get('default_target')
|
|
|
|
def get_stage_r_env(self, platform: str, stage: str) -> ResolutionEnv:
|
|
r_env = self._cache_platform_r_env(platform)
|
|
|
|
stage_cfg = self.flow_cfg[platform][stage]
|
|
stage_values = stage_cfg.get('values')
|
|
if stage_values:
|
|
r_env.add_values(stage_values)
|
|
|
|
return r_env
|
|
|
|
""" Get dependencies without value resolution applied """
|
|
def get_dependencies_raw(self, platform: 'str | None' = None):
|
|
return _get_ovs_raw('dependencies', self.flow_cfg, platform, None)
|
|
|
|
""" Get values without value resolution applied """
|
|
def get_values_raw(self, platform: 'str | None' = None,
|
|
stage: 'str | None' = None):
|
|
return _get_ovs_raw('values', self.flow_cfg, platform, stage)
|
|
|
|
def get_stage_value_overrides(self, platform: str, stage: str):
|
|
stage_cfg = self.flow_cfg[platform].get(stage)
|
|
if stage_cfg is None:
|
|
return {}
|
|
|
|
stage_vals_ovds = stage_cfg.get('values')
|
|
if stage_vals_ovds is None:
|
|
return {}
|
|
return stage_vals_ovds
|
|
|
|
def get_dependency_platform_overrides(self, platform: str):
|
|
platform_ovds = self.flow_cfg[platform].get('dependencies')
|
|
if platform_ovds is None:
|
|
return {}
|
|
return platform_ovds
|
|
|
|
|
|
class FlowConfig:
|
|
platform: str
|
|
r_env: ResolutionEnv
|
|
dependencies_explicit: 'dict[str, ]'
|
|
stages: 'dict[str, Stage]'
|
|
|
|
def __init__(self, project_config: ProjectFlowConfig,
|
|
platform_def: FlowDefinition, platform: str):
|
|
self.r_env = platform_def.r_env
|
|
platform_vals = project_config.get_values_raw(platform)
|
|
self.r_env.add_values(platform_vals)
|
|
self.stages = platform_def.stages
|
|
self.platform = platform
|
|
|
|
raw_project_deps = project_config.get_dependencies_raw(platform)
|
|
|
|
self.dependencies_explicit = \
|
|
_realpath_deep(self.r_env.resolve(raw_project_deps))
|
|
|
|
for stage_name, stage in platform_def.stages.items():
|
|
project_val_ovds = \
|
|
project_config.get_stage_value_overrides(platform, stage_name)
|
|
stage.value_overrides.update(project_val_ovds)
|
|
|
|
def get_dependency_overrides(self):
|
|
return self.dependencies_explicit
|
|
|
|
def get_r_env(self, stage_name: str) -> ResolutionEnv:
|
|
stage = self.stages[stage_name]
|
|
r_env = copy(self.r_env)
|
|
r_env.add_values(stage.value_overrides)
|
|
|
|
return r_env
|
|
|
|
def get_stage(self, stage_name: str) -> Stage:
|
|
return self.stages[stage_name]
|
|
|
|
class FlowConfigException(Exception):
|
|
path: str
|
|
message: str
|
|
|
|
def __init__(self, path: str, message: str):
|
|
self.path = path
|
|
self.message = message
|
|
|
|
def __str__(self) -> str:
|
|
return f'Error in config `{self.path}: {self.message}'
|
|
|
|
def open_project_flow_cfg(path: str) -> ProjectFlowConfig:
|
|
cfg = ProjectFlowConfig(path)
|
|
|
|
flow_cfg_json: str
|
|
with open(path, 'r') as flow_cfg_file:
|
|
flow_cfg_json = flow_cfg_file.read()
|
|
cfg.flow_cfg = json.loads(flow_cfg_json)
|
|
|
|
return cfg |