Fix build error by adding -fno-stack-protector to CFLAGS in usr/LBuild (#63)
[lunaix-os.git] / lunaix-os / scripts / build-tools / shared / shconfig / common.py
1 import inspect
2 import textwrap
3
4 from typing         import Callable, Any
5 from lib.utils      import Schema
6
7 def select(val, _if, _else):
8     return _if if val else _else
9
10 def get_config_name(name):
11     return f"CONFIG_{name.upper()}"
12
13 def cmd(name, *alias):
14     def __cmd(fn: Callable):
15         fn.__annotations__["__CMD__"] = True
16         fn.__annotations__["__NAME__"] = name
17         fn.__annotations__["__ALIAS__"] = [*alias]
18         return fn
19     return __cmd
20
21 def imply_schema(fn: Callable):
22     type_map = inspect.get_annotations(fn)
23     arg_list = []
24
25     for arg in inspect.getargs(fn.__code__).args:
26         if arg == "self":
27             continue
28
29         t = type_map.get(arg)
30         t = t if t else Any
31
32         arg_list.append((arg, t))
33     
34     return Schema([x[1] for x in arg_list]), arg_list
35
36 class ShconfigException(Exception):
37     def __init__(self, *args):
38         super().__init__(*args)
39
40     def __str__(self):
41         return str(self.args[0])
42
43 class Executor:
44     def __init__(self, body: Callable):
45         self.name = body.__annotations__["__NAME__"]
46         self.alias = body.__annotations__["__ALIAS__"]
47         self.help = inspect.getdoc(body)
48         self.help = textwrap.dedent(self.help if self.help else "")
49         
50         schema, args = imply_schema(body)
51         self.argstr  = ' '.join(
52             [f'<{n.upper()}: {t.__name__}>' for n, t in args])
53         
54         self.__fn = body
55         self.__argtype = schema
56
57     def match_name(self, name):
58         return self.name == name or name in self.alias
59
60     def try_invoke(self, *args):
61         t_args = [self.__type_mapper(x) for x in args]
62         if self.__argtype != t_args:
63             raise ShconfigException(
64                 f"invalid parameter ({args}), expect: ({self.argstr})")
65     
66         self.__fn(*t_args)
67
68     def __type_mapper(self, strtype):
69         if strtype in ['True', 'False']:
70             return bool(strtype)
71         if strtype in ['y', 'n']:
72             return bool(strtype == 'y')
73         
74         try: return int(strtype)
75         except: pass
76
77         return strtype
78     
79     def __str__(self):
80         return '\n'.join([
81             *[f"{name} {self.argstr}" for name in self.alias + [self.name]],
82             textwrap.indent(self.help, '\t')
83         ])
84
85 class CmdTable:     
86     def __init__(self):
87         self._cmd_map = []
88
89         fns = inspect.getmembers(self, 
90                                  lambda p: isinstance(p, Callable))
91         for _, fn in fns:
92             if not hasattr(fn, "__annotations__"):
93                 continue
94             if "__CMD__" not in fn.__annotations__:
95                 continue
96
97             self._cmd_map.append(Executor(fn))
98         
99     def call(self, name, *args):
100         for exe in self._cmd_map:
101             if exe.match_name(name):
102                 exe.try_invoke(*args)
103                 return
104
105         raise ShconfigException(
106                 f"command not found {name}")