Fix a second kind of __inherit_cache race (trunk + branches)
[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 """
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
29 import bb.fetch, bb.build, bb.utils
30 from bb import data, fetch, methodpool
31
32 from ConfHandler import include, localpath, obtain, init
33 from bb.parse import ParseError
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 def supports(fn, d):
60     localfn = localpath(fn, d)
61     return localfn[-3:] == ".bb" or localfn[-8:] == ".bbclass" or localfn[-4:] == ".inc"
62
63 def inherit(files, d):
64     __inherit_cache = data.getVar('__inherit_cache', d) or []
65     fn = ""
66     lineno = 0
67     files = data.expand(files, d)
68     for file in files:
69         if file[0] != "/" and file[-8:] != ".bbclass":
70             file = os.path.join('classes', '%s.bbclass' % file)
71
72         if not file in __inherit_cache:
73             bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s:%d: inheriting %s" % (fn, lineno, file))
74             __inherit_cache.append( file )
75             data.setVar('__inherit_cache', __inherit_cache, d)
76             include(fn, file, d, "inherit")
77             __inherit_cache = data.getVar('__inherit_cache', d) or []
78
79 def handle(fn, d, include = 0):
80     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__
81     __body__ = []
82     __infunc__ = ""
83     __classname__ = ""
84     __residue__ = []
85
86     if include == 0:
87         bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data)")
88     else:
89         bb.msg.debug(2, bb.msg.domain.Parsing, "BB " + fn + ": handle(data, include)")
90
91     (root, ext) = os.path.splitext(os.path.basename(fn))
92     base_name = "%s%s" % (root,ext)
93     init(d)
94
95     if ext == ".bbclass":
96         __classname__ = root
97         classes.append(__classname__)
98
99     if include != 0:
100         oldfile = data.getVar('FILE', d)
101     else:
102         oldfile = None
103
104     fn = obtain(fn, d)
105     bbpath = (data.getVar('BBPATH', d, 1) or '').split(':')
106     if not os.path.isabs(fn):
107         f = None
108         for p in bbpath:
109             j = os.path.join(p, fn)
110             if os.access(j, os.R_OK):
111                 abs_fn = j
112                 f = open(j, 'r')
113                 break
114         if f is None:
115             raise IOError("file not found")
116     else:
117         f = open(fn,'r')
118         abs_fn = fn
119
120     if ext != ".bbclass":
121         bbpath.insert(0, os.path.dirname(abs_fn))
122         data.setVar('BBPATH', ":".join(bbpath), d)
123
124     if include:
125         bb.parse.mark_dependency(d, abs_fn)
126
127     if ext != ".bbclass":
128         data.setVar('FILE', fn, d)
129         i = (data.getVar("INHERIT", d, 1) or "").split()
130         if not "base" in i and __classname__ != "base":
131             i[0:0] = ["base"]
132         inherit(i, d)
133
134     lineno = 0
135     while 1:
136         lineno = lineno + 1
137         s = f.readline()
138         if not s: break
139         s = s.rstrip()
140         feeder(lineno, s, fn, base_name, d)
141     if __inpython__:
142         # add a blank line to close out any python definition
143         feeder(IN_PYTHON_EOF, "", fn, base_name, d)
144     if ext == ".bbclass":
145         classes.remove(__classname__)
146     else:
147         if include == 0:
148             data.expandKeys(d)
149             data.update_data(d)
150             anonqueue = data.getVar("__anonqueue", d, 1) or []
151             body = [x['content'] for x in anonqueue]
152             flag = { 'python' : 1, 'func' : 1 }
153             data.setVar("__anonfunc", "\n".join(body), d)
154             data.setVarFlags("__anonfunc", flag, d)
155             from bb import build
156             try:
157                 t = data.getVar('T', d)
158                 data.setVar('T', '${TMPDIR}/', d)
159                 build.exec_func("__anonfunc", d)
160                 data.delVar('T', d)
161                 if t:
162                     data.setVar('T', t, d)
163             except Exception, e:
164                 bb.msg.debug(1, bb.msg.domain.Parsing, "executing anonymous function: %s" % e)
165                 raise
166             data.delVar("__anonqueue", d)
167             data.delVar("__anonfunc", d)
168             set_additional_vars(fn, d, include)
169             data.update_data(d)
170
171             all_handlers = {} 
172             for var in data.getVar('__BBHANDLERS', d) or []:
173                 # try to add the handler
174                 # if we added it remember the choiche
175                 handler = data.getVar(var,d)
176                 if bb.event.register(var,handler) == bb.event.Registered:
177                     all_handlers[var] = handler
178
179             for var in data.getVar('__BBTASKS', d) or []:
180                 deps = data.getVarFlag(var, 'deps', d) or []
181                 postdeps = data.getVarFlag(var, 'postdeps', d) or []
182                 bb.build.add_task(var, deps, d)
183                 for p in postdeps:
184                     pdeps = data.getVarFlag(p, 'deps', d) or []
185                     pdeps.append(var)
186                     data.setVarFlag(p, 'deps', pdeps, d)
187                     bb.build.add_task(p, pdeps, d)
188
189             # now add the handlers
190             if not len(all_handlers) == 0:
191                 data.setVar('__all_handlers__', all_handlers, d)
192
193         bbpath.pop(0)
194     if oldfile:
195         bb.data.setVar("FILE", oldfile, d)
196
197     # we have parsed the bb class now
198     if ext == ".bbclass" or ext == ".inc":
199         __parsed_methods__[base_name] = 1
200
201     return d
202
203 def feeder(lineno, s, fn, root, d):
204     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__,__infunc__, __body__, classes, bb, __residue__
205     if __infunc__:
206         if s == '}':
207             __body__.append('')
208             data.setVar(__infunc__, '\n'.join(__body__), d)
209             data.setVarFlag(__infunc__, "func", 1, d)
210             if __infunc__ == "__anonymous":
211                 anonqueue = bb.data.getVar("__anonqueue", d) or []
212                 anonitem = {}
213                 anonitem["content"] = bb.data.getVar("__anonymous", d)
214                 anonitem["flags"] = bb.data.getVarFlags("__anonymous", d)
215                 anonqueue.append(anonitem)
216                 bb.data.setVar("__anonqueue", anonqueue, d)
217                 bb.data.delVarFlags("__anonymous", d)
218                 bb.data.delVar("__anonymous", d)
219             __infunc__ = ""
220             __body__ = []
221         else:
222             __body__.append(s)
223         return
224
225     if __inpython__:
226         m = __python_func_regexp__.match(s)
227         if m and lineno != IN_PYTHON_EOF:
228             __body__.append(s)
229             return
230         else:
231             # Note we will add root to parsedmethods after having parse
232             # 'this' file. This means we will not parse methods from
233             # bb classes twice
234             if not root  in __parsed_methods__:
235                 text = '\n'.join(__body__)
236                 methodpool.insert_method( root, text, fn )
237                 funcs = data.getVar('__functions__', d) or {}
238                 if not funcs.has_key( root ):
239                     funcs[root] = text 
240                 else:
241                     funcs[root] = "%s\n%s" % (funcs[root], text)
242
243                 data.setVar('__functions__', funcs, d)
244             __body__ = []
245             __inpython__ = False
246
247             if lineno == IN_PYTHON_EOF:
248                 return
249
250 #           fall through
251
252     if s == '' or s[0] == '#': return          # skip comments and empty lines
253
254     if s[-1] == '\\':
255         __residue__.append(s[:-1])
256         return
257
258     s = "".join(__residue__) + s
259     __residue__ = []
260
261     m = __func_start_regexp__.match(s)
262     if m:
263         __infunc__ = m.group("func") or "__anonymous"
264         key = __infunc__
265         if data.getVar(key, d):
266 #           clean up old version of this piece of metadata, as its
267 #           flags could cause problems
268             data.setVarFlag(key, 'python', None, d)
269             data.setVarFlag(key, 'fakeroot', None, d)
270         if m.group("py") is not None:
271             data.setVarFlag(key, "python", "1", d)
272         else:
273             data.delVarFlag(key, "python", d)
274         if m.group("fr") is not None:
275             data.setVarFlag(key, "fakeroot", "1", d)
276         else:
277             data.delVarFlag(key, "fakeroot", d)
278         return
279
280     m = __def_regexp__.match(s)
281     if m:
282         __body__.append(s)
283         __inpython__ = True
284         return
285
286     m = __export_func_regexp__.match(s)
287     if m:
288         fns = m.group(1)
289         n = __word__.findall(fns)
290         for f in n:
291             allvars = []
292             allvars.append(f)
293             allvars.append(classes[-1] + "_" + f)
294
295             vars = [[ allvars[0], allvars[1] ]]
296             if len(classes) > 1 and classes[-2] is not None:
297                 allvars.append(classes[-2] + "_" + f)
298                 vars = []
299                 vars.append([allvars[2], allvars[1]])
300                 vars.append([allvars[0], allvars[2]])
301
302             for (var, calledvar) in vars:
303                 if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
304                     continue
305
306                 if data.getVar(var, d):
307                     data.setVarFlag(var, 'python', None, d)
308                     data.setVarFlag(var, 'func', None, d)
309
310                 for flag in [ "func", "python" ]:
311                     if data.getVarFlag(calledvar, flag, d):
312                         data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
313                 for flag in [ "dirs" ]:
314                     if data.getVarFlag(var, flag, d):
315                         data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
316
317                 if data.getVarFlag(calledvar, "python", d):
318                     data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
319                 else:
320                     data.setVar(var, "\t" + calledvar + "\n", d)
321                 data.setVarFlag(var, 'export_func', '1', d)
322
323         return
324
325     m = __addtask_regexp__.match(s)
326     if m:
327         func = m.group("func")
328         before = m.group("before")
329         after = m.group("after")
330         if func is None:
331             return
332         var = "do_" + func
333
334         data.setVarFlag(var, "task", 1, d)
335
336         bbtasks = data.getVar('__BBTASKS', d) or []
337         bbtasks.append(var)
338         data.setVar('__BBTASKS', bbtasks, 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         bbhands = data.getVar('__BBHANDLERS', d) or []
353         for h in hs:
354             bbhands.append(h)
355             data.setVarFlag(h, "handler", 1, d)
356         data.setVar('__BBHANDLERS', bbhands, d)
357         return
358
359     m = __inherit_regexp__.match(s)
360     if m:
361
362         files = m.group(1)
363         n = __word__.findall(files)
364         inherit(n, d)
365         return
366
367     from bb.parse import ConfHandler
368     return ConfHandler.feeder(lineno, s, fn, d)
369
370 __pkgsplit_cache__={}
371 def vars_from_file(mypkg, d):
372     if not mypkg:
373         return (None, None, None)
374     if mypkg in __pkgsplit_cache__:
375         return __pkgsplit_cache__[mypkg]
376
377     myfile = os.path.splitext(os.path.basename(mypkg))
378     parts = myfile[0].split('_')
379     __pkgsplit_cache__[mypkg] = parts
380     exp = 3 - len(parts)
381     tmplist = []
382     while exp != 0:
383         exp -= 1
384         tmplist.append(None)
385     parts.extend(tmplist)
386     return parts
387
388 def set_additional_vars(file, d, include):
389     """Deduce rest of variables, e.g. ${A} out of ${SRC_URI}"""
390
391     bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s: set_additional_vars" % file)
392
393     src_uri = data.getVar('SRC_URI', d, 1)
394     if not src_uri:
395         return
396
397     a = (data.getVar('A', d, 1) or '').split()
398
399     from bb import fetch
400     try:
401         fetch.init(src_uri.split(), d)
402     except fetch.NoMethodError:
403         pass
404     except bb.MalformedUrl,e:
405         raise ParseError("Unable to generate local paths for SRC_URI due to malformed uri: %s" % e)
406
407     a += fetch.localpaths(d)
408     del fetch
409     data.setVar('A', " ".join(a), d)
410
411
412 # Add us to the handlers list
413 from bb.parse import handlers
414 handlers.append({'supports': supports, 'handle': handle, 'init': init})
415 del handlers