| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 2022 F4PGA Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| from pathlib import Path |
| from copy import copy |
| from os import listdir as os_listdir |
| from json import dump as json_dump, load as json_load |
| |
| from f4pga.flows.common import ResolutionEnv, deep |
| from f4pga.flows.stage import Stage |
| |
| |
| def open_flow_cfg(path: str) -> dict: |
| with Path(path).open("r") as rfptr: |
| return json_load(rfptr) |
| |
| |
| def p_get_ovs_raw(dict_name: str, flow_cfg, part: "str | None", stage: "str | None"): |
| vals = flow_cfg.get(dict_name) |
| if vals is None: |
| vals = {} |
| if part is not None: |
| platform_vals = flow_cfg[part].get(dict_name) |
| if platform_vals is not None: |
| vals.update(platform_vals) |
| if stage is not None: |
| stage_deps = flow_cfg[part][stage].get(dict_name) |
| if stage_deps is not None: |
| vals.update(stage_deps) |
| |
| return vals |
| |
| |
| def verify_platform_name(platform: str, mypath: str): |
| for plat_def_filename in os_listdir(str(Path(mypath) / "platforms")): |
| platform_name = str(Path(plat_def_filename).stem) |
| if platform == platform_name: |
| return True |
| return False |
| |
| |
| class FlowDefinition: |
| stages: "dict[str, Stage]" # stage name -> module path mapping |
| 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) |
| |
| for stage_name, stage_def in flow_def["stages"].items(): |
| self.stages[stage_name] = Stage(stage_name, stage_def) |
| |
| def stage_names(self): |
| return self.stages.keys() |
| |
| |
| KWORDS = {"dependencies", "values", "default_platform", "default_target"} |
| |
| |
| class ProjectFlowConfig: |
| flow_cfg: dict |
| path: str |
| |
| def __init__(self, path: str): |
| self.flow_cfg = {} |
| self.path = copy(path) |
| |
| def parts(self): |
| for part in self.flow_cfg.keys(): |
| if part not in KWORDS: |
| yield part |
| |
| def get_default_part(self) -> "str | None": |
| return self.flow_cfg.get("default_part") |
| |
| def get_default_target(self, part: str) -> "str | None": |
| return self.flow_cfg[part].get("default_target") |
| |
| def get_dependencies_raw(self, part: "str | None" = None): |
| """ |
| Get dependencies without value resolution applied. |
| """ |
| return p_get_ovs_raw("dependencies", self.flow_cfg, part, None) |
| |
| def get_values_raw(self, part: "str | None" = None, stage: "str | None" = None): |
| """ |
| Get values without value resolution applied. |
| """ |
| return p_get_ovs_raw("values", self.flow_cfg, part, stage) |
| |
| def get_stage_value_overrides(self, part: str, stage: str): |
| stage_vals_ovds = {} |
| |
| vals = self.flow_cfg.get("values") |
| if vals is not None: |
| stage_vals_ovds.update(vals) |
| stage_cfg = self.flow_cfg[part].get(stage) |
| if stage_cfg is not None: |
| vals = stage_cfg.get("values") |
| if vals is not None: |
| stage_vals_ovds.update(vals) |
| |
| return stage_vals_ovds |
| |
| def get_dependency_platform_overrides(self, part: str): |
| platform_ovds = self.flow_cfg[part].get("dependencies") |
| if platform_ovds is None: |
| return {} |
| return platform_ovds |
| |
| |
| def override_prj_flow_cfg_by_cli(cfg: ProjectFlowConfig, cli_d: "dict[str, dict[str, dict]]"): |
| for part_name, part_cfg in cli_d.items(): |
| print(f"OVERRIDING CONFIG FOR {part_name}") |
| p_cfg = cfg.flow_cfg.get(part_name) |
| if p_cfg is None: |
| p_cfg = {} |
| cfg.flow_cfg[part_name] = p_cfg |
| cli_p_values = part_cfg.get("values") |
| cli_p_dependencies = part_cfg.get("dependencies") |
| p_values = p_cfg.get("values") |
| p_dependencies = p_cfg.get("dependencies") |
| if cli_p_values is not None: |
| if p_values is None: |
| p_values = {} |
| part_cfg["values"] = p_values |
| p_values.update(cli_p_values) |
| if cli_p_dependencies is not None: |
| if p_dependencies is None: |
| p_dependencies = {} |
| part_cfg["dependencies"] = p_dependencies |
| p_dependencies.update(cli_p_dependencies) |
| |
| for stage_name, cli_stage_cfg in part_cfg.items(): |
| if stage_name in KWORDS: |
| continue |
| |
| stage_cfg = part_cfg.get(stage_name) |
| if stage_cfg is None: |
| stage_cfg = {} |
| part_cfg[stage_name] = stage_cfg |
| |
| stage_values = stage_cfg.get("values") |
| stage_dependencies = stage_cfg.get("dependencies") |
| cli_stage_values = cli_stage_cfg.get("values") |
| cli_stage_dependencies = cli_stage_cfg.get("dependencies") |
| |
| if cli_stage_values is not None: |
| if stage_values is None: |
| stage_values = {} |
| stage_cfg["values"] = stage_values |
| stage_values.update(cli_stage_values) |
| if cli_stage_dependencies is not None: |
| if stage_dependencies is None: |
| stage_dependencies = {} |
| stage_cfg["dependencies"] = stage_dependencies |
| stage_dependencies.update(cli_stage_dependencies) |
| |
| |
| class FlowConfig: |
| part: str |
| r_env: ResolutionEnv |
| dependencies_explicit: "dict[str, ]" |
| stages: "dict[str, Stage]" |
| |
| def __init__(self, project_config: ProjectFlowConfig, platform_def: FlowDefinition, part: str): |
| self.r_env = platform_def.r_env |
| self.r_env.add_values(project_config.get_values_raw(part)) |
| self.stages = platform_def.stages |
| self.part = part |
| |
| self.dependencies_explicit = deep(lambda p: str(Path(p).resolve()), allow_none=True)( |
| self.r_env.resolve(project_config.get_dependencies_raw(part)) |
| ) |
| |
| for stage_name, stage in platform_def.stages.items(): |
| project_val_ovds = project_config.get_stage_value_overrides(part, 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) |
| with Path(path).open("r") as rfptr: |
| cfg.flow_cfg = json_load(rfptr) |
| return cfg |