[parser] Firs set of AST nodes with construction and eval
[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 class IncludeNode:
43     def __init__(self, what_file, fn, lineno):
44         self.what_file = what_file
45         self.from_fn = fn
46         self.from_lineno = lineno
47
48     def eval(self, data):
49         """
50         Include the file and evaluate the statements
51         """
52         s = bb.data.expand(self.what_file, data)
53         bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (self.from_fn, self.from_lineno, s))
54
55         # TODO: Cache those includes...
56         statements = StatementGroup()
57         bb.parse.ConfHandler.include(statements, self.from_fn, s, data, False)
58         statements.eval(data)
59
60 class ExportNode:
61     def __init__(self, var):
62         self.var = var
63
64     def eval(self, data):
65         bb.data.setVarFlag(self.var, "export", 1, data)
66
67 class DataNode:
68     """
69     Various data related updates. For the sake of sanity
70     we have one class doing all this. This means that all
71     this need to be re-evaluated... we might be able to do
72     that faster with multiple classes.
73     """
74     def __init__(self, groupd):
75         self.groupd = groupd
76
77     def eval(self, data):
78         groupd = self.groupd
79         key = groupd["var"]
80         if "exp" in groupd and groupd["exp"] != None:
81             bb.data.setVarFlag(key, "export", 1, data)
82         if "ques" in groupd and groupd["ques"] != None:
83             val = getFunc(groupd, key, data)
84             if val == None:
85                 val = groupd["value"]
86         elif "colon" in groupd and groupd["colon"] != None:
87             e = data.createCopy()
88             bb.data.update_data(e)
89             val = bb.data.expand(groupd["value"], e)
90         elif "append" in groupd and groupd["append"] != None:
91             val = "%s %s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
92         elif "prepend" in groupd and groupd["prepend"] != None:
93             val = "%s %s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
94         elif "postdot" in groupd and groupd["postdot"] != None:
95             val = "%s%s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
96         elif "predot" in groupd and groupd["predot"] != None:
97             val = "%s%s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
98         else:
99             val = groupd["value"]
100         if 'flag' in groupd and groupd['flag'] != None:
101             bb.msg.debug(3, bb.msg.domain.Parsing, "setVarFlag(%s, %s, %s, data)" % (key, groupd['flag'], val))
102             bb.data.setVarFlag(key, groupd['flag'], val, data)
103         else:
104             bb.data.setVar(key, val, data)
105
106         
107
108 def handleInclude(statements, m, fn, lineno, data, force):
109     # AST handling
110     statements.append(IncludeNode(m.group(1), fn, lineno))
111
112     s = bb.data.expand(m.group(1), data)
113     bb.msg.debug(3, bb.msg.domain.Parsing, "CONF %s:%d: including %s" % (fn, lineno, s))
114     if force:
115         bb.parse.ConfHandler.include(statements, fn, s, data, "include required")
116     else:
117         bb.parse.ConfHandler.include(statements, fn, s, data, False)
118
119 def handleExport(statements, m, data):
120     # AST handling
121     statements.append(ExportNode(m.group(1)))
122
123     bb.data.setVarFlag(m.group(1), "export", 1, data)
124
125 def handleData(statements, groupd, data):
126     # AST handling
127     statements.append(DataNode(groupd))
128
129     key = groupd["var"]
130     if "exp" in groupd and groupd["exp"] != None:
131         bb.data.setVarFlag(key, "export", 1, data)
132     if "ques" in groupd and groupd["ques"] != None:
133         val = getFunc(groupd, key, data)
134         if val == None:
135             val = groupd["value"]
136     elif "colon" in groupd and groupd["colon"] != None:
137         e = data.createCopy()
138         bb.data.update_data(e)
139         val = bb.data.expand(groupd["value"], e)
140     elif "append" in groupd and groupd["append"] != None:
141         val = "%s %s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
142     elif "prepend" in groupd and groupd["prepend"] != None:
143         val = "%s %s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
144     elif "postdot" in groupd and groupd["postdot"] != None:
145         val = "%s%s" % ((getFunc(groupd, key, data) or ""), groupd["value"])
146     elif "predot" in groupd and groupd["predot"] != None:
147         val = "%s%s" % (groupd["value"], (getFunc(groupd, key, data) or ""))
148     else:
149         val = groupd["value"]
150     if 'flag' in groupd and groupd['flag'] != None:
151         bb.msg.debug(3, bb.msg.domain.Parsing, "setVarFlag(%s, %s, %s, data)" % (key, groupd['flag'], val))
152         bb.data.setVarFlag(key, groupd['flag'], val, data)
153     else:
154         bb.data.setVar(key, val, data)
155
156 def getFunc(groupd, key, data):
157     if 'flag' in groupd and groupd['flag'] != None:
158         return bb.data.getVarFlag(key, groupd['flag'], data)
159     else:
160         return bb.data.getVar(key, data)
161
162 def handleMethod(statements, func_name, lineno, fn, body, d):
163     if func_name == "__anonymous":
164         funcname = ("__anon_%s_%s" % (lineno, fn.translate(string.maketrans('/.+-', '____'))))
165         if not funcname in bb.methodpool._parsed_fns:
166             text = "def %s(d):\n" % (funcname) + '\n'.join(body)
167             bb.methodpool.insert_method(funcname, text, fn)
168         anonfuncs = bb.data.getVar('__BBANONFUNCS', d) or []
169         anonfuncs.append(funcname)
170         bb.data.setVar('__BBANONFUNCS', anonfuncs, d)
171     else:
172         bb.data.setVarFlag(func_name, "func", 1, d)
173         bb.data.setVar(func_name, '\n'.join(body), d)
174
175 def handlePythonMethod(statements, root, body, fn):
176     # Note we will add root to parsedmethods after having parse
177     # 'this' file. This means we will not parse methods from
178     # bb classes twice
179     if not root in __parsed_methods__:
180         text = '\n'.join(body)
181         bb.methodpool.insert_method(root, text, fn)
182
183 def handleMethodFlags(statements, key, m, d):
184     if bb.data.getVar(key, d):
185         # Clean up old version of this piece of metadata, as its
186         # flags could cause problems
187         bb.data.setVarFlag(key, 'python', None, d)
188         bb.data.setVarFlag(key, 'fakeroot', None, d)
189     if m.group("py") is not None:
190         bb.data.setVarFlag(key, "python", "1", d)
191     else:
192         bb.data.delVarFlag(key, "python", d)
193     if m.group("fr") is not None:
194         bb.data.setVarFlag(key, "fakeroot", "1", d)
195     else:
196         bb.data.delVarFlag(key, "fakeroot", d)
197
198 def handleExportFuncs(statements, m, classes, d):
199     fns = m.group(1)
200     n = __word__.findall(fns)
201     for f in n:
202         allvars = []
203         allvars.append(f)
204         allvars.append(classes[-1] + "_" + f)
205
206         vars = [[ allvars[0], allvars[1] ]]
207         if len(classes) > 1 and classes[-2] is not None:
208             allvars.append(classes[-2] + "_" + f)
209             vars = []
210             vars.append([allvars[2], allvars[1]])
211             vars.append([allvars[0], allvars[2]])
212
213         for (var, calledvar) in vars:
214             if bb.data.getVar(var, d) and not bb.data.getVarFlag(var, 'export_func', d):
215                 continue
216
217             if bb.data.getVar(var, d):
218                 bb.data.setVarFlag(var, 'python', None, d)
219                 bb.data.setVarFlag(var, 'func', None, d)
220
221             for flag in [ "func", "python" ]:
222                 if bb.data.getVarFlag(calledvar, flag, d):
223                     bb.data.setVarFlag(var, flag, bb.data.getVarFlag(calledvar, flag, d), d)
224             for flag in [ "dirs" ]:
225                 if bb.data.getVarFlag(var, flag, d):
226                     bb.data.setVarFlag(calledvar, flag, bb.data.getVarFlag(var, flag, d), d)
227
228             if bb.data.getVarFlag(calledvar, "python", d):
229                 bb.data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
230             else:
231                 bb.data.setVar(var, "\t" + calledvar + "\n", d)
232             bb.data.setVarFlag(var, 'export_func', '1', d)
233
234 def handleAddTask(statements, m, d):
235     func = m.group("func")
236     before = m.group("before")
237     after = m.group("after")
238     if func is None:
239         return
240     if func[:3] != "do_":
241         var = "do_" + func
242
243     bb.data.setVarFlag(var, "task", 1, d)
244
245     bbtasks = bb.data.getVar('__BBTASKS', d) or []
246     if not var in bbtasks:
247         bbtasks.append(var)
248     bb.data.setVar('__BBTASKS', bbtasks, d)
249
250     existing = bb.data.getVarFlag(var, "deps", d) or []
251     if after is not None:
252         # set up deps for function
253         for entry in after.split():
254             if entry not in existing:
255                 existing.append(entry)
256     bb.data.setVarFlag(var, "deps", existing, d)
257     if before is not None:
258         # set up things that depend on this func
259         for entry in before.split():
260             existing = bb.data.getVarFlag(entry, "deps", d) or []
261             if var not in existing:
262                 bb.data.setVarFlag(entry, "deps", [var] + existing, d)
263
264 def handleBBHandlers(statements, m, d):
265     fns = m.group(1)
266     hs = __word__.findall(fns)
267     bbhands = bb.data.getVar('__BBHANDLERS', d) or []
268     for h in hs:
269         bbhands.append(h)
270         bb.data.setVarFlag(h, "handler", 1, d)
271     bb.data.setVar('__BBHANDLERS', bbhands, d)
272
273 def handleInherit(statements, m, d):
274     files = m.group(1)
275     n = __word__.findall(files)
276     bb.parse.BBHandler.inherit(statements, n, d)
277
278 def finalise(fn, d):
279     bb.data.expandKeys(d)
280     bb.data.update_data(d)
281     anonqueue = bb.data.getVar("__anonqueue", d, 1) or []
282     body = [x['content'] for x in anonqueue]
283     flag = { 'python' : 1, 'func' : 1 }
284     bb.data.setVar("__anonfunc", "\n".join(body), d)
285     bb.data.setVarFlags("__anonfunc", flag, d)
286     from bb import build
287     try:
288         t = bb.data.getVar('T', d)
289         bb.data.setVar('T', '${TMPDIR}/anonfunc/', d)
290         anonfuncs = bb.data.getVar('__BBANONFUNCS', d) or []
291         code = ""
292         for f in anonfuncs:
293             code = code + "    %s(d)\n" % f
294         bb.data.setVar("__anonfunc", code, d)        
295         build.exec_func("__anonfunc", d)
296         bb.data.delVar('T', d)
297         if t:
298             bb.data.setVar('T', t, d)
299     except Exception, e:
300         bb.msg.debug(1, bb.msg.domain.Parsing, "Exception when executing anonymous function: %s" % e)
301         raise
302     bb.data.delVar("__anonqueue", d)
303     bb.data.delVar("__anonfunc", d)
304     bb.data.update_data(d)
305
306     all_handlers = {} 
307     for var in bb.data.getVar('__BBHANDLERS', d) or []:
308         # try to add the handler
309         handler = bb.data.getVar(var,d)
310         bb.event.register(var, handler)
311
312     tasklist = bb.data.getVar('__BBTASKS', d) or []
313     bb.build.add_tasks(tasklist, d)
314
315     bb.event.fire(bb.event.RecipeParsed(fn), d)
316