fix dependency check logic cause config always disabled
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg2 / config.py
1 import ast, os
2
3 from lib.utils import ConfigAST
4 from .common import NodeProperty, NodeDependency
5 from .nodes import GroupNode, TermNode
6
7
8 class ConfigEnvironment:
9     def __init__(self):
10         self.__node_table = {}
11         self.__exec = None
12         self.__globals = {}
13         self.__custom_fn = {
14             "env": lambda x: os.environ.get(x)
15         }
16     
17     def check_dependency(self, node) -> bool:
18         dep = NodeProperty.Dependency[node]
19         
20         if not isinstance(dep, NodeDependency):
21             return node.enabled()
22         
23         value_map = {}
24         for name in dep._names:
25             if name not in self.__node_table:
26                 raise node.config_error(f"config: '{name}' does not exists")
27             
28             n = self.__node_table[name]
29             value_map[name] = self.check_dependency(n)
30
31         return node.enabled() and dep.evaluate(value_map)
32     
33     def register_node(self, cfg_node):
34         name = cfg_node._name
35         if name in self.__node_table:
36             raise cfg_node.config_error(f"redefinition of '{name}'")
37         
38         self.__node_table[name] = cfg_node
39     
40     def get_node(self, name):
41         return self.__node_table.get(name)
42     
43     def relocate_children(self):
44         for node in self.__node_table.values():
45             parent = NodeProperty.Parent[node]
46             if not parent:
47                 continue
48
49             if not isinstance(parent, str):
50                 continue
51             
52             if parent not in self.__node_table:
53                 raise Exception(node, "unknow parent: %s"%(parent))
54             
55             parent_node = self.__node_table[parent]
56             if not isinstance(parent_node, GroupNode):
57                 raise Exception(node, "not a valid parent: %s"%(parent))
58             
59             parent_node.add_child(node)
60
61     def set_exec_context(self, astns):
62         filtered = []
63         for x in astns:
64             if isinstance(x, ConfigAST.EnterFileMarker):
65                 continue
66             if isinstance(x, ConfigAST.LeaveFileMarker):
67                 continue
68             filtered.append(x)
69
70         module = ast.Module(filtered, type_ignores=[])
71         module = ast.fix_missing_locations(module)
72
73         self.__exec = compile(module, "", mode='exec')
74
75     def __update_globals(self):
76         g = {}
77
78         exec(self.__exec, g)
79         self.__globals = {
80             **g,
81             **self.__custom_fn
82         }
83
84     def context(self):
85         return self.__globals
86         
87     def refresh(self):
88         if not self.__exec:
89             return
90
91         self.__update_globals()
92
93         for name, node in self.__node_table.items():
94             node.update()
95
96         for name, node in self.__node_table.items():
97             node.sanity_check()
98             NodeProperty.Enabled[node] = self.check_dependency(node)
99
100     def reset(self):
101         self.__node_table.clear()
102
103     def nodes(self):
104         for node in self.__node_table.values():
105             yield node
106
107     def top_levels(self):
108         for node in self.__node_table.values():
109             if NodeProperty.Parent[node] is None:
110                 yield node
111
112     def terms(self):
113         for node in self.__node_table.values():
114             if isinstance(node, TermNode):
115                 yield node
116
117     def loaded(self):
118         return self.__exec is not None