f4pga: cleanup

Signed-off-by: Unai Martinez-Corral <umartinezcorral@antmicro.com>
This commit is contained in:
Unai Martinez-Corral 2022-08-15 03:31:39 +02:00
parent eb6b0e5c78
commit 86f281349f
1 changed files with 58 additions and 32 deletions

View File

@ -41,7 +41,7 @@ from pathlib import Path
from argparse import Namespace from argparse import Namespace
from sys import argv as sys_argv from sys import argv as sys_argv
from os import environ from os import environ
from json import load as json_load, loads as json_loads from json import load as json_load
from typing import Iterable from typing import Iterable
from colorama import Fore, Style from colorama import Fore, Style
@ -72,7 +72,7 @@ F4CACHEPATH = '.f4cache'
install_dir = environ.get("F4PGA_INSTALL_DIR", "/usr/local") install_dir = environ.get("F4PGA_INSTALL_DIR", "/usr/local")
mypath = str(Path(__file__).resolve().parent) ROOT = Path(__file__).resolve().parent
FPGA_FAM = environ.get('FPGA_FAM', 'xc7') FPGA_FAM = environ.get('FPGA_FAM', 'xc7')
@ -81,6 +81,7 @@ share_dir_path = \
environ.get('F4PGA_SHARE_DIR', environ.get('F4PGA_SHARE_DIR',
str(Path(f'{install_dir}/{FPGA_FAM}/share/f4pga').resolve())) str(Path(f'{install_dir}/{FPGA_FAM}/share/f4pga').resolve()))
class DependencyNotProducedException(F4PGAException): class DependencyNotProducedException(F4PGAException):
dep_name: str dep_name: str
provider: str provider: str
@ -91,9 +92,11 @@ class DependencyNotProducedException(F4PGAException):
self.message = f'Stage `{self.provider}` did not produce promised ' \ self.message = f'Stage `{self.provider}` did not produce promised ' \
f'dependency `{self.dep_name}`' f'dependency `{self.dep_name}`'
def dep_value_str(dep: str): def dep_value_str(dep: str):
return ':' + dep return ':' + dep
def platform_stages(platform_flow, r_env): def platform_stages(platform_flow, r_env):
""" Iterates over all stages available in a given flow. """ """ Iterates over all stages available in a given flow. """
@ -102,6 +105,7 @@ def platform_stages(platform_flow, r_env):
mod_opts = stage_options.get(stage_name) if stage_options else None mod_opts = stage_options.get(stage_name) if stage_options else None
yield Stage(stage_name, modulestr, mod_opts, r_env) yield Stage(stage_name, modulestr, mod_opts, r_env)
def req_exists(r): def req_exists(r):
""" Checks whether a dependency exists on a drive. """ """ Checks whether a dependency exists on a drive. """
@ -115,6 +119,7 @@ def req_exists(r):
f'paths, or path lists (reason: {r})') f'paths, or path lists (reason: {r})')
return True return True
def map_outputs_to_stages(stages: 'list[Stage]'): def map_outputs_to_stages(stages: 'list[Stage]'):
""" """
Associates a stage with every possible output. Associates a stage with every possible output.
@ -133,18 +138,22 @@ def map_outputs_to_stages(stages: 'list[Stage]'):
'provider at most.') 'provider at most.')
return os_map return os_map
def filter_existing_deps(deps: 'dict[str, ]', f4cache): def filter_existing_deps(deps: 'dict[str, ]', f4cache):
return [(n, p) for n, p in deps.items() \ return [(n, p) for n, p in deps.items() \
if req_exists(p)] # and not dep_differ(p, f4cache)] if req_exists(p)] # and not dep_differ(p, f4cache)]
def get_stage_values_override(og_values: dict, stage: Stage): def get_stage_values_override(og_values: dict, stage: Stage):
values = og_values.copy() values = og_values.copy()
values.update(stage.value_ovds) values.update(stage.value_ovds)
return values return values
def prepare_stage_io_input(stage: Stage): def prepare_stage_io_input(stage: Stage):
return { 'params': stage.params } if stage.params is not None else {} return { 'params': stage.params } if stage.params is not None else {}
def prepare_stage_input(stage: Stage, values: dict, dep_paths: 'dict[str, ]', def prepare_stage_input(stage: Stage, values: dict, dep_paths: 'dict[str, ]',
config_paths: 'dict[str, ]'): config_paths: 'dict[str, ]'):
takes = {} takes = {}
@ -168,6 +177,7 @@ def prepare_stage_input(stage: Stage, values: dict, dep_paths: 'dict[str, ]',
return stage_mod_cfg return stage_mod_cfg
def update_dep_statuses(paths, consumer: str, f4cache: F4Cache): def update_dep_statuses(paths, consumer: str, f4cache: F4Cache):
if type(paths) is str: if type(paths) is str:
return f4cache.update(Path(paths), consumer) return f4cache.update(Path(paths), consumer)
@ -179,6 +189,7 @@ def update_dep_statuses(paths, consumer: str, f4cache: F4Cache):
return update_dep_statuses(p, consumer, f4cache) return update_dep_statuses(p, consumer, f4cache)
fatal(-1, 'WRONG PATHS TYPE') fatal(-1, 'WRONG PATHS TYPE')
def dep_differ(paths, consumer: str, f4cache: F4Cache): def dep_differ(paths, consumer: str, f4cache: F4Cache):
""" """
Check if a dependency differs from its last version, lack of dependency is Check if a dependency differs from its last version, lack of dependency is
@ -195,6 +206,8 @@ def dep_differ(paths, consumer: str, f4cache: F4Cache):
return True in [dep_differ(p, consumer, f4cache) \ return True in [dep_differ(p, consumer, f4cache) \
for _, p in paths.items()] for _, p in paths.items()]
return False return False
def dep_will_differ(target: str, paths, consumer: str, def dep_will_differ(target: str, paths, consumer: str,
os_map: 'dict[str, Stage]', run_stages: 'set[str]', os_map: 'dict[str, Stage]', run_stages: 'set[str]',
f4cache: F4Cache): f4cache: F4Cache):
@ -209,12 +222,14 @@ def dep_will_differ(target: str, paths, consumer: str,
dep_differ(paths, consumer, f4cache) dep_differ(paths, consumer, f4cache)
return dep_differ(paths, consumer, f4cache) return dep_differ(paths, consumer, f4cache)
def _print_unreachable_stage_message(provider: Stage, take: str): def _print_unreachable_stage_message(provider: Stage, take: str):
sfprint(0, ' Stage ' sfprint(0, ' Stage '
f'`{Style.BRIGHT + provider.name + Style.RESET_ALL}` is ' f'`{Style.BRIGHT + provider.name + Style.RESET_ALL}` is '
'unreachable due to unmet dependency ' 'unreachable due to unmet dependency '
f'`{Style.BRIGHT + take.name + Style.RESET_ALL}`') f'`{Style.BRIGHT + take.name + Style.RESET_ALL}`')
def config_mod_runctx(stage: Stage, values: 'dict[str, ]', def config_mod_runctx(stage: Stage, values: 'dict[str, ]',
dep_paths: 'dict[str, str | list[str]]', dep_paths: 'dict[str, str | list[str]]',
config_paths: 'dict[str, str | list[str]]'): config_paths: 'dict[str, str | list[str]]'):
@ -222,6 +237,7 @@ def config_mod_runctx(stage: Stage, values: 'dict[str, ]',
dep_paths, config_paths) dep_paths, config_paths)
return ModRunCtx(share_dir_path, bin_dir_path, config) return ModRunCtx(share_dir_path, bin_dir_path, config)
def _process_dep_path(path: str, f4cache: F4Cache): def _process_dep_path(path: str, f4cache: F4Cache):
f4cache.process_file(Path(path)) f4cache.process_file(Path(path))
_cache_deps = deep(_process_dep_path) _cache_deps = deep(_process_dep_path)
@ -452,6 +468,7 @@ class Flow:
sfprint(0, f'Target {Style.BRIGHT + self.target + Style.RESET_ALL} ' sfprint(0, f'Target {Style.BRIGHT + self.target + Style.RESET_ALL} '
f'-> {self.dep_paths[self.target]}') f'-> {self.dep_paths[self.target]}')
def display_dep_info(stages: 'Iterable[Stage]'): def display_dep_info(stages: 'Iterable[Stage]'):
sfprint(0, 'Platform dependencies/targets:') sfprint(0, 'Platform dependencies/targets:')
longest_out_name_len = 0 longest_out_name_len = 0
@ -485,10 +502,11 @@ def display_dep_info(stages: 'Iterable[Stage]'):
sfprint(0, f' {Style.BRIGHT + out.name + Style.RESET_ALL}:' sfprint(0, f' {Style.BRIGHT + out.name + Style.RESET_ALL}:'
f'{indent}{pdesc}{nl_indentstr}{pgen}') f'{indent}{pdesc}{nl_indentstr}{pgen}')
def display_stage_info(stage: Stage): def display_stage_info(stage: Stage):
if stage is None: if stage is None:
sfprint(0, f'Stage does not exist') sfprint(0, f'Stage does not exist')
sfbuild_fail() f4pga_fail()
return return
sfprint(0, f'Stage `{Style.BRIGHT}{stage.name}{Style.RESET_ALL}`:') sfprint(0, f'Stage `{Style.BRIGHT}{stage.name}{Style.RESET_ALL}`:')
@ -500,18 +518,21 @@ def display_stage_info(stage: Stage):
sfprint(0, f' {mod_info}') sfprint(0, f' {mod_info}')
sfbuild_done_str = Style.BRIGHT + Fore.GREEN + 'DONE'
sfbuild_silent = 0
def sfbuild_fail(): f4pga_done_str = Style.BRIGHT + Fore.GREEN + 'DONE'
global sfbuild_done_str
sfbuild_done_str = Style.BRIGHT + Fore.RED + 'FAILED'
def sfbuild_done():
sfprint(1, f'f4pga: {sfbuild_done_str}' def f4pga_fail():
global f4pga_done_str
f4pga_done_str = Style.BRIGHT + Fore.RED + 'FAILED'
def f4pga_done():
sfprint(1, f'f4pga: {f4pga_done_str}'
f'{Style.RESET_ALL + Fore.RESET}') f'{Style.RESET_ALL + Fore.RESET}')
exit(0) exit(0)
def setup_resolution_env(): def setup_resolution_env():
""" Sets up a ResolutionEnv with default built-ins. """ """ Sets up a ResolutionEnv with default built-ins. """
@ -543,6 +564,7 @@ def setup_resolution_env():
r_env.add_values(_generate_values()) r_env.add_values(_generate_values())
return r_env return r_env
def open_project_flow_config(path: str) -> ProjectFlowConfig: def open_project_flow_config(path: str) -> ProjectFlowConfig:
try: try:
flow_cfg = open_project_flow_cfg(path) flow_cfg = open_project_flow_cfg(path)
@ -550,11 +572,12 @@ def open_project_flow_config(path: str) -> ProjectFlowConfig:
fatal(-1, 'The provided flow configuration file does not exist') fatal(-1, 'The provided flow configuration file does not exist')
return flow_cfg return flow_cfg
def verify_part_stage_params(flow_cfg: FlowConfig, def verify_part_stage_params(flow_cfg: FlowConfig,
part: 'str | None' = None): part: 'str | None' = None):
if part: if part:
platform_name = get_platform_name_for_part(part) platform_name = get_platform_name_for_part(part)
if not verify_platform_name(platform_name, mypath): if not verify_platform_name(platform_name, str(ROOT)):
sfprint(0, f'Platform `{part}`` is unsupported.') sfprint(0, f'Platform `{part}`` is unsupported.')
return False return False
if part not in flow_cfg.part(): if part not in flow_cfg.part():
@ -563,17 +586,19 @@ def verify_part_stage_params(flow_cfg: FlowConfig,
return True return True
def get_platform_name_for_part(part_name: str): def get_platform_name_for_part(part_name: str):
""" """
Gets a name that identifies the platform setup required for a specific chip. Gets a name that identifies the platform setup required for a specific chip.
The reason for such distinction is that plenty of chips with different names The reason for such distinction is that plenty of chips with different names
differ only in a type of package they use. differ only in a type of package they use.
""" """
with (Path(mypath) / 'part_db.json').open('r') as rfptr: with (ROOT / 'part_db.json').open('r') as rfptr:
for key, val in json_load(rfptr).items(): for key, val in json_load(rfptr).items():
if part_name.upper() in val: if part_name.upper() in val:
return key return key
raise(Exception(f"Unknown part name <{part_name}>!")) raise Exception(f"Unknown part name <{part_name}>!")
def make_flow_config(project_flow_cfg: ProjectFlowConfig, part_name: str) -> FlowConfig: def make_flow_config(project_flow_cfg: ProjectFlowConfig, part_name: str) -> FlowConfig:
""" Create `FlowConfig` from given project flow configuration and part name """ """ Create `FlowConfig` from given project flow configuration and part name """
@ -592,22 +617,20 @@ def make_flow_config(project_flow_cfg: ProjectFlowConfig, part_name: str) -> Flo
r_env = setup_resolution_env() r_env = setup_resolution_env()
r_env.add_values({'part_name': part_name.lower()}) r_env.add_values({'part_name': part_name.lower()})
scan_modules(mypath) scan_modules(str(ROOT))
platform_path = str(Path(mypath) / f'platforms/{platform}.json') platform_path = ROOT / f'platforms/{platform}.json'
platform_def = None if not platform_path.exists():
try:
with open(platform_path) as platform_file:
platform_def = platform_file.read()
except FileNotFoundError as _:
raise F4PGAException( raise F4PGAException(
message=f'The platform flow definition file {platform_path} for the platform ' \ message=f'The platform flow definition file {platform_path} for the platform ' \
f'{platform} cannot be found.' f'{platform} cannot be found.'
) )
with platform_path.open('r') as rfptr:
flow_definition_dict = json_loads(platform_def) flow_cfg = FlowConfig(
flow_def = FlowDefinition(flow_definition_dict, r_env) project_flow_cfg,
flow_cfg = FlowConfig(project_flow_cfg, flow_def, part_name) FlowDefinition(json_load(rfptr), r_env),
part_name
)
if len(flow_cfg.stages) == 0: if len(flow_cfg.stages) == 0:
raise F4PGAException(message = 'Platform flow does not define any stage') raise F4PGAException(message = 'Platform flow does not define any stage')
@ -638,11 +661,11 @@ def cmd_build(args: Namespace):
if args.info: if args.info:
display_dep_info(flow_cfg.stages.values()) display_dep_info(flow_cfg.stages.values())
sfbuild_done() f4pga_done()
if args.stageinfo: if args.stageinfo:
display_stage_info(flow_cfg.stages.get(args.stageinfo[0])) display_stage_info(flow_cfg.stages.get(args.stageinfo[0]))
sfbuild_done() f4pga_done()
target = args.target target = args.target
if target is None: if target is None:
@ -663,7 +686,7 @@ def cmd_build(args: Namespace):
sfprint(dep_print_verbosity, '') sfprint(dep_print_verbosity, '')
if args.pretend: if args.pretend:
sfbuild_done() f4pga_done()
try: try:
flow.execute() flow.execute()
@ -671,18 +694,19 @@ def cmd_build(args: Namespace):
raise e raise e
except Exception as e: except Exception as e:
sfprint(0, f'{e}') sfprint(0, f'{e}')
sfbuild_fail() f4pga_fail()
if flow.f4cache: if flow.f4cache:
flow.f4cache.save() flow.f4cache.save()
def cmd_show_dependencies(args: Namespace): def cmd_show_dependencies(args: Namespace):
""" `showd` command implementation """ """ `showd` command implementation """
flow_cfg = open_project_flow_config(args.flow) flow_cfg = open_project_flow_config(args.flow)
if not verify_part_stage_params(flow_cfg, args.part): if not verify_part_stage_params(flow_cfg, args.part):
sfbuild_fail() f4pga_fail()
return return
platform_overrides: 'set | None' = None platform_overrides: 'set | None' = None
@ -711,6 +735,7 @@ def cmd_show_dependencies(args: Namespace):
set_verbosity_level(-1) set_verbosity_level(-1)
def main(): def main():
parser = setup_argparser() parser = setup_argparser()
args = parser.parse_args() args = parser.parse_args()
@ -719,14 +744,15 @@ def main():
if args.command == 'build': if args.command == 'build':
cmd_build(args) cmd_build(args)
sfbuild_done() f4pga_done()
if args.command == 'showd': if args.command == 'showd':
cmd_show_dependencies(args) cmd_show_dependencies(args)
sfbuild_done() f4pga_done()
sfprint(0, 'Please use a command.\nUse `--help` flag to learn more.') sfprint(0, 'Please use a command.\nUse `--help` flag to learn more.')
sfbuild_done() f4pga_done()
if __name__ == '__main__': if __name__ == '__main__':
main() main()