Revert the '-' character fix in class names since it breaks things
[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         __inherit_cache = data.getVar('__inherit_cache', d) or []
99         if not fn in __inherit_cache:
100             __inherit_cache.append(fn)
101             data.setVar('__inherit_cache', __inherit_cache, d)
102
103     if include != 0:
104         oldfile = data.getVar('FILE', d)
105     else:
106         oldfile = None
107
108     fn = obtain(fn, d)
109     bbpath = (data.getVar('BBPATH', d, 1) or '').split(':')
110     if not os.path.isabs(fn):
111         f = None
112         for p in bbpath:
113             j = os.path.join(p, fn)
114             if os.access(j, os.R_OK):
115                 abs_fn = j
116                 f = open(j, 'r')
117                 break
118         if f is None:
119             raise IOError("file not found")
120     else:
121         f = open(fn,'r')
122         abs_fn = fn
123
124     if ext != ".bbclass":
125         bbpath.insert(0, os.path.dirname(abs_fn))
126         data.setVar('BBPATH', ":".join(bbpath), d)
127
128     if include:
129         bb.parse.mark_dependency(d, abs_fn)
130
131     if ext != ".bbclass":
132         data.setVar('FILE', fn, 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, "Exception when 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                 handler = data.getVar(var,d)
175                 bb.event.register(var, handler)
176
177             tasklist = data.getVar('__BBTASKS', d) or []
178             bb.build.add_tasks(tasklist, d)
179
180         bbpath.pop(0)
181     if oldfile:
182         bb.data.setVar("FILE", oldfile, d)
183
184     # we have parsed the bb class now
185     if ext == ".bbclass" or ext == ".inc":
186         __parsed_methods__[base_name] = 1
187
188     return d
189
190 def feeder(lineno, s, fn, root, d):
191     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__,__infunc__, __body__, classes, bb, __residue__
192     if __infunc__:
193         if s == '}':
194             __body__.append('')
195             data.setVar(__infunc__, '\n'.join(__body__), d)
196             data.setVarFlag(__infunc__, "func", 1, d)
197             if __infunc__ == "__anonymous":
198                 anonqueue = bb.data.getVar("__anonqueue", d) or []
199                 anonitem = {}
200                 anonitem["content"] = bb.data.getVar("__anonymous", d)
201                 anonitem["flags"] = bb.data.getVarFlags("__anonymous", d)
202                 anonqueue.append(anonitem)
203                 bb.data.setVar("__anonqueue", anonqueue, d)
204                 bb.data.delVarFlags("__anonymous", d)
205                 bb.data.delVar("__anonymous", d)
206             __infunc__ = ""
207             __body__ = []
208         else:
209             __body__.append(s)
210         return
211
212     if __inpython__:
213         m = __python_func_regexp__.match(s)
214         if m and lineno != IN_PYTHON_EOF:
215             __body__.append(s)
216             return
217         else:
218             # Note we will add root to parsedmethods after having parse
219             # 'this' file. This means we will not parse methods from
220             # bb classes twice
221             if not root  in __parsed_methods__:
222                 text = '\n'.join(__body__)
223                 methodpool.insert_method( root, text, fn )
224                 funcs = data.getVar('__functions__', d) or {}
225                 if not funcs.has_key( root ):
226                     funcs[root] = text 
227                 else:
228                     funcs[root] = "%s\n%s" % (funcs[root], text)
229
230                 data.setVar('__functions__', funcs, d)
231             __body__ = []
232             __inpython__ = False
233
234             if lineno == IN_PYTHON_EOF:
235                 return
236
237 #           fall through
238
239     if s == '' or s[0] == '#': return          # skip comments and empty lines
240
241     if s[-1] == '\\':
242         __residue__.append(s[:-1])
243         return
244
245     s = "".join(__residue__) + s
246     __residue__ = []
247
248     m = __func_start_regexp__.match(s)
249     if m:
250         __infunc__ = m.group("func") or "__anonymous"
251         key = __infunc__
252         if data.getVar(key, d):
253 #           clean up old version of this piece of metadata, as its
254 #           flags could cause problems
255             data.setVarFlag(key, 'python', None, d)
256             data.setVarFlag(key, 'fakeroot', None, d)
257         if m.group("py") is not None:
258             data.setVarFlag(key, "python", "1", d)
259         else:
260             data.delVarFlag(key, "python", d)
261         if m.group("fr") is not None:
262             data.setVarFlag(key, "fakeroot", "1", d)
263         else:
264             data.delVarFlag(key, "fakeroot", d)
265         return
266
267     m = __def_regexp__.match(s)
268     if m:
269         __body__.append(s)
270         __inpython__ = True
271         return
272
273     m = __export_func_regexp__.match(s)
274     if m:
275         fns = m.group(1)
276         n = __word__.findall(fns)
277         for f in n:
278             allvars = []
279             allvars.append(f)
280             allvars.append(classes[-1] + "_" + f)
281
282             vars = [[ allvars[0], allvars[1] ]]
283             if len(classes) > 1 and classes[-2] is not None:
284                 allvars.append(classes[-2] + "_" + f)
285                 vars = []
286                 vars.append([allvars[2], allvars[1]])
287                 vars.append([allvars[0], allvars[2]])
288
289             for (var, calledvar) in vars:
290                 if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
291                     continue
292
293                 if data.getVar(var, d):
294                     data.setVarFlag(var, 'python', None, d)
295                     data.setVarFlag(var, 'func', None, d)
296
297                 for flag in [ "func", "python" ]:
298                     if data.getVarFlag(calledvar, flag, d):
299                         data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
300                 for flag in [ "dirs" ]:
301                     if data.getVarFlag(var, flag, d):
302                         data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
303
304                 if data.getVarFlag(calledvar, "python", d):
305                     data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
306                 else:
307                     data.setVar(var, "\t" + calledvar + "\n", d)
308                 data.setVarFlag(var, 'export_func', '1', d)
309
310         return
311
312     m = __addtask_regexp__.match(s)
313     if m:
314         func = m.group("func")
315         before = m.group("before")
316         after = m.group("after")
317         if func is None:
318             return
319         var = "do_" + func
320
321         data.setVarFlag(var, "task", 1, d)
322
323         bbtasks = data.getVar('__BBTASKS', d) or []
324         if not var in bbtasks:
325             bbtasks.append(var)
326         data.setVar('__BBTASKS', bbtasks, d)
327
328         existing = data.getVarFlag(var, "deps", d) or []
329         if after is not None:
330             # set up deps for function
331             for entry in after.split():
332                 if entry not in existing:
333                     existing.append(entry)
334         data.setVarFlag(var, "deps", existing, d)
335         if before is not None:
336             # set up things that depend on this func
337             for entry in before.split():
338                 existing = data.getVarFlag(entry, "deps", d) or []
339                 if var not in existing:
340                     data.setVarFlag(entry, "deps", [var] + existing, d)
341         return
342
343     m = __addhandler_regexp__.match(s)
344     if m:
345         fns = m.group(1)
346         hs = __word__.findall(fns)
347         bbhands = data.getVar('__BBHANDLERS', d) or []
348         for h in hs:
349             bbhands.append(h)
350             data.setVarFlag(h, "handler", 1, d)
351         data.setVar('__BBHANDLERS', bbhands, d)
352         return
353
354     m = __inherit_regexp__.match(s)
355     if m:
356
357         files = m.group(1)
358         n = __word__.findall(files)
359         inherit(n, d)
360         return
361
362     from bb.parse import ConfHandler
363     return ConfHandler.feeder(lineno, s, fn, d)
364
365 __pkgsplit_cache__={}
366 def vars_from_file(mypkg, d):
367     if not mypkg:
368         return (None, None, None)
369     if mypkg in __pkgsplit_cache__:
370         return __pkgsplit_cache__[mypkg]
371
372     myfile = os.path.splitext(os.path.basename(mypkg))
373     parts = myfile[0].split('_')
374     __pkgsplit_cache__[mypkg] = parts
375     if len(parts) > 3:
376         raise ParseError("Unable to generate default variables from the filename: %s (too many underscores)" % mypkg)
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     return
389     # Nothing seems to use this variable
390     #bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s: set_additional_vars" % file)
391
392     #src_uri = data.getVar('SRC_URI', d, 1)
393     #if not src_uri:
394     #    return
395
396     #a = (data.getVar('A', d, 1) or '').split()
397
398     #from bb import fetch
399     #try:
400     #    ud = fetch.init(src_uri.split(), d)
401     #    a += fetch.localpaths(d, ud)
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     #del fetch
407
408     #data.setVar('A', " ".join(a), d)
409
410
411 # Add us to the handlers list
412 from bb.parse import handlers
413 handlers.append({'supports': supports, 'handle': handle, 'init': init})
414 del handlers