Refinement on documentation (#38)
[lunaix-os.git] / lunaix-os / scripts / build-tools / lbuild / contract.py
1 from lib.sandbox import Sandbox
2 from lib.utils import join_path
3 from .common import BuildEnvironment
4
5 import os
6
7 class LunaBuildFile(Sandbox):
8     def __init__(self, env: BuildEnvironment, path) -> None:
9         super().__init__({
10             "_script": 
11                 path,
12             "sources": 
13                 lambda src: self.export_sources(src),
14             "headers":
15                 lambda hdr: self.export_headers(hdr),
16             "configured":
17                 lambda name: self.check_config(name),
18             "config":
19                 lambda name: self.read_config(name),
20             "use":
21                 lambda file: self.import_buildfile(file),
22             **env.external_func_table()
23         })
24         
25         self.__srcs = []
26         self.__hdrs = []
27         self.__env  = env
28
29         self.__path = path
30         self.__dir  = os.path.dirname(path)
31         
32     def resolve(self):
33         self.execute(self.__path)
34         self.__env.add_sources(self.__srcs)
35         self.__env.add_headers(self.__hdrs)
36
37     def __do_process(self, list):
38         resolved = []
39         for entry in list:
40             if not entry:
41                 continue
42             resolved.append(self.__resolve_value(entry))
43         return resolved
44
45     def expand_select(self, val):
46         tests = list(val.keys())
47         if len(tests) != 1:
48             raise TypeError(
49                 "select statement must have exactly one conditional")
50         
51         test = tests[0]
52         outcomes = val[test]
53         if test not in outcomes:
54             self.__raise("unbounded select")
55         return outcomes[test]
56
57     def __resolve_value(self, source):
58         resolved = source
59         while isinstance(resolved, dict):
60             if isinstance(resolved, dict):
61                 resolved = self.expand_select(resolved)
62             else:
63                 self.__raise(f"entry with unknown type: {resolved}")
64         
65         if isinstance(resolved, list):
66             rs = []
67             for file in resolved:
68                 file = join_path(self.__dir, file)
69                 file = self.__env.to_wspath(file)
70                 rs.append(file)
71         else:
72             rs = join_path(self.__dir, resolved)
73             rs = [self.__env.to_wspath(rs)]
74
75         return rs
76     
77     def import_buildfile(self, path):
78         path = self.__resolve_value(path)
79         for p in path:
80             if (os.path.isdir(p)):
81                 p = os.path.join(p, "LBuild")
82             
83             if not os.path.exists(p):
84                 self.__raise("Build file not exist: %s", p)
85
86             if os.path.abspath(p) == os.path.abspath(self.__path):
87                 self.__raise("self dependency detected")
88
89             LunaBuildFile(self.__env, p).resolve()
90
91     def export_sources(self, src):
92         src = self.__resolve_value(src)
93         self.__srcs += src
94
95     def export_headers(self, hdr):
96         hdr = self.__resolve_value(hdr)
97         self.__hdrs += hdr
98
99     def check_config(self, name):
100         return self.__env.config_provider().has_config(name)
101     
102     def read_config(self, name):
103         return self.__env.config_provider().configured_value(name)
104     
105     def __raise(self, msg, *kargs):
106         raise Exception(msg%kargs)