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