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