Fix build error by adding -fno-stack-protector to CFLAGS in usr/LBuild (#63)
[lunaix-os.git] / lunaix-os / scripts / build-tools / shared / shconfig / commands.py
1 import textwrap
2 import pydoc
3 import re
4
5 from .common import CmdTable, ShconfigException
6 from .common import select, cmd, get_config_name
7
8 from lcfg2.config   import ConfigEnvironment
9 from lcfg2.common   import NodeProperty, NodeDependency, ConfigNodeError
10
11 class Commands(CmdTable):
12     def __init__(self, env: ConfigEnvironment):
13         super().__init__()
14
15         self.__env = env
16
17     def __get_node(self, name: str):
18         node_name = name.removeprefix("CONFIG_").lower()
19         node = self.__env.get_node(node_name)
20         if node is None:
21             raise ShconfigException(f"no such config: {name}")
22         return node
23     
24     def __get_opt_line(self, node, color_hint = False):
25         aligned = 40
26         name    = f"CONFIG_{node._name.upper()}"
27         value   = NodeProperty.Value[node]
28         enabled = NodeProperty.Enabled[node]
29         hidden  = NodeProperty.Hidden[node]
30         ro      = NodeProperty.Readonly[node]
31
32         status  = f"{select(not enabled, 'x', '.')}"          \
33                 f"{select(ro, 'r', '.')}"          \
34                 f"{select(hidden, 'h', '.')}"      \
35                                     
36         val_txt = f"{value if value is not None else '<?>'}"
37
38         if value is True:
39             val_txt = "y"
40         elif value is False:
41             val_txt = "n"
42         elif isinstance(value, str):
43             val_txt = f'"{val_txt}"'
44                 
45         line = f"[{status}] {name}"
46         to_pad = max(aligned - len(line), 4)
47         line = f"{line} {'.' * to_pad} {val_txt}"
48
49         if color_hint and not enabled:
50             line = f"\x1b[90;49m{line}\x1b[0m"
51         return line
52     
53     def __format_config_list(self, nodes):
54         lines = []
55         disabled = []
56         
57         for node in nodes:
58             _l = disabled if not NodeProperty.Enabled[node] else lines
59             _l.append(self.__get_opt_line(node, True))
60
61         if disabled:
62             lines += [
63                 "",
64                 "\t---- disabled ----",
65                 "",
66                 *disabled
67             ]
68
69         return lines
70     
71     @cmd("help", "h")
72     def __fn_help(self):
73         """
74         Print this message
75         """
76
77         print()
78         for exe in self._cmd_map:
79             print(exe, "\n")
80
81     @cmd("show", "ls")
82     def __fn_show(self):
83         """
84         Show all configurable options
85         """
86         
87         lines = [
88             "Display format:",
89             "",
90             "        (flags) CONFIG_NAME ..... VALUE",
91             " ",
92             "   (flags)",
93             "      x    Config is disabled",
94             "      r    Read-Only config",
95             "      h    Hidden config",
96             "",
97             "   VALUE (bool)",
98             "      y    True",
99             "      n    False",
100             "",
101             "",
102             "Defined configuration terms",
103             ""
104         ]
105
106         lines += self.__format_config_list(self.__env.terms())
107
108         pydoc.pager("\n".join(lines))
109
110     @cmd("set")
111     def __fn_set(self, name: str, value):
112         """
113         Update a configurable option's value
114         """
115         
116         node = self.__get_node(name)
117         if node is None:
118             raise ShconfigException(f"no such config: {name}")
119         
120         if NodeProperty.Readonly[node]:
121             raise ShconfigException(f"node is read only")
122         
123         try:
124             NodeProperty.Value[node] = value
125             self.__env.refresh()
126         except ConfigNodeError as e:
127             print(e)
128
129     @cmd("dep")
130     def __fn_dep(self, name: str):
131         """
132         Show the dependency chain and boolean conditionals
133         """
134
135         def __print_dep_recursive(env, node, inds = 0):
136             indent = " "*inds
137             dep: NodeDependency = NodeProperty.Dependency[node]
138             
139             state = 'enabled' if NodeProperty.Value[node] else 'disabled'
140             print(f"{indent}* {node._name} (currently {state})")
141             if dep is None:
142                 return
143             
144             print(f"  {indent}predicate: {dep._expr}")
145             print(f"  {indent}dependents:")
146             for name in dep._names:
147                 n = env.get_node(name)
148                 __print_dep_recursive(env, n, inds + 6)
149
150         node = self.__get_node(name)
151         __print_dep_recursive(self.__env, node)
152
153     @cmd("opt", "val", "v")
154     def __fn_opt(self, name: str):
155         """
156         Show the current value and flags of selected options
157         """
158
159         node = self.__get_node(name)        
160         print(self.__get_opt_line(node))
161
162     @cmd("what", "help", "?")
163     def __fn_what(self, name: str):
164         """
165         Show the documentation associated with the option
166         """
167
168         node = self.__get_node(name)
169         help = NodeProperty.HelpText[node]
170         help = "<no help message>" if not help else help
171
172         print()
173         print(textwrap.indent(help.strip(), "  |\t", lambda _:True))
174         print()
175
176
177     @cmd("affects")
178     def __fn_affect(self, name: str):
179         """
180         Show the effects of this option on other options
181         """
182         
183         node = self.__get_node(name)
184         link = NodeProperty.Linkage[node]
185
186         if not link:
187             return
188         
189         for other, exprs in link.linkages():
190             print(f" {other}:")
191             for expr in exprs:
192                 print(f"   > when {expr}")
193             print()
194     
195     @cmd("find")
196     def __fn_search(self, fuzz: str):
197         """
198         Perform fuzzy search on configs (accept regex)
199         """
200
201         nodes = []
202         expr = re.compile(fuzz)
203         for node in self.__env.terms():
204             name = get_config_name(node._name)
205             if not expr.findall(name):
206                 continue
207             nodes.append(node)
208
209         if not nodes:
210             print("no matches")
211             return
212
213         lines = self.__format_config_list(nodes)
214
215         pydoc.pager("\n".join(lines))