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__.py146
-rw-r--r--bitbake/lib/bb/parse/ast.py482
-rw-r--r--bitbake/lib/bb/parse/parse_py/BBHandler.py258
-rw-r--r--bitbake/lib/bb/parse/parse_py/ConfHandler.py182
-rw-r--r--bitbake/lib/bb/parse/parse_py/__init__.py33
5 files changed, 1101 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..c973f6fdbf
--- /dev/null
+++ b/bitbake/lib/bb/parse/__init__.py
@@ -0,0 +1,146 @@
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 SkipPackage(Exception):
53 """Exception raised to skip this package"""
54
55__mtime_cache = {}
56def cached_mtime(f):
57 if f not in __mtime_cache:
58 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
59 return __mtime_cache[f]
60
61def cached_mtime_noerror(f):
62 if f not in __mtime_cache:
63 try:
64 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
65 except OSError:
66 return 0
67 return __mtime_cache[f]
68
69def update_mtime(f):
70 __mtime_cache[f] = os.stat(f)[stat.ST_MTIME]
71 return __mtime_cache[f]
72
73def mark_dependency(d, f):
74 if f.startswith('./'):
75 f = "%s/%s" % (os.getcwd(), f[2:])
76 deps = (d.getVar('__depends') or []) + [(f, cached_mtime(f))]
77 d.setVar('__depends', deps)
78
79def supports(fn, data):
80 """Returns true if we have a handler for this file, false otherwise"""
81 for h in handlers:
82 if h['supports'](fn, data):
83 return 1
84 return 0
85
86def handle(fn, data, include = 0):
87 """Call the handler that is appropriate for this file"""
88 for h in handlers:
89 if h['supports'](fn, data):
90 with data.inchistory.include(fn):
91 return h['handle'](fn, data, include)
92 raise ParseError("not a BitBake file", fn)
93
94def init(fn, data):
95 for h in handlers:
96 if h['supports'](fn):
97 return h['init'](data)
98
99def init_parser(d):
100 bb.parse.siggen = bb.siggen.init(d)
101
102def resolve_file(fn, d):
103 if not os.path.isabs(fn):
104 bbpath = d.getVar("BBPATH", True)
105 newfn = bb.utils.which(bbpath, fn)
106 if not newfn:
107 raise IOError("file %s not found in %s" % (fn, bbpath))
108 fn = newfn
109
110 if not os.path.isfile(fn):
111 raise IOError("file %s not found" % fn)
112
113 logger.debug(2, "LOAD %s", fn)
114 return fn
115
116# Used by OpenEmbedded metadata
117__pkgsplit_cache__={}
118def vars_from_file(mypkg, d):
119 if not mypkg:
120 return (None, None, None)
121 if mypkg in __pkgsplit_cache__:
122 return __pkgsplit_cache__[mypkg]
123
124 myfile = os.path.splitext(os.path.basename(mypkg))
125 parts = myfile[0].split('_')
126 __pkgsplit_cache__[mypkg] = parts
127 if len(parts) > 3:
128 raise ParseError("Unable to generate default variables from filename (too many underscores)", mypkg)
129 exp = 3 - len(parts)
130 tmplist = []
131 while exp != 0:
132 exp -= 1
133 tmplist.append(None)
134 parts.extend(tmplist)
135 return parts
136
137def get_file_depends(d):
138 '''Return the dependent files'''
139 dep_files = []
140 depends = d.getVar('__base_depends', True) or []
141 depends = depends + (d.getVar('__depends', True) or [])
142 for (fn, _) in depends:
143 dep_files.append(os.path.abspath(fn))
144 return " ".join(dep_files)
145
146from 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..d4b8b09543
--- /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 def __init__(self, filename, lineno, func_name, body):
143 AstNode.__init__(self, filename, lineno)
144 self.func_name = func_name
145 self.body = body
146
147 def eval(self, data):
148 text = '\n'.join(self.body)
149 if self.func_name == "__anonymous":
150 funcname = ("__anon_%s_%s" % (self.lineno, self.filename.translate(string.maketrans('/.+-@', '_____'))))
151 text = "def %s(d):\n" % (funcname) + text
152 bb.methodpool.insert_method(funcname, text, self.filename)
153 anonfuncs = data.getVar('__BBANONFUNCS') or []
154 anonfuncs.append(funcname)
155 data.setVar('__BBANONFUNCS', anonfuncs)
156 data.setVar(funcname, text)
157 else:
158 data.setVarFlag(self.func_name, "func", 1)
159 data.setVar(self.func_name, text)
160
161class PythonMethodNode(AstNode):
162 def __init__(self, filename, lineno, function, modulename, body):
163 AstNode.__init__(self, filename, lineno)
164 self.function = function
165 self.modulename = modulename
166 self.body = body
167
168 def eval(self, data):
169 # Note we will add root to parsedmethods after having parse
170 # 'this' file. This means we will not parse methods from
171 # bb classes twice
172 text = '\n'.join(self.body)
173 bb.methodpool.insert_method(self.modulename, text, self.filename)
174 data.setVarFlag(self.function, "func", 1)
175 data.setVarFlag(self.function, "python", 1)
176 data.setVar(self.function, text)
177
178class MethodFlagsNode(AstNode):
179 def __init__(self, filename, lineno, key, m):
180 AstNode.__init__(self, filename, lineno)
181 self.key = key
182 self.m = m
183
184 def eval(self, data):
185 if data.getVar(self.key):
186 # clean up old version of this piece of metadata, as its
187 # flags could cause problems
188 data.setVarFlag(self.key, 'python', None)
189 data.setVarFlag(self.key, 'fakeroot', None)
190 if self.m.group("py") is not None:
191 data.setVarFlag(self.key, "python", "1")
192 else:
193 data.delVarFlag(self.key, "python")
194 if self.m.group("fr") is not None:
195 data.setVarFlag(self.key, "fakeroot", "1")
196 else:
197 data.delVarFlag(self.key, "fakeroot")
198
199class ExportFuncsNode(AstNode):
200 def __init__(self, filename, lineno, fns, classname):
201 AstNode.__init__(self, filename, lineno)
202 self.n = fns.split()
203 self.classname = classname
204
205 def eval(self, data):
206
207 for func in self.n:
208 calledfunc = self.classname + "_" + func
209
210 if data.getVar(func) and not data.getVarFlag(func, 'export_func'):
211 continue
212
213 if data.getVar(func):
214 data.setVarFlag(func, 'python', None)
215 data.setVarFlag(func, 'func', None)
216
217 for flag in [ "func", "python" ]:
218 if data.getVarFlag(calledfunc, flag):
219 data.setVarFlag(func, flag, data.getVarFlag(calledfunc, flag))
220 for flag in [ "dirs" ]:
221 if data.getVarFlag(func, flag):
222 data.setVarFlag(calledfunc, flag, data.getVarFlag(func, flag))
223
224 if data.getVarFlag(calledfunc, "python"):
225 data.setVar(func, " bb.build.exec_func('" + calledfunc + "', d)\n")
226 else:
227 data.setVar(func, " " + calledfunc + "\n")
228 data.setVarFlag(func, 'export_func', '1')
229
230class AddTaskNode(AstNode):
231 def __init__(self, filename, lineno, func, before, after):
232 AstNode.__init__(self, filename, lineno)
233 self.func = func
234 self.before = before
235 self.after = after
236
237 def eval(self, data):
238 var = self.func
239 if self.func[:3] != "do_":
240 var = "do_" + self.func
241
242 data.setVarFlag(var, "task", 1)
243 bbtasks = data.getVar('__BBTASKS') or []
244 if not var in bbtasks:
245 bbtasks.append(var)
246 data.setVar('__BBTASKS', bbtasks)
247
248 existing = data.getVarFlag(var, "deps") or []
249 if self.after is not None:
250 # set up deps for function
251 for entry in self.after.split():
252 if entry not in existing:
253 existing.append(entry)
254 data.setVarFlag(var, "deps", existing)
255 if self.before is not None:
256 # set up things that depend on this func
257 for entry in self.before.split():
258 existing = data.getVarFlag(entry, "deps") or []
259 if var not in existing:
260 data.setVarFlag(entry, "deps", [var] + existing)
261
262class BBHandlerNode(AstNode):
263 def __init__(self, filename, lineno, fns):
264 AstNode.__init__(self, filename, lineno)
265 self.hs = fns.split()
266
267 def eval(self, data):
268 bbhands = data.getVar('__BBHANDLERS') or []
269 for h in self.hs:
270 bbhands.append(h)
271 data.setVarFlag(h, "handler", 1)
272 data.setVar('__BBHANDLERS', bbhands)
273
274class InheritNode(AstNode):
275 def __init__(self, filename, lineno, classes):
276 AstNode.__init__(self, filename, lineno)
277 self.classes = classes
278
279 def eval(self, data):
280 bb.parse.BBHandler.inherit(self.classes, self.filename, self.lineno, data)
281
282def handleInclude(statements, filename, lineno, m, force):
283 statements.append(IncludeNode(filename, lineno, m.group(1), force))
284
285def handleExport(statements, filename, lineno, m):
286 statements.append(ExportNode(filename, lineno, m.group(1)))
287
288def handleData(statements, filename, lineno, groupd):
289 statements.append(DataNode(filename, lineno, groupd))
290
291def handleMethod(statements, filename, lineno, func_name, body):
292 statements.append(MethodNode(filename, lineno, func_name, body))
293
294def handlePythonMethod(statements, filename, lineno, funcname, modulename, body):
295 statements.append(PythonMethodNode(filename, lineno, funcname, modulename, body))
296
297def handleMethodFlags(statements, filename, lineno, key, m):
298 statements.append(MethodFlagsNode(filename, lineno, key, m))
299
300def handleExportFuncs(statements, filename, lineno, m, classname):
301 statements.append(ExportFuncsNode(filename, lineno, m.group(1), classname))
302
303def handleAddTask(statements, filename, lineno, m):
304 func = m.group("func")
305 before = m.group("before")
306 after = m.group("after")
307 if func is None:
308 return
309
310 statements.append(AddTaskNode(filename, lineno, func, before, after))
311
312def handleBBHandlers(statements, filename, lineno, m):
313 statements.append(BBHandlerNode(filename, lineno, m.group(1)))
314
315def handleInherit(statements, filename, lineno, m):
316 classes = m.group(1)
317 statements.append(InheritNode(filename, lineno, classes))
318
319def finalize(fn, d, variant = None):
320 all_handlers = {}
321 for var in d.getVar('__BBHANDLERS') or []:
322 # try to add the handler
323 bb.event.register(var, d.getVar(var), (d.getVarFlag(var, "eventmask", True) or "").split())
324
325 bb.event.fire(bb.event.RecipePreFinalise(fn), d)
326
327 bb.data.expandKeys(d)
328 bb.data.update_data(d)
329 code = []
330 for funcname in d.getVar("__BBANONFUNCS") or []:
331 code.append("%s(d)" % funcname)
332 bb.utils.better_exec("\n".join(code), {"d": d})
333 bb.data.update_data(d)
334
335 tasklist = d.getVar('__BBTASKS') or []
336 bb.build.add_tasks(tasklist, d)
337
338 bb.parse.siggen.finalise(fn, d, variant)
339
340 d.setVar('BBINCLUDED', bb.parse.get_file_depends(d))
341
342 bb.event.fire(bb.event.RecipeParsed(fn), d)
343
344def _create_variants(datastores, names, function):
345 def create_variant(name, orig_d, arg = None):
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(2, "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.SkipPackage 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.SkipPackage as e:
435 d.setVar("__SKIPPED", e.args[0])
436
437 _create_variants(datastores, versions, verfunc)
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)
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.SkipPackage 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..01f22d3b24
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/BBHandler.py
@@ -0,0 +1,258 @@
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__addhandler_regexp__ = re.compile( r"addhandler\s+(.+)" )
46__def_regexp__ = re.compile( r"def\s+(\w+).*:" )
47__python_func_regexp__ = re.compile( r"(\s+.*)|(^$)" )
48
49
50__infunc__ = ""
51__inpython__ = False
52__body__ = []
53__classname__ = ""
54
55cached_statements = {}
56
57# We need to indicate EOF to the feeder. This code is so messy that
58# factoring it out to a close_parse_file method is out of question.
59# We will use the IN_PYTHON_EOF as an indicator to just close the method
60#
61# The two parts using it are tightly integrated anyway
62IN_PYTHON_EOF = -9999999999999
63
64
65
66def supports(fn, d):
67 """Return True if fn has a supported extension"""
68 return os.path.splitext(fn)[-1] in [".bb", ".bbclass", ".inc"]
69
70def inherit(files, fn, lineno, d):
71 __inherit_cache = d.getVar('__inherit_cache') or []
72 files = d.expand(files).split()
73 for file in files:
74 if not os.path.isabs(file) and not file.endswith(".bbclass"):
75 file = os.path.join('classes', '%s.bbclass' % file)
76
77 if not os.path.isabs(file):
78 dname = os.path.dirname(fn)
79 bbpath = "%s:%s" % (dname, d.getVar("BBPATH", True))
80 abs_fn = bb.utils.which(bbpath, file)
81 if abs_fn:
82 file = abs_fn
83
84 if not file in __inherit_cache:
85 logger.log(logging.DEBUG -1, "BB %s:%d: inheriting %s", fn, lineno, file)
86 __inherit_cache.append( file )
87 d.setVar('__inherit_cache', __inherit_cache)
88 include(fn, file, lineno, d, "inherit")
89 __inherit_cache = d.getVar('__inherit_cache') or []
90
91def get_statements(filename, absolute_filename, base_name):
92 global cached_statements
93
94 try:
95 return cached_statements[absolute_filename]
96 except KeyError:
97 file = open(absolute_filename, 'r')
98 statements = ast.StatementGroup()
99
100 lineno = 0
101 while True:
102 lineno = lineno + 1
103 s = file.readline()
104 if not s: break
105 s = s.rstrip()
106 feeder(lineno, s, filename, base_name, statements)
107 file.close()
108 if __inpython__:
109 # add a blank line to close out any python definition
110 feeder(IN_PYTHON_EOF, "", filename, base_name, statements)
111
112 if filename.endswith(".bbclass") or filename.endswith(".inc"):
113 cached_statements[absolute_filename] = statements
114 return statements
115
116def handle(fn, d, include):
117 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __infunc__, __body__, __residue__, __classname__
118 __body__ = []
119 __infunc__ = ""
120 __classname__ = ""
121 __residue__ = []
122
123
124 if include == 0:
125 logger.debug(2, "BB %s: handle(data)", fn)
126 else:
127 logger.debug(2, "BB %s: handle(data, include)", fn)
128
129 base_name = os.path.basename(fn)
130 (root, ext) = os.path.splitext(base_name)
131 init(d)
132
133 if ext == ".bbclass":
134 __classname__ = root
135 __inherit_cache = d.getVar('__inherit_cache') or []
136 if not fn in __inherit_cache:
137 __inherit_cache.append(fn)
138 d.setVar('__inherit_cache', __inherit_cache)
139
140 if include != 0:
141 oldfile = d.getVar('FILE')
142 else:
143 oldfile = None
144
145 abs_fn = resolve_file(fn, d)
146
147 if include:
148 bb.parse.mark_dependency(d, abs_fn)
149
150 # actual loading
151 statements = get_statements(fn, abs_fn, base_name)
152
153 # DONE WITH PARSING... time to evaluate
154 if ext != ".bbclass":
155 d.setVar('FILE', abs_fn)
156
157 try:
158 statements.eval(d)
159 except bb.parse.SkipPackage:
160 bb.data.setVar("__SKIPPED", True, d)
161 if include == 0:
162 return { "" : d }
163
164 if ext != ".bbclass" and include == 0:
165 return ast.multi_finalize(fn, d)
166
167 if oldfile:
168 d.setVar("FILE", oldfile)
169
170 return d
171
172def feeder(lineno, s, fn, root, statements):
173 global __func_start_regexp__, __inherit_regexp__, __export_func_regexp__, __addtask_regexp__, __addhandler_regexp__, __def_regexp__, __python_func_regexp__, __inpython__, __infunc__, __body__, bb, __residue__, __classname__
174 if __infunc__:
175 if s == '}':
176 __body__.append('')
177 ast.handleMethod(statements, fn, lineno, __infunc__, __body__)
178 __infunc__ = ""
179 __body__ = []
180 else:
181 __body__.append(s)
182 return
183
184 if __inpython__:
185 m = __python_func_regexp__.match(s)
186 if m and lineno != IN_PYTHON_EOF:
187 __body__.append(s)
188 return
189 else:
190 ast.handlePythonMethod(statements, fn, lineno, __inpython__,
191 root, __body__)
192 __body__ = []
193 __inpython__ = False
194
195 if lineno == IN_PYTHON_EOF:
196 return
197
198 if s and s[0] == '#':
199 if len(__residue__) != 0 and __residue__[0][0] != "#":
200 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))
201
202 if len(__residue__) != 0 and __residue__[0][0] == "#" and (not s or s[0] != "#"):
203 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))
204
205 if s and s[-1] == '\\':
206 __residue__.append(s[:-1])
207 return
208
209 s = "".join(__residue__) + s
210 __residue__ = []
211
212 # Skip empty lines
213 if s == '':
214 return
215
216 # Skip comments
217 if s[0] == '#':
218 return
219
220 m = __func_start_regexp__.match(s)
221 if m:
222 __infunc__ = m.group("func") or "__anonymous"
223 ast.handleMethodFlags(statements, fn, lineno, __infunc__, m)
224 return
225
226 m = __def_regexp__.match(s)
227 if m:
228 __body__.append(s)
229 __inpython__ = m.group(1)
230
231 return
232
233 m = __export_func_regexp__.match(s)
234 if m:
235 ast.handleExportFuncs(statements, fn, lineno, m, __classname__)
236 return
237
238 m = __addtask_regexp__.match(s)
239 if m:
240 ast.handleAddTask(statements, fn, lineno, m)
241 return
242
243 m = __addhandler_regexp__.match(s)
244 if m:
245 ast.handleBBHandlers(statements, fn, lineno, m)
246 return
247
248 m = __inherit_regexp__.match(s)
249 if m:
250 ast.handleInherit(statements, fn, lineno, m)
251 return
252
253 return ConfHandler.feeder(lineno, s, fn, statements)
254
255# Add us to the handlers list
256from .. import handlers
257handlers.append({'supports': supports, 'handle': handle, 'init': init})
258del 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..7b30c8acb3
--- /dev/null
+++ b/bitbake/lib/bb/parse/parse_py/ConfHandler.py
@@ -0,0 +1,182 @@
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 = bb.utils.which(bbpath, fn)
86 if abs_fn:
87 fn = abs_fn
88
89 from bb.parse import handle
90 try:
91 ret = handle(fn, data, True)
92 except (IOError, OSError):
93 if error_out:
94 raise ParseError("Could not %(error_out)s file %(fn)s" % vars(), oldfn, lineno)
95 logger.debug(2, "CONF file '%s' not found", fn)
96
97# We have an issue where a UI might want to enforce particular settings such as
98# an empty DISTRO variable. If configuration files do something like assigning
99# a weak default, it turns out to be very difficult to filter out these changes,
100# particularly when the weak default might appear half way though parsing a chain
101# of configuration files. We therefore let the UIs hook into configuration file
102# parsing. This turns out to be a hard problem to solve any other way.
103confFilters = []
104
105def handle(fn, data, include):
106 init(data)
107
108 if include == 0:
109 oldfile = None
110 else:
111 oldfile = data.getVar('FILE')
112
113 abs_fn = resolve_file(fn, data)
114 f = open(abs_fn, 'r')
115
116 if include:
117 bb.parse.mark_dependency(data, abs_fn)
118
119 statements = ast.StatementGroup()
120 lineno = 0
121 while True:
122 lineno = lineno + 1
123 s = f.readline()
124 if not s:
125 break
126 w = s.strip()
127 # skip empty lines
128 if not w:
129 continue
130 s = s.rstrip()
131 while s[-1] == '\\':
132 s2 = f.readline().strip()
133 lineno = lineno + 1
134 if (not s2 or s2 and s2[0] != "#") and s[0] == "#" :
135 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))
136 s = s[:-1] + s2
137 # skip comments
138 if s[0] == '#':
139 continue
140 feeder(lineno, s, fn, statements)
141
142 # DONE WITH PARSING... time to evaluate
143 data.setVar('FILE', abs_fn)
144 statements.eval(data)
145 if oldfile:
146 data.setVar('FILE', oldfile)
147
148 f.close()
149
150 for f in confFilters:
151 f(fn, data)
152
153 return data
154
155def feeder(lineno, s, fn, statements):
156 m = __config_regexp__.match(s)
157 if m:
158 groupd = m.groupdict()
159 ast.handleData(statements, fn, lineno, groupd)
160 return
161
162 m = __include_regexp__.match(s)
163 if m:
164 ast.handleInclude(statements, fn, lineno, m, False)
165 return
166
167 m = __require_regexp__.match(s)
168 if m:
169 ast.handleInclude(statements, fn, lineno, m, True)
170 return
171
172 m = __export_regexp__.match(s)
173 if m:
174 ast.handleExport(statements, fn, lineno, m)
175 return
176
177 raise ParseError("unparsed line: '%s'" % s, fn, lineno);
178
179# Add us to the handlers list
180from bb.parse import handlers
181handlers.append({'supports': supports, 'handle': handle, 'init': init})
182del 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'