bitbake/method pool:
[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 debug, data, fetch, fatal, 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             debug(2, "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         debug(2, "BB " + fn + ": handle(data)")
79     else:
80         debug(2, "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.debug(1, "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             if not root  in __parsed_methods__:
228                 text = '\n'.join(__body__)
229                 methodpool.insert_method( root, text, fn )
230                 funcs = data.getVar('__functions__', d) or {}
231                 if not funcs.has_key( root ):
232                     funcs[root] = text 
233                 else:
234                     funcs[root] = "%s\n%s" % (funcs[root], text)
235
236                 data.setVar('__functions__', funcs, d)
237             __body__ = []
238             __inpython__ = False
239
240 #           fall through
241
242     if s == '' or s[0] == '#': return          # skip comments and empty lines
243
244     if s[-1] == '\\':
245         __residue__.append(s[:-1])
246         return
247
248     s = "".join(__residue__) + s
249     __residue__ = []
250
251     m = __func_start_regexp__.match(s)
252     if m:
253         __infunc__ = m.group("func") or "__anonymous"
254         key = __infunc__
255         if data.getVar(key, d):
256 #           clean up old version of this piece of metadata, as its
257 #           flags could cause problems
258             data.setVarFlag(key, 'python', None, d)
259             data.setVarFlag(key, 'fakeroot', None, d)
260         if m.group("py") is not None:
261             data.setVarFlag(key, "python", "1", d)
262         else:
263             data.delVarFlag(key, "python", d)
264         if m.group("fr") is not None:
265             data.setVarFlag(key, "fakeroot", "1", d)
266         else:
267             data.delVarFlag(key, "fakeroot", d)
268         return
269
270     m = __def_regexp__.match(s)
271     if m:
272         __body__.append(s)
273         __inpython__ = True
274         return
275
276     m = __export_func_regexp__.match(s)
277     if m:
278         fns = m.group(1)
279         n = __word__.findall(fns)
280         for f in n:
281             allvars = []
282             allvars.append(f)
283             allvars.append(classes[-1] + "_" + f)
284
285             vars = [[ allvars[0], allvars[1] ]]
286             if len(classes) > 1 and classes[-2] is not None:
287                 allvars.append(classes[-2] + "_" + f)
288                 vars = []
289                 vars.append([allvars[2], allvars[1]])
290                 vars.append([allvars[0], allvars[2]])
291
292             for (var, calledvar) in vars:
293                 if data.getVar(var, d) and not data.getVarFlag(var, 'export_func', d):
294                     continue
295
296                 if data.getVar(var, d):
297                     data.setVarFlag(var, 'python', None, d)
298                     data.setVarFlag(var, 'func', None, d)
299
300                 for flag in [ "func", "python" ]:
301                     if data.getVarFlag(calledvar, flag, d):
302                         data.setVarFlag(var, flag, data.getVarFlag(calledvar, flag, d), d)
303                 for flag in [ "dirs" ]:
304                     if data.getVarFlag(var, flag, d):
305                         data.setVarFlag(calledvar, flag, data.getVarFlag(var, flag, d), d)
306
307                 if data.getVarFlag(calledvar, "python", d):
308                     data.setVar(var, "\tbb.build.exec_func('" + calledvar + "', d)\n", d)
309                 else:
310                     data.setVar(var, "\t" + calledvar + "\n", d)
311                 data.setVarFlag(var, 'export_func', '1', d)
312
313         return
314
315     m = __addtask_regexp__.match(s)
316     if m:
317         func = m.group("func")
318         before = m.group("before")
319         after = m.group("after")
320         if func is None:
321             return
322         var = "do_" + func
323
324         data.setVarFlag(var, "task", 1, d)
325
326         if after is not None:
327 #           set up deps for function
328             data.setVarFlag(var, "deps", after.split(), d)
329         if before is not None:
330 #           set up things that depend on this func
331             data.setVarFlag(var, "postdeps", before.split(), d)
332         return
333
334     m = __addhandler_regexp__.match(s)
335     if m:
336         fns = m.group(1)
337         hs = __word__.findall(fns)
338         for h in hs:
339             data.setVarFlag(h, "handler", 1, d)
340         return
341
342     m = __inherit_regexp__.match(s)
343     if m:
344
345         files = m.group(1)
346         n = __word__.findall(files)
347         inherit(n, d)
348         return
349
350     from bb.parse import ConfHandler
351     return ConfHandler.feeder(lineno, s, fn, d)
352
353 __pkgsplit_cache__={}
354 def vars_from_file(mypkg, d):
355     if not mypkg:
356         return (None, None, None)
357     if mypkg in __pkgsplit_cache__:
358         return __pkgsplit_cache__[mypkg]
359
360     myfile = os.path.splitext(os.path.basename(mypkg))
361     parts = myfile[0].split('_')
362     __pkgsplit_cache__[mypkg] = parts
363     exp = 3 - len(parts)
364     tmplist = []
365     while exp != 0:
366         exp -= 1
367         tmplist.append(None)
368     parts.extend(tmplist)
369     return parts
370
371 def set_additional_vars(file, d, include):
372     """Deduce rest of variables, e.g. ${A} out of ${SRC_URI}"""
373
374     debug(2,"BB %s: set_additional_vars" % file)
375
376     src_uri = data.getVar('SRC_URI', d)
377     if not src_uri:
378         return
379     src_uri = data.expand(src_uri, d)
380
381     a = data.getVar('A', d)
382     if a:
383         a = data.expand(a, d).split()
384     else:
385         a = []
386
387     from bb import fetch
388     try:
389         fetch.init(src_uri.split(), d)
390     except fetch.NoMethodError:
391         pass
392     except bb.MalformedUrl,e:
393         raise ParseError("Unable to generate local paths for SRC_URI due to malformed uri: %s" % e)
394
395     a += fetch.localpaths(d)
396     del fetch
397     data.setVar('A', " ".join(a), d)
398
399
400 # Add us to the handlers list
401 from bb.parse import handlers
402 handlers.append({'supports': supports, 'handle': handle, 'init': init})
403 del handlers