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