"""
Config ISA support
"""
- _arch = env("ARCH")
- return _arch if _arch else "x86_64"
+
+ match env("ARCH"):
+ case "i386":
+ return "i386"
+ case "aarch64":
+ return "aarch64"
+ case "rv64":
+ return "rv64"
+ case "x86_64":
+ return "x86_64"
+
+ print("unknown ARCH:", env("ARCH"), "default to x86_64")
+ return "x86_64"
@"Base operand size"
@readonly
-from . import acpi, ahci, char, gfxa, rtc, term, timer, bus
+from . import acpi, ahci, char, rtc, term, timer, bus
if config.use_devicetree:
from . import devtree
+if config.use_graphic_dev:
+ from . import gfxa
+
src.c += "irq.c"
\ No newline at end of file
"""
require (use_devicetree)
- return 256
\ No newline at end of file
+ return 256
+
+ @"Graphic Devices"
+ def use_graphic_dev() -> int:
+ """
+ Support of graphical devices and display protocol
+ """
+ return False
\ No newline at end of file
@"VGA 80x25 text-mode console"
def vga_console() -> bool:
- """ Enable VGA console device (text mode only) """
-
+ """
+ Enable VGA console device (text mode only)
+ """
+ require (use_graphic_dev)
+
return True
@"VGA character game device"
You normally don't need to include this, unless you want some user space fun ;)
"""
+ require (vga_console)
return False
\ No newline at end of file
Multiple `LConfig`s may be defined across different sub-directory in large scale
project for better maintainability
-LunaConfig allow you to import content of other LBuild using python's relative import
+LunaConfig allow you to import content of other LConfig using python's relative import
feature:
```py
```
This import mechanism works like `#include` directive in C preprocessor,
-the `from . import` construct will automatically intercepted by the LBuild interpreter and
+the `from . import` construct will automatically intercepted by the LConfig interpreter and
be replaced with the content from `./module/LConfig`
You can also address file in the deeper hierarchy of the directory tree, for example
Note that the dependency check only perform on the enablement of the node but not visibility.
-### Auto Toggling
+### Auto Toggling (Inverse Dependency)
> usage: `Terms` with `bool` value type
For prevention of this potential drawback, LunaConfig implemented a syntactical validator to identify these possible bad-practice and issue warning (or rise a fatal-error depending on the user setting).
-Currently, LunaConfig detect the misuses based on these rule rules:
+Currently, LunaConfig detect the misuses based on these rules:
+ `dynamic-logic`: The presence of conditional branching that could lead to complex logic. However, pattern matching is allowed.
+ `while-loop`, `for-loop`: The presence of any loop structure.
ast.visit(TreeSanitiser())
ast.visit(build)
+ for node in env.nodes():
+ node.apply_node_body()
+
env.set_exec_context(build.__noncfg_astns)
env.relocate_children()
\ No newline at end of file
Enabled = PropertyAccessor("$enabled")
Status = PropertyAccessor("$status")
Dependency = PropertyAccessor("$depends")
+ Linkage = PropertyAccessor("$linkage")
Hidden = PropertyAccessor("hidden")
Parent = PropertyAccessor("parent")
Label = PropertyAccessor("label")
return
dep = NodeDependency(node, expr)
- NodeProperty.Dependency[node] = dep
\ No newline at end of file
+ NodeProperty.Dependency[node] = dep
+
+
+class NodeInverseDependency:
+ def __init__(self):
+ self.__map = {}
+
+ def add_linkage(self, name, expr):
+ if name not in self.__map:
+ self.__map[name] = [expr]
+ else:
+ self.__map[name].append(expr)
+
+ def linkages(self):
+ return self.__map.items()
\ No newline at end of file
import ast
-from lib.utils import SourceLogger, Schema
+from lib.utils import SourceLogger, Schema
from .common import NodeProperty, ConfigNodeError, NodeDependency
from .lazy import LazyLookup
from .rewriter import ConfigNodeASTRewriter
NodeProperty.Readonly[self] = False
NodeProperty.Status[self] = "Empty"
- def set_node_body(self, ast_nodes, rewriter = ConfigNodeASTRewriter):
- new_ast = ast.Module(ast_nodes, [])
+ def set_node_body(self, ast_nodes):
+ self.__exec = ast.Module(ast_nodes, [])
+
+ def apply_node_body(self, rewriter = ConfigNodeASTRewriter):
+ assert isinstance(self.__exec, ast.Module)
+
+ new_ast = self.__exec
validator.validate(self, new_ast)
new_ast = rewriter(self).rewrite(new_ast)
from lib.utils import Schema
from .lazy import Lazy
-from .common import NodeProperty
+from .common import NodeProperty, NodeInverseDependency
class RewriteRule:
MaybeBuiltin = Schema(
self.__when_epxr = None
+ def __put_linkage(self, to_node, item):
+ node = self.__cfg_node._env.get_node(to_node)
+ link = NodeProperty.Linkage[node]
+
+ if not link:
+ link = NodeInverseDependency()
+ NodeProperty.Linkage[node] = link
+
+ link.add_linkage(self.__cfg_node._name, ast.unparse(item))
+
def __subscript_accessor(self, name, ctx, token):
return ast.Subscript(
value=ast.Name("__lzLut__", ctx=ast.Load()),
if RewriteRule.WhenToggler != node:
raise cfgn.config_error(
- f"invalid when(...) expression: {ast.unparse(node)}")
+ f"when(...): invalid expression: {ast.unparse(node)}")
if RewriteRule.WhenTogglerItem == node:
and_list.append(node)
for i in range(len(and_list)):
item = and_list[i]
operator = item.ops[0]
+
+ if RewriteRule.WhenTogglerItem != item:
+ raise cfgn.config_error(
+ f"when(...): non-trivial subclause : {ast.unparse(node)}")
name = Lazy.from_type(cfgn, Lazy.NodeValue, item.left.id)
acc = self.__subscript_accessor(name, ast.Load(), node)
+ self.__put_linkage(item.left.id, item)
+
if isinstance(operator, ast.Is):
operator = ast.Eq()
else:
return None
def visit_Return(self, node):
- if NodeProperty.WhenToggle[self.__cfg_node]:
+ if self.__when_epxr:
return None
return self.generic_visit(node)
TrivialReturn = Schema(Schema.Union(
TrivialValue,
- TrivialTest,
- InlineIf,
ast.JoinedStr
))
raise ShconfigException(f"no such config: {name}")
return node
- def __get_opt_line(self, node):
+ def __get_opt_line(self, node, color_hint = False):
aligned = 40
name = f"CONFIG_{node._name.upper()}"
value = NodeProperty.Value[node]
line = f"[{status}] {name}"
to_pad = max(aligned - len(line), 4)
- return f"{line} {'.' * to_pad} {val_txt}"
+ line = f"{line} {'.' * to_pad} {val_txt}"
+
+ if color_hint and not enabled:
+ line = f"\x1b[90;49m{line}\x1b[0m"
+ return line
@cmd("help", "h")
def __fn_help(self):
]
for node in self.__env.terms():
- lines.append(self.__get_opt_line(node))
+ lines.append(self.__get_opt_line(node, True))
pydoc.pager("\n".join(lines))
Show the dependency chain and boolean conditionals
"""
- def __print_dep_recursive(env, node, level = 0):
- indent = " "*(level * 4)
+ def __print_dep_recursive(env, node, inds = 0):
+ indent = " "*inds
dep: NodeDependency = NodeProperty.Dependency[node]
-
- print(f"{indent}+ {node._name} -> {NodeProperty.Value[node]}")
+
+ state = 'enabled' if NodeProperty.Value[node] else 'disabled'
+ print(f"{indent}* {node._name} (currently {state})")
if dep is None:
return
- print(f"{indent}= {dep._expr}")
+ print(f" {indent}predicate: {dep._expr}")
+ print(f" {indent}dependents:")
for name in dep._names:
n = env.get_node(name)
- __print_dep_recursive(env, n, level + 1)
+ __print_dep_recursive(env, n, inds + 6)
node = self.__get_node(name)
- print(node._name)
__print_dep_recursive(self.__env, node)
@cmd("opt", "val", "v")
print()
print(textwrap.indent(help.strip(), " |\t", lambda _:True))
print()
+
+
+ @cmd("effect", "link")
+ def __fn_effect(self, name: str):
+ """
+ Show the effects of this option on other options
+ """
+
+ node = self.__get_node(name)
+ link = NodeProperty.Linkage[node]
+
+ if not link:
+ return
+
+ for other, exprs in link.linkages():
+ print(f" {other}:")
+ for expr in exprs:
+ print(f" > when {expr}")
+ print()
\ No newline at end of file