Better repoting of disabled component to the user.

This commit is contained in:
Nicolas Van Bossuyt 2023-02-10 11:37:37 +01:00
parent dcde816298
commit 250930c977
5 changed files with 74 additions and 41 deletions

View file

@ -40,6 +40,9 @@ def gen(out: TextIO, context: Context):
writer.separator("Components")
for instance in context.instances:
if not instance.enabled:
continue
objects = instance.objsfiles(context)
writer.comment(f"Component: {instance.manifest.id}")
writer.comment(f"Resolved: {', '.join(instance.resolved)}")
@ -76,9 +79,8 @@ def gen(out: TextIO, context: Context):
writer.newline()
def build(componentSpec: str, targetSpec: str, props: Props = {}) -> str:
def build(componentSpec: str, targetSpec: str, props: Props = {}) -> str:
context = contextFor(targetSpec, props)
target = context.target
shell.mkdir(context.builddir())
ninjaPath = f"{context.builddir()}/build.ninja"
@ -91,6 +93,10 @@ def build(componentSpec: str, targetSpec: str, props: Props = {}) -> str:
if instance is None:
raise Exception(f"Component {componentSpec} not found")
if not instance.enabled:
raise Exception(
f"Component {componentSpec} is disabled: {instance.disableReason}")
shell.exec(f"ninja", "-v", "-f", ninjaPath, instance.outfile(context))
return instance.outfile(context)

View file

@ -155,10 +155,12 @@ def graphCmd(args: Args):
scope: str | None = cast(str | None, args.tryConsumeOpt("scope"))
onlyLibs: bool = args.consumeOpt("only-libs", False) == True
showDisabled: bool = args.consumeOpt("show-disabled", False) == True
context = contextFor(targetSpec)
graph.view(context, scope=scope, showExe=not onlyLibs)
graph.view(context, scope=scope, showExe=not onlyLibs,
showDisabled=showDisabled)
cmds += [Cmd("g", "graph", "Show dependency graph", graphCmd)]

View file

@ -15,15 +15,21 @@ class IContext(Protocol):
class ComponentInstance:
enabled: bool = True
disableReason = ""
manifest: ComponentManifest
sources: list[str] = []
resolved: list[str] = []
def __init__(
self,
enabled: bool,
disableReason: str,
manifest: ComponentManifest,
sources: list[str],
resolved: list[str]):
self.enabled = enabled
self.disableReason = disableReason
self.manifest = manifest
self.sources = sources
self.resolved = resolved
@ -112,11 +118,12 @@ def loadAllComponents() -> list[ComponentManifest]:
files))
def filterDisabled(components: list[ComponentManifest], target: TargetManifest) -> list[ComponentManifest]:
return list(filter(lambda c: c.isEnabled(target), components))
def filterDisabled(components: list[ComponentManifest], target: TargetManifest) -> tuple[list[ComponentManifest], list[ComponentManifest]]:
return list(filter(lambda c: c.isEnabled(target)[0], components)), \
list(filter(lambda c: not c.isEnabled(target)[0], components))
def providerFor(what: str, components: list[ComponentManifest]) -> str | None:
def providerFor(what: str, components: list[ComponentManifest]) -> tuple[str | None, str]:
result: list[ComponentManifest] = list(
filter(lambda c: c.id == what, components))
@ -125,26 +132,27 @@ def providerFor(what: str, components: list[ComponentManifest]) -> str | None:
result = list(filter(lambda x: (what in x.provides), components))
if len(result) == 0:
logger.error(f"No provider for {what}")
return None
logger.error(f"No provider for '{what}'")
return (None, f"No provider for '{what}'")
if len(result) > 1:
logger.error(f"Multiple providers for {what}: {result}")
return None
ids = list(map(lambda x: x.id, result))
logger.error(f"Multiple providers for '{what}': {result}")
return (None, f"Multiple providers for '{what}': {','.join(ids)}")
return result[0].id
return (result[0].id, "")
def resolveDeps(componentSpec: str, components: list[ComponentManifest], target: TargetManifest) -> tuple[bool, list[str]]:
def resolveDeps(componentSpec: str, components: list[ComponentManifest], target: TargetManifest) -> tuple[bool, str, list[str]]:
mapping = dict(map(lambda c: (c.id, c), components))
def resolveInner(what: str, stack: list[str] = []) -> tuple[bool, list[str]]:
def resolveInner(what: str, stack: list[str] = []) -> tuple[bool, str, list[str]]:
result: list[str] = []
what = target.route(what)
resolved = providerFor(what, components)
resolved, unresolvedReason = providerFor(what, components)
if resolved is None:
return False, []
return False, unresolvedReason, []
if resolved in stack:
raise Exception(f"Dependency loop: {stack} -> {resolved}")
@ -152,35 +160,37 @@ def resolveDeps(componentSpec: str, components: list[ComponentManifest], target:
stack.append(resolved)
for req in mapping[resolved].requires:
keep, reqs = resolveInner(req, stack)
keep, unresolvedReason, reqs = resolveInner(req, stack)
if not keep:
stack.pop()
logger.error(f"Dependency {req} not met for {resolved}")
return False, []
logger.error(f"Dependency '{req}' not met for '{resolved}'")
return False, unresolvedReason, []
result.extend(reqs)
stack.pop()
result.insert(0, resolved)
return True, result
return True, "", result
enabled, resolved = resolveInner(componentSpec)
enabled, unresolvedReason, resolved = resolveInner(componentSpec)
return enabled, resolved
return enabled, unresolvedReason, resolved
def instanciate(componentSpec: str, components: list[ComponentManifest], target: TargetManifest) -> ComponentInstance | None:
manifest = next(filter(lambda c: c.id == componentSpec, components))
sources = shell.find(
manifest.dirname(), ["*.c", "*.cpp", "*.s", "*.asm"], recusive=False)
enabled, resolved = resolveDeps(componentSpec, components, target)
enabled, unresolvedReason, resolved = resolveDeps(
componentSpec, components, target)
if not enabled:
return None
return ComponentInstance(enabled, unresolvedReason, manifest, sources, resolved[1:])
return ComponentInstance(manifest, sources, resolved[1:])
def instanciateDisabled(component: ComponentManifest, target: TargetManifest) -> ComponentInstance:
return ComponentInstance(False, component.isEnabled(target)[1], component, [], [])
context: dict = {}
@ -190,7 +200,7 @@ def contextFor(targetSpec: str, props: Props = {}) -> Context:
if targetSpec in context:
return context[targetSpec]
logger.log(f"Loading context for {targetSpec}")
logger.log(f"Loading context for '{targetSpec}'")
targetEls = targetSpec.split(":")
@ -201,7 +211,7 @@ def contextFor(targetSpec: str, props: Props = {}) -> Context:
target.props |= props
components = loadAllComponents()
components = filterDisabled(components, target)
components, disabled = filterDisabled(components, target)
tools: Tools = {}
@ -225,7 +235,10 @@ def contextFor(targetSpec: str, props: Props = {}) -> Context:
tool = component.tools[toolSpec]
tools[toolSpec].args += tool.args
instances = cast(list[ComponentInstance], list(filter(lambda e: e != None, map(lambda c: instanciate(
instances: list[ComponentInstance] = list(
map(lambda c: instanciateDisabled(c, target), disabled))
instances += cast(list[ComponentInstance], list(filter(lambda e: e != None, map(lambda c: instanciate(
c.id, components, target), components))))
context[targetSpec] = Context(

View file

@ -2,7 +2,7 @@ from osdk.context import Context
from osdk import vt100
def view(context: Context, scope: str | None = None, showExe: bool = True):
def view(context: Context, scope: str | None = None, showExe: bool = True, showDisabled: bool = False):
from graphviz import Digraph
g = Digraph(context.target.id, filename='graph.gv')
@ -26,16 +26,27 @@ def view(context: Context, scope: str | None = None, showExe: bool = True):
instance.manifest.id not in scopeInstance.resolved:
continue
fillcolor = "lightgrey" if instance.isLib() else "lightblue"
shape = "plaintext" if not scope == instance.manifest.id else 'box'
if instance.enabled:
fillcolor = "lightgrey" if instance.isLib() else "lightblue"
shape = "plaintext" if not scope == instance.manifest.id else 'box'
g.node(instance.manifest.id, f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}>",
shape=shape, style="filled", fillcolor=fillcolor)
g.node(instance.manifest.id, f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}>",
shape=shape, style="filled", fillcolor=fillcolor)
for req in instance.manifest.requires:
g.edge(instance.manifest.id, req)
for req in instance.manifest.requires:
g.edge(instance.manifest.id, req)
for req in instance.manifest.provides:
g.edge(req, instance.manifest.id, arrowhead="none")
for req in instance.manifest.provides:
g.edge(req, instance.manifest.id, arrowhead="none")
elif showDisabled:
g.node(instance.manifest.id, f"<<B>{instance.manifest.id}</B><BR/>{vt100.wordwrap(instance.manifest.decription, 40,newline='<BR/>')}<BR/><BR/><I>{vt100.wordwrap(instance.disableReason, 40,newline='<BR/>')}</I>>",
shape="plaintext", style="filled", fontcolor="#999999", fillcolor="#eeeeee")
for req in instance.manifest.requires:
g.edge(instance.manifest.id, req, color="#aaaaaa")
for req in instance.manifest.provides:
g.edge(req, instance.manifest.id,
arrowhead="none", color="#aaaaaa")
g.view(filename=f"{context.builddir()}/graph.gv")

View file

@ -154,16 +154,17 @@ class ComponentManifest(Manifest):
def __repr__(self):
return f"ComponentManifest({self.id})"
def isEnabled(self, target: TargetManifest):
def isEnabled(self, target: TargetManifest) -> tuple[bool, str]:
for k, v in self.enableIf.items():
if not k in target.props:
logger.log(
f"Component {self.id} disabled by missing {k} in target")
return False
return False, f"Missing props '{k}' in target"
if not target.props[k] in v:
vStrs = [f"'{str(x)}'" for x in v]
logger.log(
f"Component {self.id} disabled by {k}={target.props[k]} not in {v}")
return False
return False, f"Props missmatch for '{k}': Got '{target.props[k]}' but expected {', '.join(vStrs)}"
return True
return True, ""