blob: 0e1210ff4fecacad7e6e67974b674dc6d3153630 [file] [log] [blame] [edit]
#!/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