BBHandler: pass lineno and fn to handleMethod
[bitbake.git] / lib / bb / parse / parse_py / BBHandler.py
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 """
5    class for handling .bb files
6
7    Reads a .bb file and obtains its metadata
8
9 """
10
11
12 #  Copyright (C) 2003, 2004  Chris Larson
13 #  Copyright (C) 2003, 2004  Phil Blundell
14 #   
15 # This program is free software; you can redistribute it and/or modify
16 # it under the terms of the GNU General Public License version 2 as
17 # published by the Free Software Foundation.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License along
25 # with this program; if not, write to the Free Software Foundation, Inc.,
26 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27
28 import re, bb, os, sys, time, string
29 import bb.fetch, bb.build, bb.utils
30 from bb import data, fetch, methodpool
31
32 from ConfHandler import include, init
33 from bb.parse import ParseError, resolve_file
34
35 __func_start_regexp__    = re.compile( r"(((?P<py>python)|(?P<fr>fakeroot))\s*)*(?P<func>[\w\.\-\+\{\}\$]+)?\s*\(\s*\)\s*{$" )
36 __inherit_regexp__       = re.compile( r"inherit\s+(.+)" )
37 __export_func_regexp__   = re.compile( r"EXPORT_FUNCTIONS\s+(.+)" )
38 __addtask_regexp__       = re.compile("addtask\s+(?P<func>\w+)\s*((before\s*(?P<before>((.*(?=after))|(.*))))|(after\s*(?P<after>((.*(?=before))|(.*)))))*")
39 __addhandler_regexp__    = re.compile( r"addhandler\s+(.+)" )
40 __def_regexp__           = re.compile( r"def\s+(\w+).*:" )
41 __python_func_regexp__   = re.compile( r"(\s+.*)|(^$)" )
42 __word__ = re.compile(r"\S+")
43
44 __infunc__ = ""
45 __inpython__ = False
46 __body__   = []
47 __classname__ = ""
48 classes = [ None, ]
49
50 # We need to indicate EOF to the feeder. This code is so messy that
51 # factoring it out to a close_parse_file method is out of question.
52 # We will use the IN_PYTHON_EOF as an indicator to just close the method
53 #
54 # The two parts using it are tightly integrated anyway
55 IN_PYTHON_EOF = -9999999999999
56
57 __parsed_methods__ = methodpool.get_parsed_dict()
58
59 # parsing routines, to be moved into AST classes
60 def handleMethod(func_name, lineno, fn, body, d):
61     if func_name == "__anonymous":
62         funcname = ("__anon_%s_%s" % (lineno, fn.translate(string.maketrans('/.+-', '____'))))
63         if not funcname in methodpool._parsed_fns:
64             text = "def %s(d):\n" % (funcname) + '\n'.join(body)
65             methodpool.insert_method(funcname, text, fn)
66         anonfuncs = data.getVar('__BBANONFUNCS', d) or []
67         anonfuncs.append(funcname)
68         data.setVar('__BBANONFUNCS', anonfuncs, d)
69     else:
70         data.setVarFlag(func_name, "func", 1, d)
71         data.setVar(func_name, '\n'.join(body), d)
72
73 def handlePythonMethod(root, body, fn):
74     # Note we will add root to parsedmethods after having parse
75     # 'this' file. This means we will not parse methods from
76     # bb classes twice
77     if not root in __parsed_methods__:
78         text = '\n'.join(body)
79         methodpool.insert_method(root, text, fn)
80
81 def handleMethodFlags(key, m, d):
82     if data.getVar(key, d):
83         # Clean up old version of this piece of metadata, as its
84         # flags could cause problems
85         data.setVarFlag(key, 'python', None, d)
86         data.setVarFlag(key, 'fakeroot', None, d)
87     if m.group("py") is not None:
88         data.setVarFlag(key, "python", "1", d)
89     else:
90         data.delVarFlag(key, "python", d)
91     if m.group("fr") is not None:
92         data.setVarFlag(key, "fakeroot", "1", d)
93     else:
94         data.delVarFlag(key, "fakeroot", d)
95
96 def handleExportFuncs(m, d):
97     fns = m.group(1)
98     n = __word__.findall(fns)
99     for f in n:
100         allvars = []
101         allvars.append(f)
102         allvars.append(classes[-1] + "_" + f)
103
104         vars = [[ allvars[0], allvars[1] ]]
105         if len(classes) > 1 and classes[-2] is not None:
106             allvars.append(classes[-2] + "_" + f)
107             vars = []
108             vars.append([allvars[2], allvars[1]])
109             vars.append([allvars[0], allvars[2]])
110
111         for (var, calledvar) in vars:
112             if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
113                 continue
114
115             if data.getVar(var, d):
116                 data.setVarFlag(var, 'python', None, d)
117                 data.setVarFlag(var, 'func', None, d)
118
119             for flag in [ "func", "python" ]:
120                 if data.getVarFlag(calledvar, flag, d):
121                     data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
122             for flag in [ "dirs" ]:
123                 if data.getVarFlag(var, flag, d):
124                     data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
125
126             if data.getVarFlag(calledvar, "python", d):
127                 data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
128             else:
129                 data.setVar(var, "\t" + calledvar + "\n", d)
130             data.setVarFlag(var, 'export_func', '1', d)
131
132 def handleAddTask(m, d):
133     func = m.group("func")
134     before = m.group("before")
135     after = m.group("after")
136     if func is None:
137         return
138     if func[:3] != "do_":
139         var = "do_" + func
140
141     data.setVarFlag(var, "task", 1, d)
142
143     bbtasks = data.getVar('__BBTASKS', d) or []
144     if not var in bbtasks:
145         bbtasks.append(var)
146     data.setVar('__BBTASKS', bbtasks, d)
147
148     existing = data.getVarFlag(var, "deps", d) or []
149     if after is not None:
150         # set up deps for function
151         for entry in after.split():
152             if entry not in existing:
153                 existing.append(entry)
154     data.setVarFlag(var, "deps", existing, d)
155     if before is not None:
156         # set up things that depend on this func
157         for entry in before.split():
158             existing = data.getVarFlag(entry, "deps", d) or []
159             if var not in existing:
160                 data.setVarFlag(entry, "deps", [var] + existing, d)
161
162 def handleBBHandlers(m, d):
163     fns = m.group(1)
164     hs = __word__.findall(fns)
165     bbhands = data.getVar('__BBHANDLERS', d) or []
166     for h in hs:
167         bbhands.append(h)
168         data.setVarFlag(h, "handler", 1, d)
169     data.setVar('__BBHANDLERS', bbhands, d)
170
171 def handleInherit(m, d):
172     files = m.group(1)
173     n = __word__.findall(files)
174     inherit(n, d)
175
176 def supports(fn, d):
177     return fn[-3:] == ".bb" or fn[-8:] == ".bbclass" or fn[-4:] == ".inc"
178
179 def inherit(files, d):
180     __inherit_cache = data.getVar('__inherit_cache', d) or []
181     fn = ""
182     lineno = 0
183     files = data.expand(files, d)
184     for file in files:
185         if file[0] != "/" and file[-8:] != ".bbclass":
186             file = os.path.join('classes', '%s.bbclass' % file)
187
188         if not file in __inherit_cache:
189             bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s:%d: inheriting %s" % (fn, lineno, file))
190             __inherit_cache.append( file )
191             data.setVar('__inherit_cache', __inherit_cache, d)
192             include(fn, file, d, "inherit")
193             __inherit_cache = data.getVar('__inherit_cache', d) or []
194
195
196 def finalise(fn, d):
197     data.expandKeys(d)
198     data.update_data(d)
199     anonqueue = data.getVar("__anonqueue", d, 1) or []
200     body = [x['content'] for x in anonqueue]
201     flag = { 'python' : 1, 'func' : 1 }
202     data.setVar("__anonfunc", "\n".join(body), d)
203     data.setVarFlags("__anonfunc", flag, d)
204     from bb import build
205     try:
206         t = data.getVar('T', d)
207         data.setVar('T', '${TMPDIR}/anonfunc/', d)
208         anonfuncs = data.getVar('__BBANONFUNCS', d) or []
209         code = ""
210         for f in anonfuncs:
211             code = code + "    %s(d)\n" % f
212         data.setVar("__anonfunc", code, d)        
213         build.exec_func("__anonfunc", d)
214         data.delVar('T', d)
215         if t:
216             data.setVar('T', t, d)
217     except Exception, e:
218         bb.msg.debug(1, bb.msg.domain.Parsing, "Exception when executing anonymous function: %s" % e)
219         raise
220     data.delVar("__anonqueue", d)
221     data.delVar("__anonfunc", d)
222     data.update_data(d)
223
224     all_handlers = {} 
225     for var in data.getVar('__BBHANDLERS', d) or []:
226         # try to add the handler
227         handler = data.getVar(var,d)
228         bb.event.register(var, handler)
229
230     tasklist = data.getVar('__BBTASKS', d) or []
231     bb.build.add_tasks(tasklist, d)
232
233     bb.event.fire(bb.event.RecipeParsed(fn), d)
234
235
236 def handle(fn, d, include = 0):
237     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__
238     __body__ = []
239     __infunc__ = ""
240     __classname__ = ""
241     __residue__ = []
242
243     if include == 0:
244         bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data)")
245     else:
246         bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data, include)")
247
248     (root, ext) = os.path.splitext(os.path.basename(fn))
249     base_name = "%s%s" % (root,ext)
250     init(d)
251
252     if ext == ".bbclass":
253         __classname__ = root
254         classes.append(__classname__)
255         __inherit_cache = data.getVar('__inherit_cache', d) or []
256         if not fn in __inherit_cache:
257             __inherit_cache.append(fn)
258             data.setVar('__inherit_cache', __inherit_cache, d)
259
260     if include != 0:
261         oldfile = data.getVar('FILE', d)
262     else:
263         oldfile = None
264
265     (f, abs_fn) = resolve_file(fn, d)
266
267     if include:
268         bb.parse.mark_dependency(d, abs_fn)
269
270     if ext != ".bbclass":
271         data.setVar('FILE', fn, d)
272
273     lineno = 0
274     while 1:
275         lineno = lineno + 1
276         s = f.readline()
277         if not s: break
278         s = s.rstrip()
279         feeder(lineno, s, fn, base_name, d)
280     if __inpython__:
281         # add a blank line to close out any python definition
282         feeder(IN_PYTHON_EOF, "", fn, base_name, d)
283     if ext == ".bbclass":
284         classes.remove(__classname__)
285     else:
286         if include == 0:
287             multi = data.getVar('BBCLASSEXTEND', d, 1)
288             if multi:
289                 based = bb.data.createCopy(d)
290             else:
291                 based = d
292             try:
293                 finalise(fn, based)
294             except bb.parse.SkipPackage:
295                 bb.data.setVar("__SKIPPED", True, based)
296             darray = {"": based}
297
298             for cls in (multi or "").split():
299                 pn = data.getVar('PN', d, True)
300                 based = bb.data.createCopy(d)
301                 data.setVar('PN', pn + '-' + cls, based)
302                 inherit([cls], based)
303                 try:
304                     finalise(fn, based)
305                 except bb.parse.SkipPackage:
306                     bb.data.setVar("__SKIPPED", True, based)
307                 darray[cls] = based
308             return darray
309    
310     if oldfile:
311         bb.data.setVar("FILE", oldfile, d)
312
313     # we have parsed the bb class now
314     if ext == ".bbclass" or ext == ".inc":
315         __parsed_methods__[base_name] = 1
316
317     return d
318
319 def feeder(lineno, s, fn, root, d):
320     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__,__infunc__, __body__, classes, bb, __residue__
321     if __infunc__:
322         if s == '}':
323             __body__.append('')
324             handleMethod(__infunc__, lineno, fn, __body__, d)
325             __infunc__ = ""
326             __body__ = []
327         else:
328             __body__.append(s)
329         return
330
331     if __inpython__:
332         m = __python_func_regexp__.match(s)
333         if m and lineno != IN_PYTHON_EOF:
334             __body__.append(s)
335             return
336         else:
337             handlePythonMethod(root, __body__, fn)
338             __body__ = []
339             __inpython__ = False
340
341             if lineno == IN_PYTHON_EOF:
342                 return
343
344 #           fall through
345
346     if s == '' or s[0] == '#': return          # skip comments and empty lines
347
348     if s[-1] == '\\':
349         __residue__.append(s[:-1])
350         return
351
352     s = "".join(__residue__) + s
353     __residue__ = []
354
355     m = __func_start_regexp__.match(s)
356     if m:
357         __infunc__ = m.group("func") or "__anonymous"
358         handleMethodFlags(__infunc__, m, d)
359         return
360
361     m = __def_regexp__.match(s)
362     if m:
363         __body__.append(s)
364         __inpython__ = True
365         return
366
367     m = __export_func_regexp__.match(s)
368     if m:
369         handleExportFuncs(m, d)
370         return
371
372     m = __addtask_regexp__.match(s)
373     if m:
374         handleAddTask(m, d)
375         return
376
377     m = __addhandler_regexp__.match(s)
378     if m:
379         handleBBHandlers(m, d)
380         return
381
382     m = __inherit_regexp__.match(s)
383     if m:
384         handleInherit(m, d)
385         return
386
387     from bb.parse import ConfHandler
388     return ConfHandler.feeder(lineno, s, fn, d)
389
390 __pkgsplit_cache__={}
391 def vars_from_file(mypkg, d):
392     if not mypkg:
393         return (None, None, None)
394     if mypkg in __pkgsplit_cache__:
395         return __pkgsplit_cache__[mypkg]
396
397     myfile = os.path.splitext(os.path.basename(mypkg))
398     parts = myfile[0].split('_')
399     __pkgsplit_cache__[mypkg] = parts
400     if len(parts) > 3:
401         raise ParseError("Unable to generate default variables from the filename: %s (too many underscores)" % mypkg)
402     exp = 3 - len(parts)
403     tmplist = []
404     while exp != 0:
405         exp -= 1
406         tmplist.append(None)
407     parts.extend(tmplist)
408     return parts
409
410 # Add us to the handlers list
411 from bb.parse import handlers
412 handlers.append({'supports': supports, 'handle': handle, 'init': init})
413 del handlers