f4pga/sfbuild/sf_module_runner/__init__.py
Krzysztof Boronski cd2ad7144c Up-to-date version of sfbuild
Signed-off-by: Krzysztof Boronski <kboronski@antmicro.com>
2022-04-26 12:16:36 +02:00

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!')