cc8bc92e1dbd55d0dfa8953eea4ec2a3cb82b7d8
[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         dname = os.path.dirname(abs_fn)
126         if bbpath[0] != dname:
127             bbpath.insert(0, dname)
128             data.setVar('BBPATH', ":".join(bbpath), d)
129
130     if include:
131         bb.parse.mark_dependency(d, abs_fn)
132
133     if ext != ".bbclass":
134         data.setVar('FILE', fn, d)
135
136     lineno = 0
137     while 1:
138         lineno = lineno + 1
139         s = f.readline()
140         if not s: break
141         s = s.rstrip()
142         feeder(lineno, s, fn, base_name, d)
143     if __inpython__:
144         # add a blank line to close out any python definition
145         feeder(IN_PYTHON_EOF, "", fn, base_name, d)
146     if ext == ".bbclass":
147         classes.remove(__classname__)
148     else:
149         if include == 0:
150             data.expandKeys(d)
151             data.update_data(d)
152             anonqueue = data.getVar("__anonqueue", d, 1) or []
153             body = [x['content'] for x in anonqueue]
154             flag = { 'python' : 1, 'func' : 1 }
155             data.setVar("__anonfunc", "\n".join(body), d)
156             data.setVarFlags("__anonfunc", flag, d)
157             from bb import build
158             try:
159                 t = data.getVar('T', d)
160                 data.setVar('T', '${TMPDIR}/', d)
161                 build.exec_func("__anonfunc", d)
162                 data.delVar('T', d)
163                 if t:
164                     data.setVar('T', t, d)
165             except Exception, e:
166                 bb.msg.debug(1, bb.msg.domain.Parsing, "Exception when executing anonymous function: %s" % e)
167                 raise
168             data.delVar("__anonqueue", d)
169             data.delVar("__anonfunc", d)
170             set_additional_vars(fn, d, include)
171             data.update_data(d)
172
173             all_handlers = {} 
174             for var in data.getVar('__BBHANDLERS', d) or []:
175                 # try to add the handler
176                 handler = data.getVar(var,d)
177                 bb.event.register(var, handler)
178
179             tasklist = data.getVar('__BBTASKS', d) or []
180             bb.build.add_tasks(tasklist, d)
181
182         bbpath.pop(0)
183     if oldfile:
184         bb.data.setVar("FILE", oldfile, d)
185
186     # we have parsed the bb class now
187     if ext == ".bbclass" or ext == ".inc":
188         __parsed_methods__[base_name] = 1
189
190     return d
191
192 def feeder(lineno, s, fn, root, d):
193     global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__,__infunc__, __body__, classes, bb, __residue__
194     if __infunc__:
195         if s == '}':
196             __body__.append('')
197             data.setVar(__infunc__, '\n'.join(__body__), d)
198             data.setVarFlag(__infunc__, "func", 1, d)
199             if __infunc__ == "__anonymous":
200                 anonqueue = bb.data.getVar("__anonqueue", d) or []
201                 anonitem = {}
202                 anonitem["content"] = bb.data.getVar("__anonymous", d)
203                 anonitem["flags"] = bb.data.getVarFlags("__anonymous", d)
204                 anonqueue.append(anonitem)
205                 bb.data.setVar("__anonqueue", anonqueue, d)
206                 bb.data.delVarFlags("__anonymous", d)
207                 bb.data.delVar("__anonymous", d)
208             __infunc__ = ""
209             __body__ = []
210         else:
211             __body__.append(s)
212         return
213
214     if __inpython__:
215         m = __python_func_regexp__.match(s)
216         if m and lineno != IN_PYTHON_EOF:
217             __body__.append(s)
218             return
219         else:
220             # Note we will add root to parsedmethods after having parse
221             # 'this' file. This means we will not parse methods from
222             # bb classes twice
223             if not root  in __parsed_methods__:
224                 text = '\n'.join(__body__)
225                 methodpool.insert_method( root, text, fn )
226                 funcs = data.getVar('__functions__', d) or {}
227                 if not funcs.has_key( root ):
228                     funcs[root] = text 
229                 else:
230                     funcs[root] = "%s\n%s" % (funcs[root], text)
231
232                 data.setVar('__functions__', funcs, d)
233             __body__ = []
234             __inpython__ = False
235
236             if lineno == IN_PYTHON_EOF:
237                 return
238
239 #           fall through
240
241     if s == '' or s[0] == '#': return          # skip comments and empty lines
242
243     if s[-1] == '\\':
244         __residue__.append(s[:-1])
245         return
246
247     s = "".join(__residue__) + s
248     __residue__ = []
249
250     m = __func_start_regexp__.match(s)
251     if m:
252         __infunc__ = m.group("func") or "__anonymous"
253         key = __infunc__
254         if data.getVar(key, d):
255 #           clean up old version of this piece of metadata, as its
256 #           flags could cause problems
257             data.setVarFlag(key, 'python', None, d)
258             data.setVarFlag(key, 'fakeroot', None, d)
259         if m.group("py") is not None:
260             data.setVarFlag(key, "python", "1", d)
261         else:
262             data.delVarFlag(key, "python", d)
263         if m.group("fr") is not None:
264             data.setVarFlag(key, "fakeroot", "1", d)
265         else:
266             data.delVarFlag(key, "fakeroot", d)
267         return
268
269     m = __def_regexp__.match(s)
270     if m:
271         __body__.append(s)
272         __inpython__ = True
273         return
274
275     m = __export_func_regexp__.match(s)
276     if m:
277         fns = m.group(1)
278         n = __word__.findall(fns)
279         for f in n:
280             allvars = []
281             allvars.append(f)
282             allvars.append(classes[-1] + "_" + f)
283
284             vars = [[ allvars[0], allvars[1] ]]
285             if len(classes) > 1 and classes[-2] is not None:
286                 allvars.append(classes[-2] + "_" + f)
287                 vars = []
288                 vars.append([allvars[2], allvars[1]])
289                 vars.append([allvars[0], allvars[2]])
290
291             for (var, calledvar) in vars:
292                 if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
293                     continue
294
295                 if data.getVar(var, d):
296                     data.setVarFlag(var, 'python', None, d)
297                     data.setVarFlag(var, 'func', None, d)
298
299                 for flag in [ "func", "python" ]:
300                     if data.getVarFlag(calledvar, flag, d):
301                         data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
302                 for flag in [ "dirs" ]:
303                     if data.getVarFlag(var, flag, d):
304                         data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
305
306                 if data.getVarFlag(calledvar, "python", d):
307                     data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
308                 else:
309                     data.setVar(var, "\t" + calledvar + "\n", d)
310                 data.setVarFlag(var, 'export_func', '1', d)
311
312         return
313
314     m = __addtask_regexp__.match(s)
315     if m:
316         func = m.group("func")
317         before = m.group("before")
318         after = m.group("after")
319         if func is None:
320             return
321         var = "do_" + func
322
323         data.setVarFlag(var, "task", 1, d)
324
325         bbtasks = data.getVar('__BBTASKS', d) or []
326         if not var in bbtasks:
327             bbtasks.append(var)
328         data.setVar('__BBTASKS', bbtasks, d)
329
330         existing = data.getVarFlag(var, "deps", d) or []
331         if after is not None:
332             # set up deps for function
333             for entry in after.split():
334                 if entry not in existing:
335                     existing.append(entry)
336         data.setVarFlag(var, "deps", existing, d)
337         if before is not None:
338             # set up things that depend on this func
339             for entry in before.split():
340                 existing = data.getVarFlag(entry, "deps", d) or []
341                 if var not in existing:
342                     data.setVarFlag(entry, "deps", [var] + existing, d)
343         return
344
345     m = __addhandler_regexp__.match(s)
346     if m:
347         fns = m.group(1)
348         hs = __word__.findall(fns)
349         bbhands = data.getVar('__BBHANDLERS', d) or []
350         for h in hs:
351             bbhands.append(h)
352             data.setVarFlag(h, "handler", 1, d)
353         data.setVar('__BBHANDLERS', bbhands, 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     if len(parts) > 3:
378         raise ParseError("Unable to generate default variables from the filename: %s (too many underscores)" % mypkg)
379     exp = 3 - len(parts)
380     tmplist = []
381     while exp != 0:
382         exp -= 1
383         tmplist.append(None)
384     parts.extend(tmplist)
385     return parts
386
387 def set_additional_vars(file, d, include):
388     """Deduce rest of variables, e.g. ${A} out of ${SRC_URI}"""
389
390     return
391     # Nothing seems to use this variable
392     #bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s: set_additional_vars" % file)
393
394     #src_uri = data.getVar('SRC_URI', d, 1)
395     #if not src_uri:
396     #    return
397
398     #a = (data.getVar('A', d, 1) or '').split()
399
400     #from bb import fetch
401     #try:
402     #    ud = fetch.init(src_uri.split(), d)
403     #    a += fetch.localpaths(d, ud)
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     #del fetch
409
410     #data.setVar('A', " ".join(a), d)
411
412
413 # Add us to the handlers list
414 from bb.parse import handlers
415 handlers.append({'supports': supports, 'handle': handle, 'init': init})
416 del handlers