2 from pathlib import Path
3 from .common import DirectoryTracker
4 from lib.utils import ConfigAST, Schema
6 class LBuildImporter(ast.NodeTransformer):
7 ConfigImportFn = Schema(
9 func=Schema(ast.Name, id="import_"),
12 ScopedAccess = Schema(
16 def __init__(self, env, file):
18 self.__parent = file.parent
21 with file.open('r') as f:
22 subtree = ast.parse(f.read())
25 DirectoryTracker.emit_enter(self.__parent),
26 *self.visit(subtree).body,
27 DirectoryTracker.emit_leave()
33 def __gen_import_subtree(self, relpath, variants):
36 p = relpath/ var / "LBuild"
37 block += LBuildImporter(self.__env, p).tree()
39 return ast.If(ast.Constant(True), block, [])
41 def visit_ImportFrom(self, node):
42 if ConfigAST.ConfigImport != node:
46 module = "" if not module else module
47 subpath = Path(*module.split('.'))
49 p = self.__parent / subpath
50 return self.__gen_import_subtree(p, [x.name for x in node.names])
52 def visit_Attribute(self, node):
53 if LBuildImporter.ScopedAccess != node:
54 return self.generic_visit(node)
59 if scope not in self.__env.scopes:
60 return self.generic_visit(node)
62 provider = self.__env.scopes[scope]
65 value=ast.Name(provider.context_name(), ctx=node.value.ctx),
66 slice=ast.Constant(subscope),
70 def visit_Expr(self, node):
72 if LBuildImporter.ConfigImportFn == val:
73 name = val.args[0].value
74 return self.__gen_import_subtree(self.__parent, [name])
76 return self.generic_visit(node)
78 class BuildEnvironment:
82 def load(self, rootfile):
83 self.__exec = self.__load(Path(rootfile))
85 def register_scope(self, scope):
86 if scope.name in self.scopes:
87 raise Exception(f"{scope.name} already exists")
88 self.scopes[scope.name] = scope
90 def __load(self, rootfile):
92 LBuildImporter(self, rootfile).tree(),
95 module = ast.fix_missing_locations(module)
96 return compile(module, rootfile, 'exec')
103 for scope in self.scopes.values():
105 g[scope.context_name()] = scope
107 DirectoryTracker.bind(g)