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