lib/bb/parse/parse_py/BBHandler.py: Commit the parsed method at EOF
[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.split():
70             bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s:%d: inheriting %s" % (fn, lineno, file))
71             __inherit_cache += " %s" % file
72             include(fn, file, d, True)
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             for anon in anonqueue:
151                 data.setVar("__anonfunc", anon["content"], d)
152                 data.setVarFlags("__anonfunc", anon["flags"], d)
153                 from bb import build
154                 try:
155                     t = data.getVar('T', d)
156                     data.setVar('T', '${TMPDIR}/', d)
157                     build.exec_func("__anonfunc", d)
158                     data.delVar('T', d)
159                     if t:
160                         data.setVar('T', t, d)
161                 except Exception, e:
162                     bb.msg.debug(1, bb.msg.domain.Parsing, "executing anonymous function: %s" % e)
163                     raise
164             data.delVar("__anonqueue", d)
165             data.delVar("__anonfunc", d)
166             set_additional_vars(fn, d, include)
167             data.update_data(d)
168
169             all_handlers = {} 
170             for var in data.keys(d):
171                 # try to add the handler
172                 # if we added it remember the choiche
173                 if data.getVarFlag(var, 'handler', d):
174                     handler = data.getVar(var,d)
175                     if bb.event.register(var,handler) == bb.event.Registered:
176                         all_handlers[var] = handler
177
178                     continue
179
180                 if not data.getVarFlag(var, 'task', d):
181                     continue
182
183                 deps = data.getVarFlag(var, 'deps', d) or []
184                 postdeps = data.getVarFlag(var, 'postdeps', d) or []
185                 bb.build.add_task(var, deps, d)
186                 for p in postdeps:
187                     pdeps = data.getVarFlag(p, 'deps', d) or []
188                     pdeps.append(var)
189                     data.setVarFlag(p, 'deps', pdeps, d)
190                     bb.build.add_task(p, pdeps, d)
191
192             # now add the handlers
193             if not len(all_handlers) == 0:
194                 data.setVar('__all_handlers__', all_handlers, d)
195
196         bbpath.pop(0)
197     if oldfile:
198         bb.data.setVar("FILE", oldfile, d)
199
200     # we have parsed the bb class now
201     if ext == ".bbclass" or ext == ".inc":
202         __parsed_methods__[base_name] = 1
203
204     return d
205
206 def feeder(lineno, s, fn, root, d):
207     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__
208     if __infunc__:
209         if s == '}':
210             __body__.append('')
211             data.setVar(__infunc__, '\n'.join(__body__), d)
212             data.setVarFlag(__infunc__, "func", 1, d)
213             if __infunc__ == "__anonymous":
214                 anonqueue = bb.data.getVar("__anonqueue", d) or []
215                 anonitem = {}
216                 anonitem["content"] = bb.data.getVar("__anonymous", d)
217                 anonitem["flags"] = bb.data.getVarFlags("__anonymous", d)
218                 anonqueue.append(anonitem)
219                 bb.data.setVar("__anonqueue", anonqueue, d)
220                 bb.data.delVarFlags("__anonymous", d)
221                 bb.data.delVar("__anonymous", d)
222             __infunc__ = ""
223             __body__ = []
224         else:
225             __body__.append(s)
226         return
227
228     if __inpython__:
229         m = __python_func_regexp__.match(s)
230         if m and lineno != IN_PYTHON_EOF:
231             __body__.append(s)
232             return
233         else:
234             # Note we will add root to parsedmethods after having parse
235             # 'this' file. This means we will not parse methods from
236             # bb classes twice
237             if not root  in __parsed_methods__:
238                 text = '\n'.join(__body__)
239                 methodpool.insert_method( root, text, fn )
240                 funcs = data.getVar('__functions__', d) or {}
241                 if not funcs.has_key( root ):
242                     funcs[root] = text 
243                 else:
244                     funcs[root] = "%s\n%s" % (funcs[root], text)
245
246                 data.setVar('__functions__', funcs, d)
247             __body__ = []
248             __inpython__ = False
249
250             if lineno == IN_PYTHON_EOF:
251                 return
252
253 #           fall through
254
255     if s == '' or s[0] == '#': return          # skip comments and empty lines
256
257     if s[-1] == '\\':
258         __residue__.append(s[:-1])
259         return
260
261     s = "".join(__residue__) + s
262     __residue__ = []
263
264     m = __func_start_regexp__.match(s)
265     if m:
266         __infunc__ = m.group("func") or "__anonymous"
267         key = __infunc__
268         if data.getVar(key, d):
269 #           clean up old version of this piece of metadata, as its
270 #           flags could cause problems
271             data.setVarFlag(key, 'python', None, d)
272             data.setVarFlag(key, 'fakeroot', None, d)
273         if m.group("py") is not None:
274             data.setVarFlag(key, "python", "1", d)
275         else:
276             data.delVarFlag(key, "python", d)
277         if m.group("fr") is not None:
278             data.setVarFlag(key, "fakeroot", "1", d)
279         else:
280             data.delVarFlag(key, "fakeroot", d)
281         return
282
283     m = __def_regexp__.match(s)
284     if m:
285         __body__.append(s)
286         __inpython__ = True
287         return
288
289     m = __export_func_regexp__.match(s)
290     if m:
291         fns = m.group(1)
292         n = __word__.findall(fns)
293         for f in n:
294             allvars = []
295             allvars.append(f)
296             allvars.append(classes[-1] + "_" + f)
297
298             vars = [[ allvars[0], allvars[1] ]]
299             if len(classes) > 1 and classes[-2] is not None:
300                 allvars.append(classes[-2] + "_" + f)
301                 vars = []
302                 vars.append([allvars[2], allvars[1]])
303                 vars.append([allvars[0], allvars[2]])
304
305             for (var, calledvar) in vars:
306                 if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
307                     continue
308
309                 if data.getVar(var, d):
310                     data.setVarFlag(var, 'python', None, d)
311                     data.setVarFlag(var, 'func', None, d)
312
313                 for flag in [ "func", "python" ]:
314                     if data.getVarFlag(calledvar, flag, d):
315                         data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
316                 for flag in [ "dirs" ]:
317                     if data.getVarFlag(var, flag, d):
318                         data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
319
320                 if data.getVarFlag(calledvar, "python", d):
321                     data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
322                 else:
323                     data.setVar(var, "\t" + calledvar + "\n", d)
324                 data.setVarFlag(var, 'export_func', '1', d)
325
326         return
327
328     m = __addtask_regexp__.match(s)
329     if m:
330         func = m.group("func")
331         before = m.group("before")
332         after = m.group("after")
333         if func is None:
334             return
335         var = "do_" + func
336
337         data.setVarFlag(var, "task", 1, d)
338
339         if after is not None:
340 #           set up deps for function
341             data.setVarFlag(var, "deps", after.split(), d)
342         if before is not None:
343 #           set up things that depend on this func
344             data.setVarFlag(var, "postdeps", before.split(), d)
345         return
346
347     m = __addhandler_regexp__.match(s)
348     if m:
349         fns = m.group(1)
350         hs = __word__.findall(fns)
351         for h in hs:
352             data.setVarFlag(h, "handler", 1, d)
353         return
354
355     m = __inherit_regexp__.match(s)
356     if m:
357
358         files = m.group(1)
359         n = __word__.findall(files)
360         inherit(n, d)
361         return
362
363     from bb.parse import ConfHandler
364     return ConfHandler.feeder(lineno, s, fn, d)
365
366 __pkgsplit_cache__={}
367 def vars_from_file(mypkg, d):
368     if not mypkg:
369         return (None, None, None)
370     if mypkg in __pkgsplit_cache__:
371         return __pkgsplit_cache__[mypkg]
372
373     myfile = os.path.splitext(os.path.basename(mypkg))
374     parts = myfile[0].split('_')
375     __pkgsplit_cache__[mypkg] = parts
376     exp = 3 - len(parts)
377     tmplist = []
378     while exp != 0:
379         exp -= 1
380         tmplist.append(None)
381     parts.extend(tmplist)
382     return parts
383
384 def set_additional_vars(file, d, include):
385     """Deduce rest of variables, e.g. ${A} out of ${SRC_URI}"""
386
387     bb.msg.debug(2, bb.msg.domain.Parsing, "BB %s: set_additional_vars" % file)
388
389     src_uri = data.getVar('SRC_URI', d)
390     if not src_uri:
391         return
392     src_uri = data.expand(src_uri, d)
393
394     a = data.getVar('A', d)
395     if a:
396         a = data.expand(a, d).split()
397     else:
398         a = []
399
400     from bb import fetch
401     try:
402         fetch.init(src_uri.split(), d)
403     except fetch.NoMethodError:
404         pass
405     except bb.MalformedUrl,e:
406         raise ParseError("Unable to generate local paths for SRC_URI due to malformed uri: %s" % e)
407
408     a += fetch.localpaths(d)
409     del fetch
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