summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/parse
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/parse')
-rw-r--r--bitbake/lib/bb/parse/__init__.py164
-rw-r--r--bitbake/lib/bb/parse/ast.py482
-rw-r--r--bitbake/lib/bb/parse/parse_py/BBHandler.py261
-rw-r--r--bitbake/lib/bb/parse/parse_py/ConfHandler.py189
-rw-r--r--bitbake/lib/bb/parse/parse_py/__init__.py33
5 files changed, 1129 insertions, 0 deletions
diff --git a/bitbake/lib/bb/parse/__init__.py b/bitbake/lib/bb/parse/__init__.py
new file mode 100644
index 0000000000..25effc2200
--- /dev/null
+++ b/bitbake/lib/bb/parse/__init__.py
@@ -0,0 +1,164 @@
1"""
2BitBake Parsers
3
4File parsers for the BitBake build tools.
5
6"""
7
8
9# Copyright (C) 2003, 2004 Chris Larson
10# Copyright (C) 2003, 2004 Phil Blundell
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License version 2 as
14# published by the Free Software Foundation.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License along
22# with this program; if not, write to the Free Software Foundation, Inc.,
23# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24#
25# Based on functions from the base bb module, Copyright 2003 Holger Schurig
26
27handlers = []
28
29import os
30import stat
31import logging
32import bb
33import bb.utils
34import bb.siggen
35
36logger = logging.getLogger("BitBake.Parsing")
37
38class ParseError(Exception):
39 """Exception raised when parsing fails"""
40 def __init__(self, msg, filename, lineno=0):
41 self.msg = msg
42 self.filename = filename
43 self.lineno = lineno
44 Exception.__init__(self, msg, filename, lineno)
45
46 def __str__(self):
47 if self.lineno:
48 return "ParseError at %s:%d: %s" % (self.filename, self.lineno, self.msg)
49 else:
50 return "ParseError in %s: %s" % (self.filename, self.msg)
51
52class SkipRecipe(Exception):
53 """Exception raised to skip this recipe"""
54
55class SkipPackage(SkipRecipe):
56 """Exception raised to skip this recipe (use SkipRecipe in new code)"""
57
58__mtime_cache = {}
59def cached_mtime(f):
60 if f not in __mtime_cache:
61 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
62 return __mtime_cache[f]
63
64def cached_mtime_noerror(f):
65 if f not in __mtime_cache:
66 try:
67 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
68 except OSError:
69 return 0
70 return __mtime_cache[f]
71
72def update_mtime(f):
73 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
74 return __mtime_cache[f]
75
76def update_cache(f):
77 if f in __mtime_cache:
78 logger.debug(1, "Updating mtime cache for %s" % f)
79 update_mtime(f)
80
81def mark_dependency(d, f):
82 if f.startswith('./'):
83 f = "%s/%s" % (os.getcwd(), f[2:])
84 deps = (d.getVar('__depends') or [])
85 s = (f, cached_mtime_noerror(f))
86 if s not in deps:
87 deps.append(s)
88 d.setVar('__depends', deps)
89
90def check_dependency(d, f):
91 s = (f, cached_mtime_noerror(f))
92 deps = (d.getVar('__depends') or [])
93 return s in deps
94
95def supports(fn, data):
96 """Returns true if we have a handler for this file, false otherwise"""
97 for h in handlers:
98 if h['supports'](fn, data):
99 return 1
100 return 0
101
102def handle(fn, data, include = 0):
103 """Call the handler that is appropriate for this file"""
104 for h in handlers:
105 if h['supports'](fn, data):
106 with data.inchistory.include(fn):
107 return h['handle'](fn, data, include)
108 raise ParseError("not a BitBake file", fn)
109
110def init(fn, data):
111 for h in handlers:
112 if h['supports'](fn):
113 return h['init'](data)
114
115def init_parser(d):
116 bb.parse.siggen = bb.siggen.init(d)
117
118def resolve_file(fn, d):
119 if not os.path.isabs(fn):
120 bbpath = d.getVar("BBPATH", True)
121 newfn, attempts = bb.utils.which(bbpath, fn, history=True)
122 for af in attempts:
123 mark_dependency(d, af)
124 if not newfn:
125 raise IOError("file %s not found in %s" % (fn, bbpath))
126 fn = newfn
127
128 mark_dependency(d, fn)
129 if not os.path.isfile(fn):
130 raise IOError("file %s not found" % fn)
131
132 return fn
133
134# Used by OpenEmbedded metadata
135__pkgsplit_cache__={}
136def vars_from_file(mypkg, d):
137 if not mypkg or not mypkg.endswith((".bb", ".bbappend")):
138 return (None, None, None)
139 if mypkg in __pkgsplit_cache__:
140 return __pkgsplit_cache__[mypkg]
141
142 myfile = os.path.splitext(os.path.basename(mypkg))
143 parts = myfile[0].split('_')
144 __pkgsplit_cache__[mypkg] = parts
145 if len(parts) > 3:
146 raise ParseError("Unable to generate default variables from filename (too many underscores)", mypkg)
147 exp = 3 - len(parts)
148 tmplist = []
149 while exp != 0:
150 exp -= 1
151 tmplist.append(None)
152 parts.extend(tmplist)
153 return parts
154
155def get_file_depends(d):
156 '''Return the dependent files'''
157 dep_files = []
158 depends = d.getVar('__base_depends', True) or []
159 depends = depends + (d.getVar('__depends', True) or [])
160 for (fn, _) in depends:
161 dep_files.append(os.path.abspath(fn))
162 return " ".join(dep_files)
163
164from bb.parse.parse_py import __version__, ConfHandler, BBHandler
diff --git a/bitbake/lib/bb/parse/ast.py b/bitbake/lib/bb/parse/ast.py
new file mode 100644
index 0000000000..4b10ee7013
--- /dev/null
+++ b/bitbake/lib/bb/parse/ast.py
@@ -0,0 +1,482 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4 AbstractSyntaxTree classes for the Bitbake language
5"""
6
7# Copyright (C) 2003, 2004 Chris Larson
8# Copyright (C) 2003, 2004 Phil Blundell
9# Copyright (C) 2009 Holger Hans Peter Freyther
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License version 2 as
13# published by the Free Software Foundation.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License along
21# with this program; if not, write to the Free Software Foundation, Inc.,
22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
24from __future__ import absolute_import
25from future_builtins import filter
26import re
27import string
28import logging
29import bb
30import itertools
31from bb import methodpool
32from bb.parse import logger
33
34_bbversions_re = re.compile(r"\[(?P<from>[0-9]+)-(?P<to>[0-9]+)\]")
35
36class StatementGroup(list):
37 def eval(self, data):
38 for statement in self:
39 statement.eval(data)
40
41class AstNode(object):
42 def __init__(self, filename, lineno):
43 self.filename = filename
44 self.lineno = lineno
45
46class IncludeNode(AstNode):
47 def __init__(self, filename, lineno, what_file, force):
48 AstNode.__init__(self, filename, lineno)
49 self.what_file = what_file
50 self.force = force
51
52 def eval(self, data):
53 """
54 Include the file and evaluate the statements
55 """
56 s = data.expand(self.what_file)
57 logger.debug(2, "CONF %s:%s: including %s", self.filename, self.lineno, s)
58
59 # TODO: Cache those includes... maybe not here though
60 if self.force:
61 bb.parse.ConfHandler.include(self.filename, s, self.lineno, data, "include required")
62 else:
63 bb.parse.ConfHandler.include(self.filename, s, self.lineno, data, False)
64
65class ExportNode(AstNode):
66 def __init__(self, filename, lineno, var):
67 AstNode.__init__(self, filename, lineno)
68 self.var = var
69
70 def eval(self, data):
71 data.setVarFlag(self.var, "export", 1, op = 'exported')
72
73class DataNode(AstNode):
74 """
75 Various data related updates. For the sake of sanity
76 we have one class doing all this. This means that all
77 this need to be re-evaluated... we might be able to do
78 that faster with multiple classes.
79 """
80 def __init__(self, filename, lineno, groupd):
81 AstNode.__init__(self, filename, lineno)
82 self.groupd = groupd
83
84 def getFunc(self, key, data):
85 if 'flag' in self.groupd and self.groupd['flag'] != None:
86 return data.getVarFlag(key, self.groupd['flag'], noweakdefault=True)
87 else:
88 return data.getVar(key, noweakdefault=True)
89
90 def eval(self, data):
91 groupd = self.groupd
92 key = groupd["var"]
93 loginfo = {
94 'variable': key,
95 'file': self.filename,
96 'line': self.lineno,
97 }
98 if "exp" in groupd and groupd["exp"] != None:
99 data.setVarFlag(key, "export", 1, op = 'exported', **loginfo)
100
101 op = "set"
102 if "ques" in groupd and groupd["ques"] != None:
103 val = self.getFunc(key, data)
104 op = "set?"
105 if val == None:
106 val = groupd["value"]
107 elif "colon" in groupd and groupd["colon"] != None:
108 e = data.createCopy()
109 bb.data.update_data(e)
110 op = "immediate"
111 val = e.expand(groupd["value"], key + "[:=]")
112 elif "append" in groupd and groupd["append"] != None:
113 op = "append"
114 val = "%s %s" % ((self.getFunc(key, data) or ""), groupd["value"])
115 elif "prepend" in groupd and groupd["prepend"] != None:
116 op = "prepend"
117 val = "%s %s" % (groupd["value"], (self.getFunc(key, data) or ""))
118 elif "postdot" in groupd and groupd["postdot"] != None:
119 op = "postdot"
120 val = "%s%s" % ((self.getFunc(key, data) or ""), groupd["value"])
121 elif "predot" in groupd and groupd["predot"] != None:
122 op = "predot"
123 val = "%s%s" % (groupd["value"], (self.getFunc(key, data) or ""))
124 else:
125 val = groupd["value"]
126
127 flag = None
128 if 'flag' in groupd and groupd['flag'] != None:
129 flag = groupd['flag']
130 elif groupd["lazyques"]:
131 flag = "defaultval"
132
133 loginfo['op'] = op
134 loginfo['detail'] = groupd["value"]
135
136 if flag:
137 data.setVarFlag(key, flag, val, **loginfo)
138 else:
139 data.setVar(key, val, **loginfo)
140
141class MethodNode(AstNode):
142 tr_tbl = string.maketrans('/.+-@%', '______')
143
144 def __init__(self, filename, lineno, func_name, body):
145 AstNode.__init__(self, filename, lineno)
146 self.func_name = func_name
147 self.body = body
148
149 def eval(self, data):
150 text = '\n'.join(self.body)
151 if self.func_name == "__anonymous":
152 funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(MethodNode.tr_tbl)))
153 text = "def %s(d):\n" % (funcname) + text
154 bb.methodpool.insert_method(funcname, text, self.filename)
155 anonfuncs = data.getVar('__BBANONFUNCS') or []
156 anonfuncs.append(funcname)
157 data.setVar('__BBANONFUNCS', anonfuncs)
158 data.setVar(funcname, text)
159 else:
160 data.setVarFlag(self.func_name, "func", 1)
161 data.setVar(self.func_name, text)
162
163class PythonMethodNode(AstNode):
164 def __init__(self, filename, lineno, function, modulename, body):
165 AstNode.__init__(self, filename, lineno)
166 self.function = function
167 self.modulename = modulename
168 self.body = body
169
170 def eval(self, data):
171 # Note we will add root to parsedmethods after having parse
172 # 'this' file. This means we will not parse methods from
173 # bb classes twice
174 text = '\n'.join(self.body)
175 bb.methodpool.insert_method(self.modulename, text, self.filename)
176 data.setVarFlag(self.function, "func", 1)
177 data.setVarFlag(self.function, "python", 1)
178 data.setVar(self.function, text)
179
180class MethodFlagsNode(AstNode):
181 def __init__(self, filename, lineno, key, m):
182 AstNode.__init__(self, filename, lineno)
183 self.key = key
184 self.m = m
185
186 def eval(self, data):
187 if data.getVar(self.key):
188 # clean up old version of this piece of metadata, as its
189 # flags could cause problems
190 data.setVarFlag(self.key, 'python', None)
191 data.setVarFlag(self.key, 'fakeroot', None)
192 if self.m.group("py") is not None:
193 data.setVarFlag(self.key, "python", "1")
194 else:
195 data.delVarFlag(self.key, "python")
196 if self.m.group("fr") is not None:
197 data.setVarFlag(self.key, "fakeroot", "1")
198 else:
199 data.delVarFlag(self.key, "fakeroot")
200
201class ExportFuncsNode(AstNode):
202 def __init__(self, filename, lineno, fns, classname):
203 AstNode.__init__(self, filename, lineno)
204 self.n = fns.split()
205 self.classname = classname
206
207 def eval(self, data):
208
209 for func in self.n:
210 calledfunc = self.classname + "_" + func
211
212 if data.getVar(func) and not data.getVarFlag(func, 'export_func'):
213 continue
214
215 if data.getVar(func):
216 data.setVarFlag(func, 'python', None)
217 data.setVarFlag(func, 'func', None)
218
219 for flag in [ "func", "python" ]:
220 if data.getVarFlag(calledfunc, flag):
221 data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag))
222 for flag in [ "dirs" ]:
223 if data.getVarFlag(func, flag):
224 data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag))
225
226 if data.getVarFlag(calledfunc, "python"):
227 data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n")
228 else:
229 if "-" in self.classname:
230 bb.fatal("The classname %s contains a dash character and is calling an sh function %s using EXPORT_FUNCTIONS. Since a dash is illegal in sh function names, this cannot work, please rename the class or don't use EXPORT_FUNCTIONS." % (self.classname, calledfunc))
231 data.setVar(func, " " + calledfunc + "\n")
232 data.setVarFlag(func, 'export_func', '1')
233
234class AddTaskNode(AstNode):
235 def __init__(self, filename, lineno, func, before, after):
236 AstNode.__init__(self, filename, lineno)
237 self.func = func
238 self.before = before
239 self.after = after
240
241 def eval(self, data):
242 bb.build.addtask(self.func, self.before, self.after, data)
243
244class DelTaskNode(AstNode):
245 def __init__(self, filename, lineno, func):
246 AstNode.__init__(self, filename, lineno)
247 self.func = func
248
249 def eval(self, data):
250 bb.build.deltask(self.func, data)
251
252class BBHandlerNode(AstNode):
253 def __init__(self, filename, lineno, fns):
254 AstNode.__init__(self, filename, lineno)
255 self.hs = fns.split()
256
257 def eval(self, data):
258 bbhands = data.getVar('__BBHANDLERS') or []
259 for h in self.hs:
260 bbhands.append(h)
261 data.setVarFlag(h, "handler", 1)
262 data.setVar('__BBHANDLERS', bbhands)
263
264class InheritNode(AstNode):
265 def __init__(self, filename, lineno, classes):
266 AstNode.__init__(self, filename, lineno)
267 self.classes = classes
268
269 def eval(self, data):
270 bb.parse.BBHandler.inherit(self.classes, self.filename, self.lineno, data)
271
272def handleInclude(statements, filename, lineno, m, force):
273 statements.append(IncludeNode(filename, lineno, m.group(1), force))
274
275def handleExport(statements, filename, lineno, m):
276 statements.append(ExportNode(filename, lineno, m.group(1)))
277
278def handleData(statements, filename, lineno, groupd):
279 statements.append(DataNode(filename, lineno, groupd))
280
281def handleMethod(statements, filename, lineno, func_name, body):
282 statements.append(MethodNode(filename, lineno, func_name, body))
283
284def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
285 statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
286
287def handleMethodFlags(statements, filename, lineno, key, m):
288 statements.append(MethodFlagsNode(filename, lineno, key, m))
289
290def handleExportFuncs(statements, filename, lineno, m, classname):
291 statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
292
293def handleAddTask(statements, filename, lineno, m):
294 func = m.group("func")
295 before = m.group("before")
296 after = m.group("after")
297 if func is None:
298 return
299
300 statements.append(AddTaskNode(filename, lineno, func, before, after))
301
302def handleDelTask(statements, filename, lineno, m):
303 func = m.group("func")
304 if func is None:
305 return
306
307 statements.append(DelTaskNode(filename, lineno, func))
308
309def handleBBHandlers(statements, filename, lineno, m):
310 statements.append(BBHandlerNode(filename, lineno, m.group(1)))
311
312def handleInherit(statements, filename, lineno, m):
313 classes = m.group(1)
314 statements.append(InheritNode(filename, lineno, classes))
315
316def finalize(fn, d, variant = None):
317 all_handlers = {}
318 for var in d.getVar('__BBHANDLERS') or []:
319 # try to add the handler
320 bb.event.register(var, d.getVar(var), (d.getVarFlag(var, "eventmask", True) or "").split())
321
322 bb.event.fire(bb.event.RecipePreFinalise(fn), d)
323
324 bb.data.expandKeys(d)
325 bb.data.update_data(d)
326 code = []
327 for funcname in d.getVar("__BBANONFUNCS") or []:
328 code.append("%s(d)" % funcname)
329 bb.utils.better_exec("\n".join(code), {"d": d})
330 bb.data.update_data(d)
331
332 tasklist = d.getVar('__BBTASKS') or []
333 deltasklist = d.getVar('__BBDELTASKS') or []
334 bb.build.add_tasks(tasklist, deltasklist, d)
335
336 bb.parse.siggen.finalise(fn, d, variant)
337
338 d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
339
340 bb.event.fire(bb.event.RecipeParsed(fn), d)
341
342def _create_variants(datastores, names, function, onlyfinalise):
343 def create_variant(name, orig_d, arg = None):
344 if onlyfinalise and name not in onlyfinalise:
345 return
346 new_d = bb.data.createCopy(orig_d)
347 function(arg or name, new_d)
348 datastores[name] = new_d
349
350 for variant, variant_d in datastores.items():
351 for name in names:
352 if not variant:
353 # Based on main recipe
354 create_variant(name, variant_d)
355 else:
356 create_variant("%s-%s" % (variant, name), variant_d, name)
357
358def _expand_versions(versions):
359 def expand_one(version, start, end):
360 for i in xrange(start, end + 1):
361 ver = _bbversions_re.sub(str(i), version, 1)
362 yield ver
363
364 versions = iter(versions)
365 while True:
366 try:
367 version = next(versions)
368 except StopIteration:
369 break
370
371 range_ver = _bbversions_re.search(version)
372 if not range_ver:
373 yield version
374 else:
375 newversions = expand_one(version, int(range_ver.group("from")),
376 int(range_ver.group("to")))
377 versions = itertools.chain(newversions, versions)
378
379def multi_finalize(fn, d):
380 appends = (d.getVar("__BBAPPEND", True) or "").split()
381 for append in appends:
382 logger.debug(1, "Appending .bbappend file %s to %s", append, fn)
383 bb.parse.BBHandler.handle(append, d, True)
384
385 onlyfinalise = d.getVar("__ONLYFINALISE", False)
386
387 safe_d = d
388 d = bb.data.createCopy(safe_d)
389 try:
390 finalize(fn, d)
391 except bb.parse.SkipRecipe as e:
392 d.setVar("__SKIPPED", e.args[0])
393 datastores = {"": safe_d}
394
395 versions = (d.getVar("BBVERSIONS", True) or "").split()
396 if versions:
397 pv = orig_pv = d.getVar("PV", True)
398 baseversions = {}
399
400 def verfunc(ver, d, pv_d = None):
401 if pv_d is None:
402 pv_d = d
403
404 overrides = d.getVar("OVERRIDES", True).split(":")
405 pv_d.setVar("PV", ver)
406 overrides.append(ver)
407 bpv = baseversions.get(ver) or orig_pv
408 pv_d.setVar("BPV", bpv)
409 overrides.append(bpv)
410 d.setVar("OVERRIDES", ":".join(overrides))
411
412 versions = list(_expand_versions(versions))
413 for pos, version in enumerate(list(versions)):
414 try:
415 pv, bpv = version.split(":", 2)
416 except ValueError:
417 pass
418 else:
419 versions[pos] = pv
420 baseversions[pv] = bpv
421
422 if pv in versions and not baseversions.get(pv):
423 versions.remove(pv)
424 else:
425 pv = versions.pop()
426
427 # This is necessary because our existing main datastore
428 # has already been finalized with the old PV, we need one
429 # that's been finalized with the new PV.
430 d = bb.data.createCopy(safe_d)
431 verfunc(pv, d, safe_d)
432 try:
433 finalize(fn, d)
434 except bb.parse.SkipRecipe as e:
435 d.setVar("__SKIPPED", e.args[0])
436
437 _create_variants(datastores, versions, verfunc, onlyfinalise)
438
439 extended = d.getVar("BBCLASSEXTEND", True) or ""
440 if extended:
441 # the following is to support bbextends with arguments, for e.g. multilib
442 # an example is as follows:
443 # BBCLASSEXTEND = "multilib:lib32"
444 # it will create foo-lib32, inheriting multilib.bbclass and set
445 # BBEXTENDCURR to "multilib" and BBEXTENDVARIANT to "lib32"
446 extendedmap = {}
447 variantmap = {}
448
449 for ext in extended.split():
450 eext = ext.split(':', 2)
451 if len(eext) > 1:
452 extendedmap[ext] = eext[0]
453 variantmap[ext] = eext[1]
454 else:
455 extendedmap[ext] = ext
456
457 pn = d.getVar("PN", True)
458 def extendfunc(name, d):
459 if name != extendedmap[name]:
460 d.setVar("BBEXTENDCURR", extendedmap[name])
461 d.setVar("BBEXTENDVARIANT", variantmap[name])
462 else:
463 d.setVar("PN", "%s-%s" % (pn, name))
464 bb.parse.BBHandler.inherit(extendedmap[name], fn, 0, d)
465
466 safe_d.setVar("BBCLASSEXTEND", extended)
467 _create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
468
469 for variant, variant_d in datastores.iteritems():
470 if variant:
471 try:
472 if not onlyfinalise or variant in onlyfinalise:
473 finalize(fn, variant_d, variant)
474 except bb.parse.SkipRecipe as e:
475 variant_d.setVar("__SKIPPED", e.args[0])
476
477 if len(datastores) > 1:
478 variants = filter(None, datastores.iterkeys())
479 safe_d.setVar("__VARIANTS", " ".join(variants))
480
481 datastores[""] = d
482 return datastores
diff --git a/bitbake/lib/bb/parse/parse_py/BBHandler.py b/bitbake/lib/bb/parse/parse_py/BBHandler.py
new file mode 100644
index 0000000000..9633340d1b
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/BBHandler.py
@@ -0,0 +1,261 @@
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
28from __future__ import absolute_import
29import re, bb, os
30import logging
31import bb.build, bb.utils
32from bb import data
33
34from . import ConfHandler
35from .. import resolve_file, ast, logger
36from .ConfHandler import include, init
37
38# For compatibility
39bb.deprecate_import(__name__, "bb.parse", ["vars_from_file"])
40
41__func_start_regexp__ = re.compile( r"(((?P<py>python)|(?P<fr>fakeroot))\s*)*(?P<func>[\w\.\-\+\{\}\$]+)?\s*\(\s*\)\s*{$" )
42__inherit_regexp__ = re.compile( r"inherit\s+(.+)" )
43__export_func_regexp__ = re.compile( r"EXPORT_FUNCTIONS\s+(.+)" )
44__addtask_regexp__ = re.compile("addtask\s+(?P<func>\w+)\s*((before\s*(?P<before>((.*(?=after))|(.*))))|(after\s*(?P<after>((.*(?=before))|(.*)))))*")
45__deltask_regexp__ = re.compile("deltask\s+(?P<func>\w+)")
46__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
47__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
48__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
49
50
51__infunc__ = ""
52__inpython__ = False
53__body__ = []
54__classname__ = ""
55
56cached_statements = {}
57
58# We need to indicate EOF to the feeder. This code is so messy that
59# factoring it out to a close_parse_file method is out of question.
60# We will use the IN_PYTHON_EOF as an indicator to just close the method
61#
62# The two parts using it are tightly integrated anyway
63IN_PYTHON_EOF = -9999999999999
64
65
66
67def supports(fn, d):
68 """Return True if fn has a supported extension"""
69 return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
70
71def inherit(files, fn, lineno, d):
72 __inherit_cache = d.getVar('__inherit_cache') or []
73 files = d.expand(files).split()
74 for file in files:
75 if not os.path.isabs(file) and not file.endswith(".bbclass"):
76 file = os.path.join('classes', '%s.bbclass' % file)
77
78 if not os.path.isabs(file):
79 dname = os.path.dirname(fn)
80 bbpath = "%s:%s" % (dname, d.getVar("BBPATH", True))
81 abs_fn, attempts = bb.utils.which(bbpath, file, history=True)
82 for af in attempts:
83 if af != abs_fn:
84 bb.parse.mark_dependency(d, af)
85 if abs_fn:
86 file = abs_fn
87
88 if not file in __inherit_cache:
89 logger.debug(1, "Inheriting %s (from %s:%d)" % (file, fn, lineno))
90 __inherit_cache.append( file )
91 d.setVar('__inherit_cache', __inherit_cache)
92 include(fn, file, lineno, d, "inherit")
93 __inherit_cache = d.getVar('__inherit_cache') or []
94
95def get_statements(filename, absolute_filename, base_name):
96 global cached_statements
97
98 try:
99 return cached_statements[absolute_filename]
100 except KeyError:
101 file = open(absolute_filename, 'r')
102 statements = ast.StatementGroup()
103
104 lineno = 0
105 while True:
106 lineno = lineno + 1
107 s = file.readline()
108 if not s: break
109 s = s.rstrip()
110 feeder(lineno, s, filename, base_name, statements)
111 file.close()
112 if __inpython__:
113 # add a blank line to close out any python definition
114 feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
115
116 if filename.endswith(".bbclass") or filename.endswith(".inc"):
117 cached_statements[absolute_filename] = statements
118 return statements
119
120def handle(fn, d, include):
121 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__, __classname__
122 __body__ = []
123 __infunc__ = ""
124 __classname__ = ""
125 __residue__ = []
126
127 base_name = os.path.basename(fn)
128 (root, ext) = os.path.splitext(base_name)
129 init(d)
130
131 if ext == ".bbclass":
132 __classname__ = root
133 __inherit_cache = d.getVar('__inherit_cache') or []
134 if not fn in __inherit_cache:
135 __inherit_cache.append(fn)
136 d.setVar('__inherit_cache', __inherit_cache)
137
138 if include != 0:
139 oldfile = d.getVar('FILE')
140 else:
141 oldfile = None
142
143 abs_fn = resolve_file(fn, d)
144
145 if include:
146 bb.parse.mark_dependency(d, abs_fn)
147
148 # actual loading
149 statements = get_statements(fn, abs_fn, base_name)
150
151 # DONE WITH PARSING... time to evaluate
152 if ext != ".bbclass":
153 d.setVar('FILE', abs_fn)
154
155 try:
156 statements.eval(d)
157 except bb.parse.SkipRecipe:
158 bb.data.setVar("__SKIPPED", True, d)
159 if include == 0:
160 return { "" : d }
161
162 if ext != ".bbclass" and include == 0:
163 return ast.multi_finalize(fn, d)
164
165 if oldfile:
166 d.setVar("FILE", oldfile)
167
168 return d
169
170def feeder(lineno, s, fn, root, statements):
171 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__
172 if __infunc__:
173 if s == '}':
174 __body__.append('')
175 ast.handleMethod(statements, fn, lineno, __infunc__, __body__)
176 __infunc__ = ""
177 __body__ = []
178 else:
179 __body__.append(s)
180 return
181
182 if __inpython__:
183 m = __python_func_regexp__.match(s)
184 if m and lineno != IN_PYTHON_EOF:
185 __body__.append(s)
186 return
187 else:
188 ast.handlePythonMethod(statements, fn, lineno, __inpython__,
189 root, __body__)
190 __body__ = []
191 __inpython__ = False
192
193 if lineno == IN_PYTHON_EOF:
194 return
195
196 if s and s[0] == '#':
197 if len(__residue__) != 0 and __residue__[0][0] != "#":
198 bb.fatal("There is a comment on line %s of file %s (%s) which is in the middle of a multiline expression.\nBitbake used to ignore these but no longer does so, please fix your metadata as errors are likely as a result of this change." % (lineno, fn, s))
199
200 if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"):
201 bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
202
203 if s and s[-1] == '\\':
204 __residue__.append(s[:-1])
205 return
206
207 s = "".join(__residue__) + s
208 __residue__ = []
209
210 # Skip empty lines
211 if s == '':
212 return
213
214 # Skip comments
215 if s[0] == '#':
216 return
217
218 m = __func_start_regexp__.match(s)
219 if m:
220 __infunc__ = m.group("func") or "__anonymous"
221 ast.handleMethodFlags(statements, fn, lineno, __infunc__, m)
222 return
223
224 m = __def_regexp__.match(s)
225 if m:
226 __body__.append(s)
227 __inpython__ = m.group(1)
228
229 return
230
231 m = __export_func_regexp__.match(s)
232 if m:
233 ast.handleExportFuncs(statements, fn, lineno, m, __classname__)
234 return
235
236 m = __addtask_regexp__.match(s)
237 if m:
238 ast.handleAddTask(statements, fn, lineno, m)
239 return
240
241 m = __deltask_regexp__.match(s)
242 if m:
243 ast.handleDelTask(statements, fn, lineno, m)
244 return
245
246 m = __addhandler_regexp__.match(s)
247 if m:
248 ast.handleBBHandlers(statements, fn, lineno, m)
249 return
250
251 m = __inherit_regexp__.match(s)
252 if m:
253 ast.handleInherit(statements, fn, lineno, m)
254 return
255
256 return ConfHandler.feeder(lineno, s, fn, statements)
257
258# Add us to the handlers list
259from .. import handlers
260handlers.append({'supports': supports, 'handle': handle, 'init': init})
261del handlers
diff --git a/bitbake/lib/bb/parse/parse_py/ConfHandler.py b/bitbake/lib/bb/parse/parse_py/ConfHandler.py
new file mode 100644
index 0000000000..978ebe4608
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/ConfHandler.py
@@ -0,0 +1,189 @@
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 configuration data files
6
7 Reads a .conf file and obtains its metadata
8
9"""
10
11# Copyright (C) 2003, 2004 Chris Larson
12# Copyright (C) 2003, 2004 Phil Blundell
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License version 2 as
16# published by the Free Software Foundation.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License along
24# with this program; if not, write to the Free Software Foundation, Inc.,
25# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26
27import re, os
28import logging
29import bb.utils
30from bb.parse import ParseError, resolve_file, ast, logger
31
32__config_regexp__ = re.compile( r"""
33 ^
34 (?P<exp>export\s*)?
35 (?P<var>[a-zA-Z0-9\-~_+.${}/]+?)
36 (\[(?P<flag>[a-zA-Z0-9\-_+.]+)\])?
37
38 \s* (
39 (?P<colon>:=) |
40 (?P<lazyques>\?\?=) |
41 (?P<ques>\?=) |
42 (?P<append>\+=) |
43 (?P<prepend>=\+) |
44 (?P<predot>=\.) |
45 (?P<postdot>\.=) |
46 =
47 ) \s*
48
49 (?!'[^']*'[^']*'$)
50 (?!\"[^\"]*\"[^\"]*\"$)
51 (?P<apo>['\"])
52 (?P<value>.*)
53 (?P=apo)
54 $
55 """, re.X)
56__include_regexp__ = re.compile( r"include\s+(.+)" )
57__require_regexp__ = re.compile( r"require\s+(.+)" )
58__export_regexp__ = re.compile( r"export\s+([a-zA-Z0-9\-_+.${}/]+)$" )
59
60def init(data):
61 topdir = data.getVar('TOPDIR')
62 if not topdir:
63 data.setVar('TOPDIR', os.getcwd())
64
65
66def supports(fn, d):
67 return fn[-5:] == ".conf"
68
69def include(oldfn, fn, lineno, data, error_out):
70 """
71 error_out: A string indicating the verb (e.g. "include", "inherit") to be
72 used in a ParseError that will be raised if the file to be included could
73 not be included. Specify False to avoid raising an error in this case.
74 """
75 if oldfn == fn: # prevent infinite recursion
76 return None
77
78 import bb
79 fn = data.expand(fn)
80 oldfn = data.expand(oldfn)
81
82 if not os.path.isabs(fn):
83 dname = os.path.dirname(oldfn)
84 bbpath = "%s:%s" % (dname, data.getVar("BBPATH", True))
85 abs_fn, attempts = bb.utils.which(bbpath, fn, history=True)
86 if abs_fn and bb.parse.check_dependency(data, abs_fn):
87 bb.warn("Duplicate inclusion for %s in %s" % (abs_fn, data.getVar('FILE', True)))
88 for af in attempts:
89 bb.parse.mark_dependency(data, af)
90 if abs_fn:
91 fn = abs_fn
92 elif bb.parse.check_dependency(data, fn):
93 bb.warn("Duplicate inclusion for %s in %s" % (fn, data.getVar('FILE', True)))
94
95 from bb.parse import handle
96 try:
97 ret = handle(fn, data, True)
98 except (IOError, OSError):
99 if error_out:
100 raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
101 logger.debug(2, "CONF file '%s' not found", fn)
102 bb.parse.mark_dependency(data, fn)
103
104# We have an issue where a UI might want to enforce particular settings such as
105# an empty DISTRO variable. If configuration files do something like assigning
106# a weak default, it turns out to be very difficult to filter out these changes,
107# particularly when the weak default might appear half way though parsing a chain
108# of configuration files. We therefore let the UIs hook into configuration file
109# parsing. This turns out to be a hard problem to solve any other way.
110confFilters = []
111
112def handle(fn, data, include):
113 init(data)
114
115 if include == 0:
116 oldfile = None
117 else:
118 oldfile = data.getVar('FILE')
119
120 abs_fn = resolve_file(fn, data)
121 f = open(abs_fn, 'r')
122
123 if include:
124 bb.parse.mark_dependency(data, abs_fn)
125
126 statements = ast.StatementGroup()
127 lineno = 0
128 while True:
129 lineno = lineno + 1
130 s = f.readline()
131 if not s:
132 break
133 w = s.strip()
134 # skip empty lines
135 if not w:
136 continue
137 s = s.rstrip()
138 while s[-1] == '\\':
139 s2 = f.readline().strip()
140 lineno = lineno + 1
141 if (not s2 or s2 and s2[0] != "#") and s[0] == "#" :
142 bb.fatal("There is a confusing multiline, partially commented expression on line %s of file %s (%s).\nPlease clarify whether this is all a comment or should be parsed." % (lineno, fn, s))
143 s = s[:-1] + s2
144 # skip comments
145 if s[0] == '#':
146 continue
147 feeder(lineno, s, abs_fn, statements)
148
149 # DONE WITH PARSING... time to evaluate
150 data.setVar('FILE', abs_fn)
151 statements.eval(data)
152 if oldfile:
153 data.setVar('FILE', oldfile)
154
155 f.close()
156
157 for f in confFilters:
158 f(fn, data)
159
160 return data
161
162def feeder(lineno, s, fn, statements):
163 m = __config_regexp__.match(s)
164 if m:
165 groupd = m.groupdict()
166 ast.handleData(statements, fn, lineno, groupd)
167 return
168
169 m = __include_regexp__.match(s)
170 if m:
171 ast.handleInclude(statements, fn, lineno, m, False)
172 return
173
174 m = __require_regexp__.match(s)
175 if m:
176 ast.handleInclude(statements, fn, lineno, m, True)
177 return
178
179 m = __export_regexp__.match(s)
180 if m:
181 ast.handleExport(statements, fn, lineno, m)
182 return
183
184 raise ParseError("unparsed line: '%s'" % s, fn, lineno);
185
186# Add us to the handlers list
187from bb.parse import handlers
188handlers.append({'supports': supports, 'handle': handle, 'init': init})
189del handlers
diff --git a/bitbake/lib/bb/parse/parse_py/__init__.py b/bitbake/lib/bb/parse/parse_py/__init__.py
new file mode 100644
index 0000000000..3e658d0de9
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/__init__.py
@@ -0,0 +1,33 @@
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"""
5BitBake Parsers
6
7File parsers for the BitBake build tools.
8
9"""
10
11# Copyright (C) 2003, 2004 Chris Larson
12# Copyright (C) 2003, 2004 Phil Blundell
13#
14# This program is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License version 2 as
16# published by the Free Software Foundation.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License along
24# with this program; if not, write to the Free Software Foundation, Inc.,
25# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26#
27# Based on functions from the base bb module, Copyright 2003 Holger Schurig
28
29from __future__ import absolute_import
30from . import ConfHandler
31from . import BBHandler
32
33__version__ = '1.0'