blob: d66d98cc8bfdd08167745eb91a37f96da0b8079d [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4BitBake 'Data' implementations
5
6Functions for interacting with the data structure used by the
7BitBake build tools.
8
9The expandKeys and update_data are the most expensive
10operations. At night the cookie monster came by and
11suggested 'give me cookies on setting the variables and
12things will work out'. Taking this suggestion into account
13applying the skills from the not yet passed 'Entwurf und
14Analyse von Algorithmen' lecture and the cookie
15monster seems to be right. We will track setVar more carefully
16to have faster update_data and expandKeys operations.
17
18This is a trade-off between speed and memory again but
19the speed is more critical here.
20"""
21
22# Copyright (C) 2003, 2004 Chris Larson
23# Copyright (C) 2005 Holger Hans Peter Freyther
24#
25# This program is free software; you can redistribute it and/or modify
26# it under the terms of the GNU General Public License version 2 as
27# published by the Free Software Foundation.
28#
29# This program is distributed in the hope that it will be useful,
30# but WITHOUT ANY WARRANTY; without even the implied warranty of
31# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32# GNU General Public License for more details.
33#
34# You should have received a copy of the GNU General Public License along
35# with this program; if not, write to the Free Software Foundation, Inc.,
36# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37#
38# Based on functions from the base bb module, Copyright 2003 Holger Schurig
39
40import sys, os, re
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080041import hashlib
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042if sys.argv[0][-5:] == "pydoc":
43 path = os.path.dirname(os.path.dirname(sys.argv[1]))
44else:
45 path = os.path.dirname(os.path.dirname(sys.argv[0]))
46sys.path.insert(0, path)
47from itertools import groupby
48
49from bb import data_smart
50from bb import codeparser
51import bb
52
53logger = data_smart.logger
54_dict_type = data_smart.DataSmart
55
56def init():
57 """Return a new object representing the Bitbake data"""
58 return _dict_type()
59
60def init_db(parent = None):
61 """Return a new object representing the Bitbake data,
62 optionally based on an existing object"""
63 if parent is not None:
64 return parent.createCopy()
65 else:
66 return _dict_type()
67
68def createCopy(source):
69 """Link the source set to the destination
70 If one does not find the value in the destination set,
71 search will go on to the source set to get the value.
72 Value from source are copy-on-write. i.e. any try to
73 modify one of them will end up putting the modified value
74 in the destination set.
75 """
76 return source.createCopy()
77
78def initVar(var, d):
79 """Non-destructive var init for data structure"""
80 d.initVar(var)
81
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082def keys(d):
83 """Return a list of keys in d"""
84 return d.keys()
85
86
87__expand_var_regexp__ = re.compile(r"\${[^{}]+}")
88__expand_python_regexp__ = re.compile(r"\${@.+?}")
89
90def expand(s, d, varname = None):
91 """Variable expansion using the data store"""
92 return d.expand(s, varname)
93
94def expandKeys(alterdata, readdata = None):
95 if readdata == None:
96 readdata = alterdata
97
98 todolist = {}
99 for key in alterdata:
100 if not '${' in key:
101 continue
102
103 ekey = expand(key, readdata)
104 if key == ekey:
105 continue
106 todolist[key] = ekey
107
108 # These two for loops are split for performance to maximise the
109 # usefulness of the expand cache
110 for key in sorted(todolist):
111 ekey = todolist[key]
112 newval = alterdata.getVar(ekey, False)
113 if newval is not None:
114 val = alterdata.getVar(key, False)
115 if val is not None:
116 bb.warn("Variable key %s (%s) replaces original key %s (%s)." % (key, val, ekey, newval))
117 alterdata.renameVar(key, ekey)
118
119def inheritFromOS(d, savedenv, permitted):
120 """Inherit variables from the initial environment."""
121 exportlist = bb.utils.preserved_envvars_exported()
122 for s in savedenv.keys():
123 if s in permitted:
124 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500125 d.setVar(s, savedenv.getVar(s), op = 'from env')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500126 if s in exportlist:
127 d.setVarFlag(s, "export", True, op = 'auto env export')
128 except TypeError:
129 pass
130
131def emit_var(var, o=sys.__stdout__, d = init(), all=False):
132 """Emit a variable to be sourced by a shell."""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600133 func = d.getVarFlag(var, "func", False)
134 if d.getVarFlag(var, 'python', False) and func:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135 return False
136
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500137 export = d.getVarFlag(var, "export", False)
138 unexport = d.getVarFlag(var, "unexport", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 if not all and not export and not unexport and not func:
140 return False
141
142 try:
143 if all:
144 oval = d.getVar(var, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500145 val = d.getVar(var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146 except (KeyboardInterrupt, bb.build.FuncFailed):
147 raise
148 except Exception as exc:
149 o.write('# expansion of %s threw %s: %s\n' % (var, exc.__class__.__name__, str(exc)))
150 return False
151
152 if all:
153 d.varhistory.emit(var, oval, val, o, d)
154
155 if (var.find("-") != -1 or var.find(".") != -1 or var.find('{') != -1 or var.find('}') != -1 or var.find('+') != -1) and not all:
156 return False
157
158 varExpanded = d.expand(var)
159
160 if unexport:
161 o.write('unset %s\n' % varExpanded)
162 return False
163
164 if val is None:
165 return False
166
167 val = str(val)
168
169 if varExpanded.startswith("BASH_FUNC_"):
170 varExpanded = varExpanded[10:-2]
171 val = val[3:] # Strip off "() "
172 o.write("%s() %s\n" % (varExpanded, val))
173 o.write("export -f %s\n" % (varExpanded))
174 return True
175
176 if func:
177 # NOTE: should probably check for unbalanced {} within the var
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500178 val = val.rstrip('\n')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500179 o.write("%s() {\n%s\n}\n" % (varExpanded, val))
180 return 1
181
182 if export:
183 o.write('export ')
184
185 # if we're going to output this within doublequotes,
186 # to a shell, we need to escape the quotes in the var
187 alter = re.sub('"', '\\"', val)
188 alter = re.sub('\n', ' \\\n', alter)
189 alter = re.sub('\\$', '\\\\$', alter)
190 o.write('%s="%s"\n' % (varExpanded, alter))
191 return False
192
193def emit_env(o=sys.__stdout__, d = init(), all=False):
194 """Emits all items in the data store in a format such that it can be sourced by a shell."""
195
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500196 isfunc = lambda key: bool(d.getVarFlag(key, "func", False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 keys = sorted((key for key in d.keys() if not key.startswith("__")), key=isfunc)
198 grouped = groupby(keys, isfunc)
199 for isfunc, keys in grouped:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 for key in sorted(keys):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 emit_var(key, o, d, all and not isfunc) and o.write('\n')
202
203def exported_keys(d):
204 return (key for key in d.keys() if not key.startswith('__') and
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500205 d.getVarFlag(key, 'export', False) and
206 not d.getVarFlag(key, 'unexport', False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207
208def exported_vars(d):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500209 k = list(exported_keys(d))
210 for key in k:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500211 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212 value = d.getVar(key)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500213 except Exception as err:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500214 bb.warn("%s: Unable to export ${%s}: %s" % (d.getVar("FILE"), key, err))
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500215 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216
217 if value is not None:
218 yield key, str(value)
219
220def emit_func(func, o=sys.__stdout__, d = init()):
221 """Emits all items in the data store in a format such that it can be sourced by a shell."""
222
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500223 keys = (key for key in d.keys() if not key.startswith("__") and not d.getVarFlag(key, "func", False))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500224 for key in sorted(keys):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 emit_var(key, o, d, False)
226
227 o.write('\n')
228 emit_var(func, o, d, False) and o.write('\n')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500229 newdeps = bb.codeparser.ShellParser(func, logger).parse_shell(d.getVar(func))
230 newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231 seen = set()
232 while newdeps:
233 deps = newdeps
234 seen |= deps
235 newdeps = set()
236 for dep in deps:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500237 if d.getVarFlag(dep, "func", False) and not d.getVarFlag(dep, "python", False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 emit_var(dep, o, d, False) and o.write('\n')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500239 newdeps |= bb.codeparser.ShellParser(dep, logger).parse_shell(d.getVar(dep))
240 newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 newdeps -= seen
242
243_functionfmt = """
244def {function}(d):
245{body}"""
246
247def emit_func_python(func, o=sys.__stdout__, d = init()):
248 """Emits all items in the data store in a format such that it can be sourced by a shell."""
249
250 def write_func(func, o, call = False):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500251 body = d.getVar(func, False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500252 if not body.startswith("def"):
253 body = _functionfmt.format(function=func, body=body)
254
255 o.write(body.strip() + "\n\n")
256 if call:
257 o.write(func + "(d)" + "\n\n")
258
259 write_func(func, o, True)
260 pp = bb.codeparser.PythonParser(func, logger)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500261 pp.parse_python(d.getVar(func, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262 newdeps = pp.execs
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500263 newdeps |= set((d.getVarFlag(func, "vardeps") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500264 seen = set()
265 while newdeps:
266 deps = newdeps
267 seen |= deps
268 newdeps = set()
269 for dep in deps:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500270 if d.getVarFlag(dep, "func", False) and d.getVarFlag(dep, "python", False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 write_func(dep, o)
272 pp = bb.codeparser.PythonParser(dep, logger)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500273 pp.parse_python(d.getVar(dep, False))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 newdeps |= pp.execs
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275 newdeps |= set((d.getVarFlag(dep, "vardeps") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500276 newdeps -= seen
277
278def update_data(d):
279 """Performs final steps upon the datastore, including application of overrides"""
280 d.finalize(parent = True)
281
282def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
283 deps = set()
284 try:
285 if key[-1] == ']':
286 vf = key[:-1].split('[')
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800287 value, parser = d.getVarFlag(vf[0], vf[1], False, retparser=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288 deps |= parser.references
289 deps = deps | (keys & parser.execs)
290 return deps, value
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 varflags = d.getVarFlags(key, ["vardeps", "vardepvalue", "vardepsexclude", "exports", "postfuncs", "prefuncs", "lineno", "filename"]) or {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500292 vardeps = varflags.get("vardeps")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500293
294 def handle_contains(value, contains, d):
295 newvalue = ""
296 for k in sorted(contains):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500297 l = (d.getVar(k) or "").split()
298 for item in sorted(contains[k]):
299 for word in item.split():
300 if not word in l:
301 newvalue += "\n%s{%s} = Unset" % (k, item)
302 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500304 newvalue += "\n%s{%s} = Set" % (k, item)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305 if not newvalue:
306 return value
307 if not value:
308 return newvalue
309 return value + newvalue
310
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800311 def handle_remove(value, deps, removes, d):
312 for r in sorted(removes):
313 r2 = d.expandWithRefs(r, None)
314 value += "\n_remove of %s" % r
315 deps |= r2.references
316 deps = deps | (keys & r2.execs)
317 return value
318
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500319 if "vardepvalue" in varflags:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800320 value = varflags.get("vardepvalue")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321 elif varflags.get("func"):
322 if varflags.get("python"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800323 value = d.getVarFlag(key, "_content", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 parser = bb.codeparser.PythonParser(key, logger)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500325 if value and "\t" in value:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500326 logger.warning("Variable %s contains tabs, please remove these (%s)" % (key, d.getVar("FILE")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500327 parser.parse_python(value, filename=varflags.get("filename"), lineno=varflags.get("lineno"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328 deps = deps | parser.references
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500329 deps = deps | (keys & parser.execs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330 value = handle_contains(value, parser.contains, d)
331 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800332 value, parsedvar = d.getVarFlag(key, "_content", False, retparser=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333 parser = bb.codeparser.ShellParser(key, logger)
334 parser.parse_shell(parsedvar.value)
335 deps = deps | shelldeps
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500336 deps = deps | parsedvar.references
337 deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
338 value = handle_contains(value, parsedvar.contains, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800339 if hasattr(parsedvar, "removes"):
340 value = handle_remove(value, deps, parsedvar.removes, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500341 if vardeps is None:
342 parser.log.flush()
343 if "prefuncs" in varflags:
344 deps = deps | set(varflags["prefuncs"].split())
345 if "postfuncs" in varflags:
346 deps = deps | set(varflags["postfuncs"].split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 if "exports" in varflags:
348 deps = deps | set(varflags["exports"].split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800350 value, parser = d.getVarFlag(key, "_content", False, retparser=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 deps |= parser.references
352 deps = deps | (keys & parser.execs)
353 value = handle_contains(value, parser.contains, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800354 if hasattr(parser, "removes"):
355 value = handle_remove(value, deps, parser.removes, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356
357 if "vardepvalueexclude" in varflags:
358 exclude = varflags.get("vardepvalueexclude")
359 for excl in exclude.split('|'):
360 if excl:
361 value = value.replace(excl, '')
362
363 # Add varflags, assuming an exclusion list is set
364 if varflagsexcl:
365 varfdeps = []
366 for f in varflags:
367 if f not in varflagsexcl:
368 varfdeps.append('%s[%s]' % (key, f))
369 if varfdeps:
370 deps |= set(varfdeps)
371
372 deps |= set((vardeps or "").split())
373 deps -= set(varflags.get("vardepsexclude", "").split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500374 except bb.parse.SkipRecipe:
375 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500376 except Exception as e:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500377 bb.warn("Exception during build_dependencies for %s" % key)
378 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379 return deps, value
380 #bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
381 #d.setVarFlag(key, "vardeps", deps)
382
383def generate_dependencies(d):
384
385 keys = set(key for key in d if not key.startswith("__"))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500386 shelldeps = set(key for key in d.getVar("__exportlist", False) if d.getVarFlag(key, "export", False) and not d.getVarFlag(key, "unexport", False))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500387 varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500388
389 deps = {}
390 values = {}
391
392 tasklist = d.getVar('__BBTASKS', False) or []
393 for task in tasklist:
394 deps[task], values[task] = build_dependencies(task, keys, shelldeps, varflagsexcl, d)
395 newdeps = deps[task]
396 seen = set()
397 while newdeps:
398 nextdeps = newdeps
399 seen |= nextdeps
400 newdeps = set()
401 for dep in nextdeps:
402 if dep not in deps:
403 deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, varflagsexcl, d)
404 newdeps |= deps[dep]
405 newdeps -= seen
406 #print "For %s: %s" % (task, str(deps[task]))
407 return tasklist, deps, values
408
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800409def generate_dependency_hash(tasklist, gendeps, lookupcache, whitelist, fn):
410 taskdeps = {}
411 basehash = {}
412
413 for task in tasklist:
414 data = lookupcache[task]
415
416 if data is None:
417 bb.error("Task %s from %s seems to be empty?!" % (task, fn))
418 data = ''
419
420 gendeps[task] -= whitelist
421 newdeps = gendeps[task]
422 seen = set()
423 while newdeps:
424 nextdeps = newdeps
425 seen |= nextdeps
426 newdeps = set()
427 for dep in nextdeps:
428 if dep in whitelist:
429 continue
430 gendeps[dep] -= whitelist
431 newdeps |= gendeps[dep]
432 newdeps -= seen
433
434 alldeps = sorted(seen)
435 for dep in alldeps:
436 data = data + dep
437 var = lookupcache[dep]
438 if var is not None:
439 data = data + str(var)
440 k = fn + "." + task
441 basehash[k] = hashlib.md5(data.encode("utf-8")).hexdigest()
442 taskdeps[task] = alldeps
443
444 return taskdeps, basehash
445
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446def inherits_class(klass, d):
447 val = d.getVar('__inherit_cache', False) or []
448 needle = os.path.join('classes', '%s.bbclass' % klass)
449 for v in val:
450 if v.endswith(needle):
451 return True
452 return False