blob: a4bb1ff7fbe389e2cbe51cb813e2f346b6afbe0d [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
Patrick Williamsc124f4f2015-09-15 14:41:29 -05005import hashlib
6import logging
7import os
8import re
9import tempfile
Patrick Williamsc0f7c042017-02-23 20:41:17 -060010import pickle
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011import bb.data
Brad Bishop6e60e8b2018-02-01 10:27:11 -050012import difflib
13import simplediff
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050014from bb.checksum import FileChecksumCache
Brad Bishop08902b02019-08-20 09:16:51 -040015from bb import runqueue
Brad Bishopa34c0302019-09-23 22:34:48 -040016import hashserv
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017
18logger = logging.getLogger('BitBake.SigGen')
19
Patrick Williamsc124f4f2015-09-15 14:41:29 -050020def init(d):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060021 siggens = [obj for obj in globals().values()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022 if type(obj) is type and issubclass(obj, SignatureGenerator)]
23
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024 desired = d.getVar("BB_SIGNATURE_HANDLER") or "noop"
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025 for sg in siggens:
26 if desired == sg.name:
27 return sg(d)
28 break
29 else:
30 logger.error("Invalid signature generator '%s', using default 'noop'\n"
31 "Available generators: %s", desired,
32 ', '.join(obj.name for obj in siggens))
33 return SignatureGenerator(d)
34
35class SignatureGenerator(object):
36 """
37 """
38 name = "noop"
39
40 def __init__(self, data):
Brad Bishop37a0e4d2017-12-04 01:01:44 -050041 self.basehash = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042 self.taskhash = {}
43 self.runtaskdeps = {}
44 self.file_checksum_values = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050045 self.taints = {}
Brad Bishop08902b02019-08-20 09:16:51 -040046 self.unitaskhashes = {}
Brad Bishop00e122a2019-10-05 11:10:57 -040047 self.setscenetasks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048
49 def finalise(self, fn, d, varient):
50 return
51
Brad Bishop08902b02019-08-20 09:16:51 -040052 def get_unihash(self, tid):
53 return self.taskhash[tid]
Brad Bishop19323692019-04-05 15:28:33 -040054
Brad Bishop08902b02019-08-20 09:16:51 -040055 def get_taskhash(self, tid, deps, dataCache):
56 self.taskhash[tid] = hashlib.sha256(tid.encode("utf-8")).hexdigest()
57 return self.taskhash[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050059 def writeout_file_checksum_cache(self):
60 """Write/update the file checksum cache onto disk"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050061 return
62
63 def stampfile(self, stampbase, file_name, taskname, extrainfo):
64 return ("%s.%s.%s" % (stampbase, taskname, extrainfo)).rstrip('.')
65
66 def stampcleanmask(self, stampbase, file_name, taskname, extrainfo):
67 return ("%s.%s.%s" % (stampbase, taskname, extrainfo)).rstrip('.')
68
69 def dump_sigtask(self, fn, task, stampbase, runtime):
70 return
71
72 def invalidate_task(self, task, d, fn):
73 bb.build.del_stamp(task, d, fn)
74
75 def dump_sigs(self, dataCache, options):
76 return
77
78 def get_taskdata(self):
Brad Bishop00e122a2019-10-05 11:10:57 -040079 return (self.runtaskdeps, self.taskhash, self.file_checksum_values, self.taints, self.basehash, self.unitaskhashes, self.setscenetasks)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080
81 def set_taskdata(self, data):
Brad Bishop00e122a2019-10-05 11:10:57 -040082 self.runtaskdeps, self.taskhash, self.file_checksum_values, self.taints, self.basehash, self.unitaskhashes, self.setscenetasks = data
Patrick Williamsc124f4f2015-09-15 14:41:29 -050083
Brad Bishopd7bf8c12018-02-25 22:55:05 -050084 def reset(self, data):
85 self.__init__(data)
86
Brad Bishop08902b02019-08-20 09:16:51 -040087 def get_taskhashes(self):
88 return self.taskhash, self.unitaskhashes
89
90 def set_taskhashes(self, hashes):
91 self.taskhash, self.unitaskhashes = hashes
92
93 def save_unitaskhashes(self):
94 return
95
Brad Bishopa34c0302019-09-23 22:34:48 -040096 def set_setscene_tasks(self, setscene_tasks):
97 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -050098
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099class SignatureGeneratorBasic(SignatureGenerator):
100 """
101 """
102 name = "basic"
103
104 def __init__(self, data):
105 self.basehash = {}
106 self.taskhash = {}
107 self.taskdeps = {}
108 self.runtaskdeps = {}
109 self.file_checksum_values = {}
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500110 self.taints = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 self.gendeps = {}
112 self.lookupcache = {}
Brad Bishopa34c0302019-09-23 22:34:48 -0400113 self.setscenetasks = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500114 self.basewhitelist = set((data.getVar("BB_HASHBASE_WHITELIST") or "").split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115 self.taskwhitelist = None
116 self.init_rundepcheck(data)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500117 checksum_cache_file = data.getVar("BB_HASH_CHECKSUM_CACHE_FILE")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500118 if checksum_cache_file:
119 self.checksum_cache = FileChecksumCache()
120 self.checksum_cache.init_cache(data, checksum_cache_file)
121 else:
122 self.checksum_cache = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123
Brad Bishop08902b02019-08-20 09:16:51 -0400124 self.unihash_cache = bb.cache.SimpleCache("1")
125 self.unitaskhashes = self.unihash_cache.init_cache(data, "bb_unihashes.dat", {})
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127 def init_rundepcheck(self, data):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500128 self.taskwhitelist = data.getVar("BB_HASHTASK_WHITELIST") or None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129 if self.taskwhitelist:
130 self.twl = re.compile(self.taskwhitelist)
131 else:
132 self.twl = None
133
134 def _build_data(self, fn, d):
135
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500136 ignore_mismatch = ((d.getVar("BB_HASH_IGNORE_MISMATCH") or '') == '1')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137 tasklist, gendeps, lookupcache = bb.data.generate_dependencies(d)
138
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139 taskdeps, basehash = bb.data.generate_dependency_hash(tasklist, gendeps, lookupcache, self.basewhitelist, fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140
141 for task in tasklist:
Brad Bishop08902b02019-08-20 09:16:51 -0400142 tid = fn + ":" + task
143 if not ignore_mismatch and tid in self.basehash and self.basehash[tid] != basehash[tid]:
144 bb.error("When reparsing %s, the basehash value changed from %s to %s. The metadata is not deterministic and this needs to be fixed." % (tid, self.basehash[tid], basehash[tid]))
Brad Bishopc342db32019-05-15 21:57:59 -0400145 bb.error("The following commands may help:")
146 cmd = "$ bitbake %s -c%s" % (d.getVar('PN'), task)
147 # Make sure sigdata is dumped before run printdiff
148 bb.error("%s -Snone" % cmd)
149 bb.error("Then:")
150 bb.error("%s -Sprintdiff\n" % cmd)
Brad Bishop08902b02019-08-20 09:16:51 -0400151 self.basehash[tid] = basehash[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152
153 self.taskdeps[fn] = taskdeps
154 self.gendeps[fn] = gendeps
155 self.lookupcache[fn] = lookupcache
156
157 return taskdeps
158
Brad Bishopa34c0302019-09-23 22:34:48 -0400159 def set_setscene_tasks(self, setscene_tasks):
160 self.setscenetasks = setscene_tasks
161
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 def finalise(self, fn, d, variant):
163
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600164 mc = d.getVar("__BBMULTICONFIG", False) or ""
165 if variant or mc:
166 fn = bb.cache.realfn2virtual(fn, variant, mc)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500167
168 try:
169 taskdeps = self._build_data(fn, d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500170 except bb.parse.SkipRecipe:
171 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 except:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500173 bb.warn("Error during finalise of %s" % fn)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174 raise
175
176 #Slow but can be useful for debugging mismatched basehashes
177 #for task in self.taskdeps[fn]:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500178 # self.dump_sigtask(fn, task, d.getVar("STAMP"), False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500179
180 for task in taskdeps:
Brad Bishop08902b02019-08-20 09:16:51 -0400181 d.setVar("BB_BASEHASH_task-%s" % task, self.basehash[fn + ":" + task])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500182
183 def rundep_check(self, fn, recipename, task, dep, depname, dataCache):
184 # Return True if we should keep the dependency, False to drop it
185 # We only manipulate the dependencies for packages not in the whitelist
186 if self.twl and not self.twl.search(recipename):
187 # then process the actual dependencies
188 if self.twl.search(depname):
189 return False
190 return True
191
192 def read_taint(self, fn, task, stampbase):
193 taint = None
194 try:
195 with open(stampbase + '.' + task + '.taint', 'r') as taintf:
196 taint = taintf.read()
197 except IOError:
198 pass
199 return taint
200
Brad Bishop08902b02019-08-20 09:16:51 -0400201 def get_taskhash(self, tid, deps, dataCache):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800202
Brad Bishop08902b02019-08-20 09:16:51 -0400203 (mc, _, task, fn) = bb.runqueue.split_tid_mcfn(tid)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800204
Brad Bishop08902b02019-08-20 09:16:51 -0400205 data = dataCache.basetaskhash[tid]
206 self.basehash[tid] = data
207 self.runtaskdeps[tid] = []
208 self.file_checksum_values[tid] = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500209 recipename = dataCache.pkg_fn[fn]
210 for dep in sorted(deps, key=clean_basepath):
Brad Bishop08902b02019-08-20 09:16:51 -0400211 (depmc, _, deptaskname, depfn) = bb.runqueue.split_tid_mcfn(dep)
212 if mc != depmc:
Andrew Geissler99467da2019-02-25 18:54:23 -0600213 continue
Brad Bishop08902b02019-08-20 09:16:51 -0400214 depname = dataCache.pkg_fn[depfn]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215 if not self.rundep_check(fn, recipename, task, dep, depname, dataCache):
216 continue
217 if dep not in self.taskhash:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800218 bb.fatal("%s is not in taskhash, caller isn't calling in dependency order?" % dep)
Brad Bishop19323692019-04-05 15:28:33 -0400219 data = data + self.get_unihash(dep)
Brad Bishop08902b02019-08-20 09:16:51 -0400220 self.runtaskdeps[tid].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221
222 if task in dataCache.file_checksums[fn]:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500223 if self.checksum_cache:
224 checksums = self.checksum_cache.get_checksums(dataCache.file_checksums[fn][task], recipename)
225 else:
226 checksums = bb.fetch2.get_file_checksums(dataCache.file_checksums[fn][task], recipename)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227 for (f,cs) in checksums:
Brad Bishop08902b02019-08-20 09:16:51 -0400228 self.file_checksum_values[tid].append((f,cs))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500229 if cs:
230 data = data + cs
231
232 taskdep = dataCache.task_deps[fn]
233 if 'nostamp' in taskdep and task in taskdep['nostamp']:
234 # Nostamp tasks need an implicit taint so that they force any dependent tasks to run
235 import uuid
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500236 taint = str(uuid.uuid4())
237 data = data + taint
Brad Bishop08902b02019-08-20 09:16:51 -0400238 self.taints[tid] = "nostamp:" + taint
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239
240 taint = self.read_taint(fn, task, dataCache.stamp[fn])
241 if taint:
242 data = data + taint
Brad Bishop08902b02019-08-20 09:16:51 -0400243 self.taints[tid] = taint
244 logger.warning("%s is tainted from a forced run" % tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245
Brad Bishop19323692019-04-05 15:28:33 -0400246 h = hashlib.sha256(data.encode("utf-8")).hexdigest()
Brad Bishop08902b02019-08-20 09:16:51 -0400247 self.taskhash[tid] = h
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500248 #d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
249 return h
250
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500251 def writeout_file_checksum_cache(self):
252 """Write/update the file checksum cache onto disk"""
253 if self.checksum_cache:
254 self.checksum_cache.save_extras()
255 self.checksum_cache.save_merge()
256 else:
257 bb.fetch2.fetcher_parse_save()
258 bb.fetch2.fetcher_parse_done()
259
Brad Bishop08902b02019-08-20 09:16:51 -0400260 def save_unitaskhashes(self):
261 self.unihash_cache.save(self.unitaskhashes)
262
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263 def dump_sigtask(self, fn, task, stampbase, runtime):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500264
Brad Bishop08902b02019-08-20 09:16:51 -0400265 tid = fn + ":" + task
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500266 referencestamp = stampbase
267 if isinstance(runtime, str) and runtime.startswith("customfile"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500268 sigfile = stampbase
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500269 referencestamp = runtime[11:]
Brad Bishop08902b02019-08-20 09:16:51 -0400270 elif runtime and tid in self.taskhash:
Brad Bishop00e122a2019-10-05 11:10:57 -0400271 sigfile = stampbase + "." + task + ".sigdata" + "." + self.get_unihash(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500272 else:
Brad Bishop08902b02019-08-20 09:16:51 -0400273 sigfile = stampbase + "." + task + ".sigbasedata" + "." + self.basehash[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
275 bb.utils.mkdirhier(os.path.dirname(sigfile))
276
277 data = {}
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500278 data['task'] = task
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279 data['basewhitelist'] = self.basewhitelist
280 data['taskwhitelist'] = self.taskwhitelist
281 data['taskdeps'] = self.taskdeps[fn][task]
Brad Bishop08902b02019-08-20 09:16:51 -0400282 data['basehash'] = self.basehash[tid]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283 data['gendeps'] = {}
284 data['varvals'] = {}
285 data['varvals'][task] = self.lookupcache[fn][task]
286 for dep in self.taskdeps[fn][task]:
287 if dep in self.basewhitelist:
288 continue
289 data['gendeps'][dep] = self.gendeps[fn][dep]
290 data['varvals'][dep] = self.lookupcache[fn][dep]
291
Brad Bishop08902b02019-08-20 09:16:51 -0400292 if runtime and tid in self.taskhash:
293 data['runtaskdeps'] = self.runtaskdeps[tid]
294 data['file_checksum_values'] = [(os.path.basename(f), cs) for f,cs in self.file_checksum_values[tid]]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500295 data['runtaskhashes'] = {}
296 for dep in data['runtaskdeps']:
Brad Bishop19323692019-04-05 15:28:33 -0400297 data['runtaskhashes'][dep] = self.get_unihash(dep)
Brad Bishop08902b02019-08-20 09:16:51 -0400298 data['taskhash'] = self.taskhash[tid]
Brad Bishop00e122a2019-10-05 11:10:57 -0400299 data['unihash'] = self.get_unihash(tid)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500301 taint = self.read_taint(fn, task, referencestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302 if taint:
303 data['taint'] = taint
304
Brad Bishop08902b02019-08-20 09:16:51 -0400305 if runtime and tid in self.taints:
306 if 'nostamp:' in self.taints[tid]:
307 data['taint'] = self.taints[tid]
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500308
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500309 computed_basehash = calc_basehash(data)
Brad Bishop08902b02019-08-20 09:16:51 -0400310 if computed_basehash != self.basehash[tid]:
311 bb.error("Basehash mismatch %s versus %s for %s" % (computed_basehash, self.basehash[tid], tid))
312 if runtime and tid in self.taskhash:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500313 computed_taskhash = calc_taskhash(data)
Brad Bishop08902b02019-08-20 09:16:51 -0400314 if computed_taskhash != self.taskhash[tid]:
315 bb.error("Taskhash mismatch %s versus %s for %s" % (computed_taskhash, self.taskhash[tid], tid))
316 sigfile = sigfile.replace(self.taskhash[tid], computed_taskhash)
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500317
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318 fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(sigfile), prefix="sigtask.")
319 try:
320 with os.fdopen(fd, "wb") as stream:
321 p = pickle.dump(data, stream, -1)
322 stream.flush()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 os.chmod(tmpfile, 0o664)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324 os.rename(tmpfile, sigfile)
325 except (OSError, IOError) as err:
326 try:
327 os.unlink(tmpfile)
328 except OSError:
329 pass
330 raise err
331
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500332 def dump_sigfn(self, fn, dataCaches, options):
333 if fn in self.taskdeps:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500334 for task in self.taskdeps[fn]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600335 tid = fn + ":" + task
Brad Bishop08902b02019-08-20 09:16:51 -0400336 mc = bb.runqueue.mc_from_tid(tid)
337 if tid not in self.taskhash:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338 continue
Brad Bishop08902b02019-08-20 09:16:51 -0400339 if dataCaches[mc].basetaskhash[tid] != self.basehash[tid]:
340 bb.error("Bitbake's cached basehash does not match the one we just generated (%s)!" % tid)
341 bb.error("The mismatched hashes were %s and %s" % (dataCaches[mc].basetaskhash[tid], self.basehash[tid]))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600342 self.dump_sigtask(fn, task, dataCaches[mc].stamp[fn], True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343
344class SignatureGeneratorBasicHash(SignatureGeneratorBasic):
345 name = "basichash"
346
Brad Bishop08902b02019-08-20 09:16:51 -0400347 def get_stampfile_hash(self, tid):
348 if tid in self.taskhash:
349 return self.taskhash[tid]
Brad Bishop19323692019-04-05 15:28:33 -0400350
351 # If task is not in basehash, then error
Brad Bishop08902b02019-08-20 09:16:51 -0400352 return self.basehash[tid]
Brad Bishop19323692019-04-05 15:28:33 -0400353
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500354 def stampfile(self, stampbase, fn, taskname, extrainfo, clean=False):
355 if taskname != "do_setscene" and taskname.endswith("_setscene"):
Brad Bishop08902b02019-08-20 09:16:51 -0400356 tid = fn + ":" + taskname[:-9]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500357 else:
Brad Bishop08902b02019-08-20 09:16:51 -0400358 tid = fn + ":" + taskname
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 if clean:
360 h = "*"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361 else:
Brad Bishop08902b02019-08-20 09:16:51 -0400362 h = self.get_stampfile_hash(tid)
Brad Bishop19323692019-04-05 15:28:33 -0400363
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500364 return ("%s.%s.%s.%s" % (stampbase, taskname, h, extrainfo)).rstrip('.')
365
366 def stampcleanmask(self, stampbase, fn, taskname, extrainfo):
367 return self.stampfile(stampbase, fn, taskname, extrainfo, clean=True)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800368
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500369 def invalidate_task(self, task, d, fn):
370 bb.note("Tainting hash to force rebuild of task %s, %s" % (fn, task))
371 bb.build.write_taint(task, d, fn)
372
Brad Bishop08902b02019-08-20 09:16:51 -0400373class SignatureGeneratorUniHashMixIn(object):
374 def get_taskdata(self):
375 return (self.server, self.method) + super().get_taskdata()
376
377 def set_taskdata(self, data):
378 self.server, self.method = data[:2]
379 super().set_taskdata(data[2:])
380
Brad Bishopa34c0302019-09-23 22:34:48 -0400381 def client(self):
382 if getattr(self, '_client', None) is None:
383 self._client = hashserv.create_client(self.server)
384 return self._client
385
Brad Bishop08902b02019-08-20 09:16:51 -0400386 def __get_task_unihash_key(self, tid):
387 # TODO: The key only *needs* to be the taskhash, the tid is just
388 # convenient
Brad Bishop00e122a2019-10-05 11:10:57 -0400389 return '%s:%s' % (tid.rsplit("/", 1)[1], self.taskhash[tid])
Brad Bishop08902b02019-08-20 09:16:51 -0400390
391 def get_stampfile_hash(self, tid):
392 if tid in self.taskhash:
393 # If a unique hash is reported, use it as the stampfile hash. This
394 # ensures that if a task won't be re-run if the taskhash changes,
395 # but it would result in the same output hash
396 unihash = self.unitaskhashes.get(self.__get_task_unihash_key(tid), None)
397 if unihash is not None:
398 return unihash
399
400 return super().get_stampfile_hash(tid)
401
402 def set_unihash(self, tid, unihash):
403 self.unitaskhashes[self.__get_task_unihash_key(tid)] = unihash
404
405 def get_unihash(self, tid):
Brad Bishop08902b02019-08-20 09:16:51 -0400406 taskhash = self.taskhash[tid]
407
Brad Bishopa34c0302019-09-23 22:34:48 -0400408 # If its not a setscene task we can return
409 if self.setscenetasks and tid not in self.setscenetasks:
410 return taskhash
411
Brad Bishop08902b02019-08-20 09:16:51 -0400412 key = self.__get_task_unihash_key(tid)
413
414 # TODO: This cache can grow unbounded. It probably only needs to keep
415 # for each task
416 unihash = self.unitaskhashes.get(key, None)
417 if unihash is not None:
418 return unihash
419
420 # In the absence of being able to discover a unique hash from the
421 # server, make it be equivalent to the taskhash. The unique "hash" only
422 # really needs to be a unique string (not even necessarily a hash), but
423 # making it match the taskhash has a few advantages:
424 #
425 # 1) All of the sstate code that assumes hashes can be the same
426 # 2) It provides maximal compatibility with builders that don't use
427 # an equivalency server
428 # 3) The value is easy for multiple independent builders to derive the
429 # same unique hash from the same input. This means that if the
430 # independent builders find the same taskhash, but it isn't reported
431 # to the server, there is a better chance that they will agree on
432 # the unique hash.
433 unihash = taskhash
434
435 try:
Brad Bishopa34c0302019-09-23 22:34:48 -0400436 data = self.client().get_unihash(self.method, self.taskhash[tid])
437 if data:
438 unihash = data
Brad Bishop08902b02019-08-20 09:16:51 -0400439 # A unique hash equal to the taskhash is not very interesting,
440 # so it is reported it at debug level 2. If they differ, that
441 # is much more interesting, so it is reported at debug level 1
442 bb.debug((1, 2)[unihash == taskhash], 'Found unihash %s in place of %s for %s from %s' % (unihash, taskhash, tid, self.server))
443 else:
444 bb.debug(2, 'No reported unihash for %s:%s from %s' % (tid, taskhash, self.server))
Brad Bishop00e122a2019-10-05 11:10:57 -0400445 except hashserv.client.HashConnectionError as e:
Brad Bishopa34c0302019-09-23 22:34:48 -0400446 bb.warn('Error contacting Hash Equivalence Server %s: %s' % (self.server, str(e)))
Brad Bishop08902b02019-08-20 09:16:51 -0400447
448 self.unitaskhashes[key] = unihash
449 return unihash
450
451 def report_unihash(self, path, task, d):
Brad Bishop08902b02019-08-20 09:16:51 -0400452 import importlib
453
454 taskhash = d.getVar('BB_TASKHASH')
455 unihash = d.getVar('BB_UNIHASH')
456 report_taskdata = d.getVar('SSTATE_HASHEQUIV_REPORT_TASKDATA') == '1'
457 tempdir = d.getVar('T')
458 fn = d.getVar('BB_FILENAME')
Brad Bishop00e122a2019-10-05 11:10:57 -0400459 tid = fn + ':do_' + task
460 key = tid.rsplit("/", 1)[1] + ':' + taskhash
461
462 if self.setscenetasks and tid not in self.setscenetasks:
463 return
Brad Bishop08902b02019-08-20 09:16:51 -0400464
465 # Sanity checks
466 cache_unihash = self.unitaskhashes.get(key, None)
467 if cache_unihash is None:
468 bb.fatal('%s not in unihash cache. Please report this error' % key)
469
470 if cache_unihash != unihash:
471 bb.fatal("Cache unihash %s doesn't match BB_UNIHASH %s" % (cache_unihash, unihash))
472
473 sigfile = None
474 sigfile_name = "depsig.do_%s.%d" % (task, os.getpid())
475 sigfile_link = "depsig.do_%s" % task
476
477 try:
478 sigfile = open(os.path.join(tempdir, sigfile_name), 'w+b')
479
480 locs = {'path': path, 'sigfile': sigfile, 'task': task, 'd': d}
481
482 if "." in self.method:
483 (module, method) = self.method.rsplit('.', 1)
484 locs['method'] = getattr(importlib.import_module(module), method)
485 outhash = bb.utils.better_eval('method(path, sigfile, task, d)', locs)
486 else:
487 outhash = bb.utils.better_eval(self.method + '(path, sigfile, task, d)', locs)
488
489 try:
Brad Bishopa34c0302019-09-23 22:34:48 -0400490 extra_data = {}
491
492 owner = d.getVar('SSTATE_HASHEQUIV_OWNER')
493 if owner:
494 extra_data['owner'] = owner
Brad Bishop08902b02019-08-20 09:16:51 -0400495
496 if report_taskdata:
497 sigfile.seek(0)
498
Brad Bishopa34c0302019-09-23 22:34:48 -0400499 extra_data['PN'] = d.getVar('PN')
500 extra_data['PV'] = d.getVar('PV')
501 extra_data['PR'] = d.getVar('PR')
502 extra_data['task'] = task
503 extra_data['outhash_siginfo'] = sigfile.read().decode('utf-8')
Brad Bishop08902b02019-08-20 09:16:51 -0400504
Brad Bishopa34c0302019-09-23 22:34:48 -0400505 data = self.client().report_unihash(taskhash, self.method, outhash, unihash, extra_data)
506 new_unihash = data['unihash']
Brad Bishop08902b02019-08-20 09:16:51 -0400507
508 if new_unihash != unihash:
509 bb.debug(1, 'Task %s unihash changed %s -> %s by server %s' % (taskhash, unihash, new_unihash, self.server))
510 bb.event.fire(bb.runqueue.taskUniHashUpdate(fn + ':do_' + task, new_unihash), d)
511 else:
512 bb.debug(1, 'Reported task %s as unihash %s to %s' % (taskhash, unihash, self.server))
Brad Bishop00e122a2019-10-05 11:10:57 -0400513 except hashserv.client.HashConnectionError as e:
Brad Bishopa34c0302019-09-23 22:34:48 -0400514 bb.warn('Error contacting Hash Equivalence Server %s: %s' % (self.server, str(e)))
Brad Bishop08902b02019-08-20 09:16:51 -0400515 finally:
516 if sigfile:
517 sigfile.close()
518
519 sigfile_link_path = os.path.join(tempdir, sigfile_link)
520 bb.utils.remove(sigfile_link_path)
521
522 try:
523 os.symlink(sigfile_name, sigfile_link_path)
524 except OSError:
525 pass
526
527
528#
529# Dummy class used for bitbake-selftest
530#
531class SignatureGeneratorTestEquivHash(SignatureGeneratorUniHashMixIn, SignatureGeneratorBasicHash):
532 name = "TestEquivHash"
533 def init_rundepcheck(self, data):
534 super().init_rundepcheck(data)
Brad Bishopa34c0302019-09-23 22:34:48 -0400535 self.server = data.getVar('BB_HASHSERVE')
Brad Bishop08902b02019-08-20 09:16:51 -0400536 self.method = "sstate_output_hash"
537
538
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539def dump_this_task(outfile, d):
540 import bb.parse
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500541 fn = d.getVar("BB_FILENAME")
542 task = "do_" + d.getVar("BB_CURRENTTASK")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500543 referencestamp = bb.build.stamp_internal(task, d, None, True)
544 bb.parse.siggen.dump_sigtask(fn, task, outfile, "customfile:" + referencestamp)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500546def init_colors(enable_color):
547 """Initialise colour dict for passing to compare_sigfiles()"""
548 # First set up the colours
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800549 colors = {'color_title': '\033[1m',
550 'color_default': '\033[0m',
551 'color_add': '\033[0;32m',
552 'color_remove': '\033[0;31m',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500553 }
554 # Leave all keys present but clear the values
555 if not enable_color:
556 for k in colors.keys():
557 colors[k] = ''
558 return colors
559
560def worddiff_str(oldstr, newstr, colors=None):
561 if not colors:
562 colors = init_colors(False)
563 diff = simplediff.diff(oldstr.split(' '), newstr.split(' '))
564 ret = []
565 for change, value in diff:
566 value = ' '.join(value)
567 if change == '=':
568 ret.append(value)
569 elif change == '+':
570 item = '{color_add}{{+{value}+}}{color_default}'.format(value=value, **colors)
571 ret.append(item)
572 elif change == '-':
573 item = '{color_remove}[-{value}-]{color_default}'.format(value=value, **colors)
574 ret.append(item)
575 whitespace_note = ''
576 if oldstr != newstr and ' '.join(oldstr.split()) == ' '.join(newstr.split()):
577 whitespace_note = ' (whitespace changed)'
578 return '"%s"%s' % (' '.join(ret), whitespace_note)
579
580def list_inline_diff(oldlist, newlist, colors=None):
581 if not colors:
582 colors = init_colors(False)
583 diff = simplediff.diff(oldlist, newlist)
584 ret = []
585 for change, value in diff:
586 value = ' '.join(value)
587 if change == '=':
588 ret.append("'%s'" % value)
589 elif change == '+':
590 item = '{color_add}+{value}{color_default}'.format(value=value, **colors)
591 ret.append(item)
592 elif change == '-':
593 item = '{color_remove}-{value}{color_default}'.format(value=value, **colors)
594 ret.append(item)
595 return '[%s]' % (', '.join(ret))
596
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597def clean_basepath(a):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500598 mc = None
Brad Bishop15ae2502019-06-18 21:44:24 -0400599 if a.startswith("mc:"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500600 _, mc, a = a.split(":", 2)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500601 b = a.rsplit("/", 2)[1] + '/' + a.rsplit("/", 2)[2]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602 if a.startswith("virtual:"):
603 b = b + ":" + a.rsplit(":", 1)[0]
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500604 if mc:
Brad Bishop15ae2502019-06-18 21:44:24 -0400605 b = b + ":mc:" + mc
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606 return b
607
608def clean_basepaths(a):
609 b = {}
610 for x in a:
611 b[clean_basepath(x)] = a[x]
612 return b
613
614def clean_basepaths_list(a):
615 b = []
616 for x in a:
617 b.append(clean_basepath(x))
618 return b
619
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500620def compare_sigfiles(a, b, recursecb=None, color=False, collapsed=False):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621 output = []
622
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500623 colors = init_colors(color)
624 def color_format(formatstr, **values):
625 """
626 Return colour formatted string.
627 NOTE: call with the format string, not an already formatted string
628 containing values (otherwise you could have trouble with { and }
629 characters)
630 """
631 if not formatstr.endswith('{color_default}'):
632 formatstr += '{color_default}'
633 # In newer python 3 versions you can pass both of these directly,
634 # but we only require 3.4 at the moment
635 formatparams = {}
636 formatparams.update(colors)
637 formatparams.update(values)
638 return formatstr.format(**formatparams)
639
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600640 with open(a, 'rb') as f:
641 p1 = pickle.Unpickler(f)
642 a_data = p1.load()
643 with open(b, 'rb') as f:
644 p2 = pickle.Unpickler(f)
645 b_data = p2.load()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646
647 def dict_diff(a, b, whitelist=set()):
648 sa = set(a.keys())
649 sb = set(b.keys())
650 common = sa & sb
651 changed = set()
652 for i in common:
653 if a[i] != b[i] and i not in whitelist:
654 changed.add(i)
655 added = sb - sa
656 removed = sa - sb
657 return changed, added, removed
658
659 def file_checksums_diff(a, b):
660 from collections import Counter
661 # Handle old siginfo format
662 if isinstance(a, dict):
663 a = [(os.path.basename(f), cs) for f, cs in a.items()]
664 if isinstance(b, dict):
665 b = [(os.path.basename(f), cs) for f, cs in b.items()]
666 # Compare lists, ensuring we can handle duplicate filenames if they exist
667 removedcount = Counter(a)
668 removedcount.subtract(b)
669 addedcount = Counter(b)
670 addedcount.subtract(a)
671 added = []
672 for x in b:
673 if addedcount[x] > 0:
674 addedcount[x] -= 1
675 added.append(x)
676 removed = []
677 changed = []
678 for x in a:
679 if removedcount[x] > 0:
680 removedcount[x] -= 1
681 for y in added:
682 if y[0] == x[0]:
683 changed.append((x[0], x[1], y[1]))
684 added.remove(y)
685 break
686 else:
687 removed.append(x)
688 added = [x[0] for x in added]
689 removed = [x[0] for x in removed]
690 return changed, added, removed
691
692 if 'basewhitelist' in a_data and a_data['basewhitelist'] != b_data['basewhitelist']:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500693 output.append(color_format("{color_title}basewhitelist changed{color_default} from '%s' to '%s'") % (a_data['basewhitelist'], b_data['basewhitelist']))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500694 if a_data['basewhitelist'] and b_data['basewhitelist']:
695 output.append("changed items: %s" % a_data['basewhitelist'].symmetric_difference(b_data['basewhitelist']))
696
697 if 'taskwhitelist' in a_data and a_data['taskwhitelist'] != b_data['taskwhitelist']:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500698 output.append(color_format("{color_title}taskwhitelist changed{color_default} from '%s' to '%s'") % (a_data['taskwhitelist'], b_data['taskwhitelist']))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500699 if a_data['taskwhitelist'] and b_data['taskwhitelist']:
700 output.append("changed items: %s" % a_data['taskwhitelist'].symmetric_difference(b_data['taskwhitelist']))
701
702 if a_data['taskdeps'] != b_data['taskdeps']:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500703 output.append(color_format("{color_title}Task dependencies changed{color_default} from:\n%s\nto:\n%s") % (sorted(a_data['taskdeps']), sorted(b_data['taskdeps'])))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500704
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 if a_data['basehash'] != b_data['basehash'] and not collapsed:
706 output.append(color_format("{color_title}basehash changed{color_default} from %s to %s") % (a_data['basehash'], b_data['basehash']))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
708 changed, added, removed = dict_diff(a_data['gendeps'], b_data['gendeps'], a_data['basewhitelist'] & b_data['basewhitelist'])
709 if changed:
710 for dep in changed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500711 output.append(color_format("{color_title}List of dependencies for variable %s changed from '{color_default}%s{color_title}' to '{color_default}%s{color_title}'") % (dep, a_data['gendeps'][dep], b_data['gendeps'][dep]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712 if a_data['gendeps'][dep] and b_data['gendeps'][dep]:
713 output.append("changed items: %s" % a_data['gendeps'][dep].symmetric_difference(b_data['gendeps'][dep]))
714 if added:
715 for dep in added:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500716 output.append(color_format("{color_title}Dependency on variable %s was added") % (dep))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717 if removed:
718 for dep in removed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500719 output.append(color_format("{color_title}Dependency on Variable %s was removed") % (dep))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500720
721
722 changed, added, removed = dict_diff(a_data['varvals'], b_data['varvals'])
723 if changed:
724 for dep in changed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 oldval = a_data['varvals'][dep]
726 newval = b_data['varvals'][dep]
727 if newval and oldval and ('\n' in oldval or '\n' in newval):
728 diff = difflib.unified_diff(oldval.splitlines(), newval.splitlines(), lineterm='')
729 # Cut off the first two lines, since we aren't interested in
730 # the old/new filename (they are blank anyway in this case)
731 difflines = list(diff)[2:]
732 if color:
733 # Add colour to diff output
734 for i, line in enumerate(difflines):
735 if line.startswith('+'):
736 line = color_format('{color_add}{line}', line=line)
737 difflines[i] = line
738 elif line.startswith('-'):
739 line = color_format('{color_remove}{line}', line=line)
740 difflines[i] = line
741 output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff='\n'.join(difflines)))
742 elif newval and oldval and (' ' in oldval or ' ' in newval):
743 output.append(color_format("{color_title}Variable {var} value changed:{color_default}\n{diff}", var=dep, diff=worddiff_str(oldval, newval, colors)))
744 else:
745 output.append(color_format("{color_title}Variable {var} value changed from '{color_default}{oldval}{color_title}' to '{color_default}{newval}{color_title}'{color_default}", var=dep, oldval=oldval, newval=newval))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 if not 'file_checksum_values' in a_data:
748 a_data['file_checksum_values'] = {}
749 if not 'file_checksum_values' in b_data:
750 b_data['file_checksum_values'] = {}
751
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752 changed, added, removed = file_checksums_diff(a_data['file_checksum_values'], b_data['file_checksum_values'])
753 if changed:
754 for f, old, new in changed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500755 output.append(color_format("{color_title}Checksum for file %s changed{color_default} from %s to %s") % (f, old, new))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756 if added:
757 for f in added:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500758 output.append(color_format("{color_title}Dependency on checksum of file %s was added") % (f))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759 if removed:
760 for f in removed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500761 output.append(color_format("{color_title}Dependency on checksum of file %s was removed") % (f))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500762
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 if not 'runtaskdeps' in a_data:
764 a_data['runtaskdeps'] = {}
765 if not 'runtaskdeps' in b_data:
766 b_data['runtaskdeps'] = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500768 if not collapsed:
769 if len(a_data['runtaskdeps']) != len(b_data['runtaskdeps']):
770 changed = ["Number of task dependencies changed"]
771 else:
772 changed = []
773 for idx, task in enumerate(a_data['runtaskdeps']):
774 a = a_data['runtaskdeps'][idx]
775 b = b_data['runtaskdeps'][idx]
776 if a_data['runtaskhashes'][a] != b_data['runtaskhashes'][b] and not collapsed:
777 changed.append("%s with hash %s\n changed to\n%s with hash %s" % (clean_basepath(a), a_data['runtaskhashes'][a], clean_basepath(b), b_data['runtaskhashes'][b]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500778
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500779 if changed:
780 clean_a = clean_basepaths_list(a_data['runtaskdeps'])
781 clean_b = clean_basepaths_list(b_data['runtaskdeps'])
782 if clean_a != clean_b:
783 output.append(color_format("{color_title}runtaskdeps changed:{color_default}\n%s") % list_inline_diff(clean_a, clean_b, colors))
784 else:
785 output.append(color_format("{color_title}runtaskdeps changed:"))
786 output.append("\n".join(changed))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
788
789 if 'runtaskhashes' in a_data and 'runtaskhashes' in b_data:
790 a = a_data['runtaskhashes']
791 b = b_data['runtaskhashes']
792 changed, added, removed = dict_diff(a, b)
793 if added:
794 for dep in added:
795 bdep_found = False
796 if removed:
797 for bdep in removed:
798 if b[dep] == a[bdep]:
799 #output.append("Dependency on task %s was replaced by %s with same hash" % (dep, bdep))
800 bdep_found = True
801 if not bdep_found:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500802 output.append(color_format("{color_title}Dependency on task %s was added{color_default} with hash %s") % (clean_basepath(dep), b[dep]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803 if removed:
804 for dep in removed:
805 adep_found = False
806 if added:
807 for adep in added:
808 if b[adep] == a[dep]:
809 #output.append("Dependency on task %s was replaced by %s with same hash" % (adep, dep))
810 adep_found = True
811 if not adep_found:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500812 output.append(color_format("{color_title}Dependency on task %s was removed{color_default} with hash %s") % (clean_basepath(dep), a[dep]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813 if changed:
814 for dep in changed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500815 if not collapsed:
816 output.append(color_format("{color_title}Hash for dependent task %s changed{color_default} from %s to %s") % (clean_basepath(dep), a[dep], b[dep]))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817 if callable(recursecb):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500818 recout = recursecb(dep, a[dep], b[dep])
819 if recout:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500820 if collapsed:
821 output.extend(recout)
822 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800823 # If a dependent hash changed, might as well print the line above and then defer to the changes in
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 # that hash since in all likelyhood, they're the same changes this task also saw.
825 output = [output[-1]] + recout
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
827 a_taint = a_data.get('taint', None)
828 b_taint = b_data.get('taint', None)
829 if a_taint != b_taint:
Brad Bishop96ff1982019-08-19 13:50:42 -0400830 if a_taint and a_taint.startswith('nostamp:'):
Brad Bishopc342db32019-05-15 21:57:59 -0400831 a_taint = a_taint.replace('nostamp:', 'nostamp(uuid4):')
Brad Bishop96ff1982019-08-19 13:50:42 -0400832 if b_taint and b_taint.startswith('nostamp:'):
Brad Bishopc342db32019-05-15 21:57:59 -0400833 b_taint = b_taint.replace('nostamp:', 'nostamp(uuid4):')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500834 output.append(color_format("{color_title}Taint (by forced/invalidated task) changed{color_default} from %s to %s") % (a_taint, b_taint))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500835
836 return output
837
838
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500839def calc_basehash(sigdata):
840 task = sigdata['task']
841 basedata = sigdata['varvals'][task]
842
843 if basedata is None:
844 basedata = ''
845
846 alldeps = sigdata['taskdeps']
847 for dep in alldeps:
848 basedata = basedata + dep
849 val = sigdata['varvals'][dep]
850 if val is not None:
851 basedata = basedata + str(val)
852
Brad Bishop19323692019-04-05 15:28:33 -0400853 return hashlib.sha256(basedata.encode("utf-8")).hexdigest()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500854
855def calc_taskhash(sigdata):
856 data = sigdata['basehash']
857
858 for dep in sigdata['runtaskdeps']:
859 data = data + sigdata['runtaskhashes'][dep]
860
861 for c in sigdata['file_checksum_values']:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500862 if c[1]:
863 data = data + c[1]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500864
865 if 'taint' in sigdata:
866 if 'nostamp:' in sigdata['taint']:
867 data = data + sigdata['taint'][8:]
868 else:
869 data = data + sigdata['taint']
870
Brad Bishop19323692019-04-05 15:28:33 -0400871 return hashlib.sha256(data.encode("utf-8")).hexdigest()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500872
873
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874def dump_sigfile(a):
875 output = []
876
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 with open(a, 'rb') as f:
878 p1 = pickle.Unpickler(f)
879 a_data = p1.load()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880
881 output.append("basewhitelist: %s" % (a_data['basewhitelist']))
882
883 output.append("taskwhitelist: %s" % (a_data['taskwhitelist']))
884
885 output.append("Task dependencies: %s" % (sorted(a_data['taskdeps'])))
886
887 output.append("basehash: %s" % (a_data['basehash']))
888
889 for dep in a_data['gendeps']:
890 output.append("List of dependencies for variable %s is %s" % (dep, a_data['gendeps'][dep]))
891
892 for dep in a_data['varvals']:
893 output.append("Variable %s value is %s" % (dep, a_data['varvals'][dep]))
894
895 if 'runtaskdeps' in a_data:
896 output.append("Tasks this task depends on: %s" % (a_data['runtaskdeps']))
897
898 if 'file_checksum_values' in a_data:
899 output.append("This task depends on the checksums of files: %s" % (a_data['file_checksum_values']))
900
901 if 'runtaskhashes' in a_data:
902 for dep in a_data['runtaskhashes']:
903 output.append("Hash for dependent task %s is %s" % (dep, a_data['runtaskhashes'][dep]))
904
905 if 'taint' in a_data:
Brad Bishopc342db32019-05-15 21:57:59 -0400906 if a_data['taint'].startswith('nostamp:'):
907 msg = a_data['taint'].replace('nostamp:', 'nostamp(uuid4):')
908 else:
909 msg = a_data['taint']
910 output.append("Tainted (by forced/invalidated task): %s" % msg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500911
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500912 if 'task' in a_data:
913 computed_basehash = calc_basehash(a_data)
914 output.append("Computed base hash is %s and from file %s" % (computed_basehash, a_data['basehash']))
915 else:
916 output.append("Unable to compute base hash")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500917
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500918 computed_taskhash = calc_taskhash(a_data)
919 output.append("Computed task hash is %s" % computed_taskhash)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500920
921 return output