blob: 29c2388038bc0b8887cbeccb5cddc4daadeda555 [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 parser.parse_python(value, filename=varflags.get("filename"), lineno=varflags.get("lineno"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500326 deps = deps | parser.references
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500327 deps = deps | (keys & parser.execs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500328 value = handle_contains(value, parser.contains, d)
329 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800330 value, parsedvar = d.getVarFlag(key, "_content", False, retparser=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331 parser = bb.codeparser.ShellParser(key, logger)
332 parser.parse_shell(parsedvar.value)
333 deps = deps | shelldeps
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500334 deps = deps | parsedvar.references
335 deps = deps | (keys & parser.execs) | (keys & parsedvar.execs)
336 value = handle_contains(value, parsedvar.contains, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800337 if hasattr(parsedvar, "removes"):
338 value = handle_remove(value, deps, parsedvar.removes, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339 if vardeps is None:
340 parser.log.flush()
341 if "prefuncs" in varflags:
342 deps = deps | set(varflags["prefuncs"].split())
343 if "postfuncs" in varflags:
344 deps = deps | set(varflags["postfuncs"].split())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600345 if "exports" in varflags:
346 deps = deps | set(varflags["exports"].split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500347 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800348 value, parser = d.getVarFlag(key, "_content", False, retparser=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349 deps |= parser.references
350 deps = deps | (keys & parser.execs)
351 value = handle_contains(value, parser.contains, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800352 if hasattr(parser, "removes"):
353 value = handle_remove(value, deps, parser.removes, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354
355 if "vardepvalueexclude" in varflags:
356 exclude = varflags.get("vardepvalueexclude")
357 for excl in exclude.split('|'):
358 if excl:
359 value = value.replace(excl, '')
360
361 # Add varflags, assuming an exclusion list is set
362 if varflagsexcl:
363 varfdeps = []
364 for f in varflags:
365 if f not in varflagsexcl:
366 varfdeps.append('%s[%s]' % (key, f))
367 if varfdeps:
368 deps |= set(varfdeps)
369
370 deps |= set((vardeps or "").split())
371 deps -= set(varflags.get("vardepsexclude", "").split())
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372 except bb.parse.SkipRecipe:
373 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500374 except Exception as e:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500375 bb.warn("Exception during build_dependencies for %s" % key)
376 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377 return deps, value
378 #bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
379 #d.setVarFlag(key, "vardeps", deps)
380
381def generate_dependencies(d):
382
383 keys = set(key for key in d if not key.startswith("__"))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500384 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 -0500385 varflagsexcl = d.getVar('BB_SIGNATURE_EXCLUDE_FLAGS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
387 deps = {}
388 values = {}
389
390 tasklist = d.getVar('__BBTASKS', False) or []
391 for task in tasklist:
392 deps[task], values[task] = build_dependencies(task, keys, shelldeps, varflagsexcl, d)
393 newdeps = deps[task]
394 seen = set()
395 while newdeps:
396 nextdeps = newdeps
397 seen |= nextdeps
398 newdeps = set()
399 for dep in nextdeps:
400 if dep not in deps:
401 deps[dep], values[dep] = build_dependencies(dep, keys, shelldeps, varflagsexcl, d)
402 newdeps |= deps[dep]
403 newdeps -= seen
404 #print "For %s: %s" % (task, str(deps[task]))
405 return tasklist, deps, values
406
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800407def generate_dependency_hash(tasklist, gendeps, lookupcache, whitelist, fn):
408 taskdeps = {}
409 basehash = {}
410
411 for task in tasklist:
412 data = lookupcache[task]
413
414 if data is None:
415 bb.error("Task %s from %s seems to be empty?!" % (task, fn))
416 data = ''
417
418 gendeps[task] -= whitelist
419 newdeps = gendeps[task]
420 seen = set()
421 while newdeps:
422 nextdeps = newdeps
423 seen |= nextdeps
424 newdeps = set()
425 for dep in nextdeps:
426 if dep in whitelist:
427 continue
428 gendeps[dep] -= whitelist
429 newdeps |= gendeps[dep]
430 newdeps -= seen
431
432 alldeps = sorted(seen)
433 for dep in alldeps:
434 data = data + dep
435 var = lookupcache[dep]
436 if var is not None:
437 data = data + str(var)
438 k = fn + "." + task
Brad Bishop19323692019-04-05 15:28:33 -0400439 basehash[k] = hashlib.sha256(data.encode("utf-8")).hexdigest()
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800440 taskdeps[task] = alldeps
441
442 return taskdeps, basehash
443
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444def inherits_class(klass, d):
445 val = d.getVar('__inherit_cache', False) or []
446 needle = os.path.join('classes', '%s.bbclass' % klass)
447 for v in val:
448 if v.endswith(needle):
449 return True
450 return False