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>
110 lines
No EOL
3.3 KiB
Python
110 lines
No EOL
3.3 KiB
Python
""" Dynamically import and run sfbuild modules """
|
|
|
|
from contextlib import contextmanager
|
|
import importlib
|
|
import os
|
|
from sf_module import Module, ModuleContext, get_mod_metadata
|
|
from sf_common import ResolutionEnv, deep, sfprint
|
|
from colorama import Fore, Style
|
|
|
|
_realpath_deep = deep(os.path.realpath)
|
|
|
|
@contextmanager
|
|
def _add_to_sys_path(path: str):
|
|
import sys
|
|
old_syspath = sys.path
|
|
sys.path = [path] + sys.path
|
|
try:
|
|
yield
|
|
finally:
|
|
sys.path = old_syspath
|
|
|
|
def import_module_from_path(path: str):
|
|
absolute_path = os.path.realpath(path)
|
|
with _add_to_sys_path(path):
|
|
spec = importlib.util.spec_from_file_location(absolute_path, absolute_path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
# Once imported a module will be added to that dict to avaid re-importing it
|
|
preloaded_modules = {}
|
|
|
|
def get_module(path: str):
|
|
global preloaded_modules
|
|
|
|
cached = preloaded_modules.get(path)
|
|
if cached:
|
|
return cached.ModuleClass
|
|
|
|
mod = import_module_from_path(path)
|
|
preloaded_modules[path] = mod
|
|
|
|
# All sfbuild modules should expose a `ModuleClass` type/alias which is a
|
|
# class implementing a Module interface
|
|
return mod.ModuleClass
|
|
|
|
class ModRunCtx:
|
|
share: str
|
|
bin: str
|
|
config: 'dict[str, ]'
|
|
|
|
def __init__(self, share: str, bin: str, config: 'dict[str, ]'):
|
|
self.share = share
|
|
self.bin = bin
|
|
self.config = config
|
|
|
|
def make_r_env(self):
|
|
return ResolutionEnv(self.config['values'])
|
|
|
|
class ModuleFailException(Exception):
|
|
module: str
|
|
mode: str
|
|
e: Exception
|
|
|
|
def __init__(self, module: str, mode: str, e: Exception):
|
|
self.module = module
|
|
self.mode = mode
|
|
self.e = e
|
|
|
|
def __str__(self) -> str:
|
|
return f'ModuleFailException:\n Module `{self.module}` failed ' \
|
|
f'MODE: \'{self.mode}\'\n\nException `{type(self.e)}`: {self.e}'
|
|
|
|
def module_io(module: Module):
|
|
return {
|
|
'name': module.name,
|
|
'takes': module.takes,
|
|
'produces': module.produces,
|
|
'meta': get_mod_metadata(module)
|
|
}
|
|
|
|
def module_map(module: Module, ctx: ModRunCtx):
|
|
try:
|
|
mod_ctx = ModuleContext(module, ctx.config, ctx.make_r_env(), ctx.share,
|
|
ctx.bin)
|
|
except Exception as e:
|
|
raise ModuleFailException(module.name, 'map', e)
|
|
|
|
return _realpath_deep(vars(mod_ctx.outputs))
|
|
|
|
def module_exec(module: Module, ctx: ModRunCtx):
|
|
try:
|
|
mod_ctx = ModuleContext(module, ctx.config, ctx.make_r_env(), ctx.share,
|
|
ctx.bin)
|
|
except Exception as e:
|
|
raise ModuleFailException(module.name, 'exec', e)
|
|
|
|
sfprint(1, 'Executing module '
|
|
f'`{Style.BRIGHT + module.name + Style.RESET_ALL}`:')
|
|
current_phase = 1
|
|
try:
|
|
for phase_msg in module.execute(mod_ctx):
|
|
sfprint(1, f' {Style.BRIGHT}[{current_phase}/{module.no_of_phases}]'
|
|
f'{Style.RESET_ALL}: {phase_msg}')
|
|
current_phase += 1
|
|
except Exception as e:
|
|
raise ModuleFailException(module.name, 'exec', e)
|
|
|
|
sfprint(1, f'Module `{Style.BRIGHT + module.name + Style.RESET_ALL}` '
|
|
'has finished its work!') |