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