[parser] Move more methods to the ast scheme
[bitbake.git] / lib / bb / parse / ast.py
1 # ex:ts=4:sw=4:sts=4:et
2 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 """
4  AbstractSyntaxTree classes for the Bitbake language
5 """
6
7 # Copyright (C) 2003, 2004 Chris Larson
8 # Copyright (C) 2003, 2004 Phil Blundell
9 # Copyright (C) 2009 Holger Hans Peter Freyther
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License version 2 as
13 # published by the Free Software Foundation.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along
21 # with this program; if not, write to the Free Software Foundation, Inc.,
22 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24 import bb, re, string
25
26 __word__ = re.compile(r"\S+")
27 __parsed_methods__ = bb.methodpool.get_parsed_dict()
28
29 class StatementGroup:
30     def __init__(self):
31         self.statements = []
32
33     def append(self, statement):
34         self.statements.append(statement)
35
36     def eval(self, data):
37         """
38         Apply each statement on the data... in order
39         """
40         map(lambda x: x.eval(data), self.statements)
41
42     def __getitem__(self, item):
43         return self.statements.__getitem__(item)
44
45 class IncludeNode:
46     def __init__(self, what_file, fn, lineno, force):
47         self.what_file = what_file
48         self.from_fn = fn
49         self.from_lineno = lineno
50         self.force = force
51
52     def eval(self, data):
53         """
54         Include the file and evaluate the statements
55         """
56         s = bb.data.expand(self.what_file, data)
57         bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (self.from_fn, self.from_lineno, s))
58
59         # TODO: Cache those includes...
60         statements = StatementGroup()
61         if self.force:
62             bb.parse.ConfHandler.include(statements, self.from_fn, s, data, "include required")
63         else:
64             bb.parse.ConfHandler.include(statements, self.from_fn, s, data, False)
65         #statements.eval(data)
66
67 class ExportNode:
68     def __init__(self, var):
69         self.var = var
70
71     def eval(self, data):
72         bb.data.setVarFlag(self.var, "export", 1, data)
73
74 class DataNode:
75     """
76     Various data related updates. For the sake of sanity
77     we have one class doing all this. This means that all
78     this need to be re-evaluated... we might be able to do
79     that faster with multiple classes.
80     """
81     def __init__(self, groupd):
82         self.groupd = groupd
83
84     def getFunc(self, key, data):
85         if 'flag' in self.groupd and self.groupd['flag'] != None:
86             return bb.data.getVarFlag(key, self.groupd['flag'], data)
87         else:
88             return bb.data.getVar(key, data)
89
90     def eval(self, data):
91         groupd = self.groupd
92         key = groupd["var"]
93         if "exp" in groupd and groupd["exp"] != None:
94             bb.data.setVarFlag(key, "export", 1, data)
95         if "ques" in groupd and groupd["ques"] != None:
96             val = self.getFunc(key, data)
97             if val == None:
98                 val = groupd["value"]
99         elif "colon" in groupd and groupd["colon"] != None:
100             e = data.createCopy()
101             bb.data.update_data(e)
102             val = bb.data.expand(groupd["value"], e)
103         elif "append" in groupd and groupd["append"] != None:
104             val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"])
105         elif "prepend" in groupd and groupd["prepend"] != None:
106             val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
107         elif "postdot" in groupd and groupd["postdot"] != None:
108             val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
109         elif "predot" in groupd and groupd["predot"] != None:
110             val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
111         else:
112             val = groupd["value"]
113         if 'flag' in groupd and groupd['flag'] != None:
114             bb.msg.debug(3, bb.msg.domain.Parsing, "setVarFlag(%s, %s, %s, data)" % (key, groupd['flag'], val))
115             bb.data.setVarFlag(key, groupd['flag'], val, data)
116         else:
117             bb.data.setVar(key, val, data)
118
119 class MethodNode:
120     def __init__(self, func_name, body, lineno, fn):
121         self.func_name = func_name
122         self.body = body
123         self.fn = fn
124         self.lineno = lineno
125
126     def eval(self, data):
127         if self.func_name == "__anonymous":
128             funcname = ("__anon_%s_%s" % (self.lineno, self.fn.translate(string.maketrans('/.+-', '____'))))
129             if not funcname in bb.methodpool._parsed_fns:
130                 text = "def %s(d):\n" % (funcname) + '\n'.join(self.body)
131                 bb.methodpool.insert_method(funcname, text, self.fn)
132             anonfuncs = bb.data.getVar('__BBANONFUNCS', data) or []
133             anonfuncs.append(funcname)
134             bb.data.setVar('__BBANONFUNCS', anonfuncs, data)
135         else:
136             bb.data.setVarFlag(self.func_name, "func", 1, data)
137             bb.data.setVar(self.func_name, '\n'.join(self.body), data)
138
139 class PythonMethodNode:
140     def __init__(self, root, body, fn):
141         self.root = root
142         self.body = body
143         self.fn = fn
144
145     def eval(self, data):
146         # Note we will add root to parsedmethods after having parse
147         # 'this' file. This means we will not parse methods from
148         # bb classes twice
149         if not self.root  in __parsed_methods__:
150             text = '\n'.join(self.body)
151             bb.methodpool.insert_method(self.root, text, self.fn)
152
153 class MethodFlagsNode:
154     def __init__(self, key, m):
155         self.key = key
156         self.m = m
157
158     def eval(self, data):
159         if bb.data.getVar(self.key, data):
160             # clean up old version of this piece of metadata, as its
161             # flags could cause problems
162             bb.data.setVarFlag(self.key, 'python', None, data)
163             bb.data.setVarFlag(self.key, 'fakeroot', None, data)
164         if self.m.group("py") is not None:
165             bb.data.setVarFlag(self.key, "python", "1", data)
166         else:
167             bb.data.delVarFlag(self.key, "python", data)
168         if self.m.group("fr") is not None:
169             bb.data.setVarFlag(self.key, "fakeroot", "1", data)
170         else:
171             bb.data.delVarFlag(self.key, "fakeroot", data)
172
173 class ExportFuncsNode:
174     def __init__(self, fns, classes):
175         self.n = __word__.findall(fns)
176         self.classes = classes
177
178     def eval(self, data):
179         for f in self.n:
180             allvars = []
181             allvars.append(f)
182             allvars.append(self.classes[-1] + "_" + f)
183
184             vars = [[ allvars[0], allvars[1] ]]
185             if len(self.classes) > 1 and self.classes[-2] is not None:
186                 allvars.append(self.classes[-2] + "_" + f)
187                 vars = []
188                 vars.append([allvars[2], allvars[1]])
189                 vars.append([allvars[0], allvars[2]])
190
191             for (var, calledvar) in vars:
192                 if bb.data.getVar(var, data) and not bb.data.getVarFlag(var, 'export_func', data):
193                     continue
194
195                 if bb.data.getVar(var, data):
196                     bb.data.setVarFlag(var, 'python', None, data)
197                     bb.data.setVarFlag(var, 'func', None, data)
198
199                 for flag in [ "func", "python" ]:
200                     if bb.data.getVarFlag(calledvar, flag, data):
201                         bb.data.setVarFlag(var, flag, bb.data.getVarFlag(calledvar, flag, data), data)
202                 for flag in [ "dirs" ]:
203                     if bb.data.getVarFlag(var, flag, data):
204                         bb.data.setVarFlag(calledvar, flag, bb.data.getVarFlag(var, flag, data), data)
205
206                 if bb.data.getVarFlag(calledvar, "python", data):
207                     bb.data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", data)
208                 else:
209                     bb.data.setVar(var, "\t" + calledvar + "\n", data)
210                 bb.data.setVarFlag(var, 'export_func', '1', data)
211
212 class AddTaskNode:
213     def __init__(self, func, before, after):
214         self.func = func
215         self.before = before
216         self.after = after
217
218     def eval(self, data):
219         var = self.func
220         if self.func[:3] != "do_":
221             var = "do_" + self.func
222
223         bb.data.setVarFlag(var, "task", 1, data)
224         bbtasks = bb.data.getVar('__BBTASKS', data) or []
225         if not var in bbtasks:
226             bbtasks.append(var)
227         bb.data.setVar('__BBTASKS', bbtasks, data)
228
229         existing = bb.data.getVarFlag(var, "deps", data) or []
230         if self.after is not None:
231             # set up deps for function
232             for entry in self.after.split():
233                 if entry not in existing:
234                     existing.append(entry)
235         bb.data.setVarFlag(var, "deps", existing, data)
236         if self.before is not None:
237             # set up things that depend on this func
238             for entry in self.before.split():
239                 existing = bb.data.getVarFlag(entry, "deps", data) or []
240                 if var not in existing:
241                     bb.data.setVarFlag(entry, "deps", [var] + existing, data)
242
243 class BBHandlerNode:
244     def __init__(self, fns):
245         self.hs = __word__.findall(fns)
246
247     def eval(self, data):
248         bbhands = bb.data.getVar('__BBHANDLERS', data) or []
249         for h in self.hs:
250             bbhands.append(h)
251             bb.data.setVarFlag(h, "handler", 1, data)
252         bb.data.setVar('__BBHANDLERS', bbhands, data)
253  
254 def handleInclude(statements, m, fn, lineno, data, force):
255     # AST handling
256     statements.append(IncludeNode(m.group(1), fn, lineno, force))
257     statements[-1].eval(data)
258
259 def handleExport(statements, m, data):
260     # AST handling
261     statements.append(ExportNode(m.group(1)))
262     statements[-1].eval(data)
263
264 def handleData(statements, groupd, data):
265     # AST handling
266     statements.append(DataNode(groupd))
267     statements[-1].eval(data)
268
269 def handleMethod(statements, func_name, lineno, fn, body, d):
270     # AST handling
271     statements.append(MethodNode(func_name, body, lineno, fn))
272     statements[-1].eval(d)
273
274 def handlePythonMethod(statements, root, body, fn):
275     # AST handling
276     statements.append(PythonMethodNode(root, body, fn))
277     statements[-1].eval(None)
278
279 def handleMethodFlags(statements, key, m, d):
280     statements.append(MethodFlagsNode(key, m))
281     statements[-1].eval(d)
282
283 def handleExportFuncs(statements, m, classes, d):
284     statements.append(ExportFuncsNode(m.group(1), classes))
285     statements[-1].eval(d)
286
287 def handleAddTask(statements, m, d):
288     func = m.group("func")
289     before = m.group("before")
290     after = m.group("after")
291     if func is None:
292         return
293
294     statements.append(AddTaskNode(func, before, after))
295     statements[-1].eval(d)
296
297 def handleBBHandlers(statements, m, d):
298     statements.append(BBHandlerNode(m.group(1)))
299     statements[-1].eval(d)
300
301 def handleInherit(statements, m, d):
302     files = m.group(1)
303     n = __word__.findall(files)
304     bb.parse.BBHandler.inherit(statements, n, d)
305
306 def finalise(fn, d):
307     bb.data.expandKeys(d)
308     bb.data.update_data(d)
309     anonqueue = bb.data.getVar("__anonqueue", d, 1) or []
310     body = [x['content'] for x in anonqueue]
311     flag = { 'python' : 1, 'func' : 1 }
312     bb.data.setVar("__anonfunc", "\n".join(body), d)
313     bb.data.setVarFlags("__anonfunc", flag, d)
314     from bb import build
315     try:
316         t = bb.data.getVar('T', d)
317         bb.data.setVar('T', '${TMPDIR}/anonfunc/', d)
318         anonfuncs = bb.data.getVar('__BBANONFUNCS', d) or []
319         code = ""
320         for f in anonfuncs:
321             code = code + "    %s(d)\n" % f
322         bb.data.setVar("__anonfunc", code, d)        
323         build.exec_func("__anonfunc", d)
324         bb.data.delVar('T', d)
325         if t:
326             bb.data.setVar('T', t, d)
327     except Exception, e:
328         bb.msg.debug(1, bb.msg.domain.Parsing, "Exception when executing anonymous function: %s" % e)
329         raise
330     bb.data.delVar("__anonqueue", d)
331     bb.data.delVar("__anonfunc", d)
332     bb.data.update_data(d)
333
334     all_handlers = {} 
335     for var in bb.data.getVar('__BBHANDLERS', d) or []:
336         # try to add the handler
337         handler = bb.data.getVar(var,d)
338         bb.event.register(var, handler)
339
340     tasklist = bb.data.getVar('__BBTASKS', d) or []
341     bb.build.add_tasks(tasklist, d)
342
343     bb.event.fire(bb.event.RecipeParsed(fn), d)
344