1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyunimport bb.siggen 5*4882a593Smuzhiyunimport bb.runqueue 6*4882a593Smuzhiyunimport oe 7*4882a593Smuzhiyun 8*4882a593Smuzhiyundef sstate_rundepfilter(siggen, fn, recipename, task, dep, depname, dataCaches): 9*4882a593Smuzhiyun # Return True if we should keep the dependency, False to drop it 10*4882a593Smuzhiyun def isNative(x): 11*4882a593Smuzhiyun return x.endswith("-native") 12*4882a593Smuzhiyun def isCross(x): 13*4882a593Smuzhiyun return "-cross-" in x 14*4882a593Smuzhiyun def isNativeSDK(x): 15*4882a593Smuzhiyun return x.startswith("nativesdk-") 16*4882a593Smuzhiyun def isKernel(mc, fn): 17*4882a593Smuzhiyun inherits = " ".join(dataCaches[mc].inherits[fn]) 18*4882a593Smuzhiyun return inherits.find("/module-base.bbclass") != -1 or inherits.find("/linux-kernel-base.bbclass") != -1 19*4882a593Smuzhiyun def isPackageGroup(mc, fn): 20*4882a593Smuzhiyun inherits = " ".join(dataCaches[mc].inherits[fn]) 21*4882a593Smuzhiyun return "/packagegroup.bbclass" in inherits 22*4882a593Smuzhiyun def isAllArch(mc, fn): 23*4882a593Smuzhiyun inherits = " ".join(dataCaches[mc].inherits[fn]) 24*4882a593Smuzhiyun return "/allarch.bbclass" in inherits 25*4882a593Smuzhiyun def isImage(mc, fn): 26*4882a593Smuzhiyun return "/image.bbclass" in " ".join(dataCaches[mc].inherits[fn]) 27*4882a593Smuzhiyun def isSPDXTask(task): 28*4882a593Smuzhiyun return task in ("do_create_spdx", "do_create_runtime_spdx") 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun depmc, _, deptaskname, depmcfn = bb.runqueue.split_tid_mcfn(dep) 31*4882a593Smuzhiyun mc, _ = bb.runqueue.split_mc(fn) 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun # We can skip the rm_work task signature to avoid running the task 34*4882a593Smuzhiyun # when we remove some tasks from the dependencie chain 35*4882a593Smuzhiyun # i.e INHERIT:remove = "create-spdx" will trigger the do_rm_work 36*4882a593Smuzhiyun if task == "do_rm_work": 37*4882a593Smuzhiyun return False 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun # Keep all dependencies between SPDX tasks in the signature. SPDX documents 40*4882a593Smuzhiyun # are linked together by hashes, which means if a dependent document changes, 41*4882a593Smuzhiyun # all downstream documents must be re-written (even if they are "safe" 42*4882a593Smuzhiyun # dependencies). 43*4882a593Smuzhiyun if isSPDXTask(task) and isSPDXTask(deptaskname): 44*4882a593Smuzhiyun return True 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun # (Almost) always include our own inter-task dependencies (unless it comes 47*4882a593Smuzhiyun # from a mcdepends). The exception is the special 48*4882a593Smuzhiyun # do_kernel_configme->do_unpack_and_patch dependency from archiver.bbclass. 49*4882a593Smuzhiyun if recipename == depname and depmc == mc: 50*4882a593Smuzhiyun if task == "do_kernel_configme" and deptaskname == "do_unpack_and_patch": 51*4882a593Smuzhiyun return False 52*4882a593Smuzhiyun return True 53*4882a593Smuzhiyun 54*4882a593Smuzhiyun # Exclude well defined recipe->dependency 55*4882a593Smuzhiyun if "%s->%s" % (recipename, depname) in siggen.saferecipedeps: 56*4882a593Smuzhiyun return False 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun # Check for special wildcard 59*4882a593Smuzhiyun if "*->%s" % depname in siggen.saferecipedeps and recipename != depname: 60*4882a593Smuzhiyun return False 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun # Don't change native/cross/nativesdk recipe dependencies any further 63*4882a593Smuzhiyun if isNative(recipename) or isCross(recipename) or isNativeSDK(recipename): 64*4882a593Smuzhiyun return True 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun # Only target packages beyond here 67*4882a593Smuzhiyun 68*4882a593Smuzhiyun # allarch packagegroups are assumed to have well behaved names which don't change between architecures/tunes 69*4882a593Smuzhiyun if isPackageGroup(mc, fn) and isAllArch(mc, fn) and not isNative(depname): 70*4882a593Smuzhiyun return False 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun # Exclude well defined machine specific configurations which don't change ABI 73*4882a593Smuzhiyun if depname in siggen.abisaferecipes and not isImage(mc, fn): 74*4882a593Smuzhiyun return False 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun # Kernel modules are well namespaced. We don't want to depend on the kernel's checksum 77*4882a593Smuzhiyun # if we're just doing an RRECOMMENDS:xxx = "kernel-module-*", not least because the checksum 78*4882a593Smuzhiyun # is machine specific. 79*4882a593Smuzhiyun # Therefore if we're not a kernel or a module recipe (inheriting the kernel classes) 80*4882a593Smuzhiyun # and we reccomend a kernel-module, we exclude the dependency. 81*4882a593Smuzhiyun if dataCaches and isKernel(depmc, depmcfn) and not isKernel(mc, fn): 82*4882a593Smuzhiyun for pkg in dataCaches[mc].runrecs[fn]: 83*4882a593Smuzhiyun if " ".join(dataCaches[mc].runrecs[fn][pkg]).find("kernel-module-") != -1: 84*4882a593Smuzhiyun return False 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun # Default to keep dependencies 87*4882a593Smuzhiyun return True 88*4882a593Smuzhiyun 89*4882a593Smuzhiyundef sstate_lockedsigs(d): 90*4882a593Smuzhiyun sigs = {} 91*4882a593Smuzhiyun types = (d.getVar("SIGGEN_LOCKEDSIGS_TYPES") or "").split() 92*4882a593Smuzhiyun for t in types: 93*4882a593Smuzhiyun siggen_lockedsigs_var = "SIGGEN_LOCKEDSIGS_%s" % t 94*4882a593Smuzhiyun lockedsigs = (d.getVar(siggen_lockedsigs_var) or "").split() 95*4882a593Smuzhiyun for ls in lockedsigs: 96*4882a593Smuzhiyun pn, task, h = ls.split(":", 2) 97*4882a593Smuzhiyun if pn not in sigs: 98*4882a593Smuzhiyun sigs[pn] = {} 99*4882a593Smuzhiyun sigs[pn][task] = [h, siggen_lockedsigs_var] 100*4882a593Smuzhiyun return sigs 101*4882a593Smuzhiyun 102*4882a593Smuzhiyunclass SignatureGeneratorOEBasic(bb.siggen.SignatureGeneratorBasic): 103*4882a593Smuzhiyun name = "OEBasic" 104*4882a593Smuzhiyun def init_rundepcheck(self, data): 105*4882a593Smuzhiyun self.abisaferecipes = (data.getVar("SIGGEN_EXCLUDERECIPES_ABISAFE") or "").split() 106*4882a593Smuzhiyun self.saferecipedeps = (data.getVar("SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS") or "").split() 107*4882a593Smuzhiyun pass 108*4882a593Smuzhiyun def rundep_check(self, fn, recipename, task, dep, depname, dataCaches = None): 109*4882a593Smuzhiyun return sstate_rundepfilter(self, fn, recipename, task, dep, depname, dataCaches) 110*4882a593Smuzhiyun 111*4882a593Smuzhiyunclass SignatureGeneratorOEBasicHashMixIn(object): 112*4882a593Smuzhiyun supports_multiconfig_datacaches = True 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun def init_rundepcheck(self, data): 115*4882a593Smuzhiyun self.abisaferecipes = (data.getVar("SIGGEN_EXCLUDERECIPES_ABISAFE") or "").split() 116*4882a593Smuzhiyun self.saferecipedeps = (data.getVar("SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS") or "").split() 117*4882a593Smuzhiyun self.lockedsigs = sstate_lockedsigs(data) 118*4882a593Smuzhiyun self.lockedhashes = {} 119*4882a593Smuzhiyun self.lockedpnmap = {} 120*4882a593Smuzhiyun self.lockedhashfn = {} 121*4882a593Smuzhiyun self.machine = data.getVar("MACHINE") 122*4882a593Smuzhiyun self.mismatch_msgs = [] 123*4882a593Smuzhiyun self.unlockedrecipes = (data.getVar("SIGGEN_UNLOCKED_RECIPES") or 124*4882a593Smuzhiyun "").split() 125*4882a593Smuzhiyun self.unlockedrecipes = { k: "" for k in self.unlockedrecipes } 126*4882a593Smuzhiyun self._internal = False 127*4882a593Smuzhiyun pass 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun def tasks_resolved(self, virtmap, virtpnmap, dataCache): 130*4882a593Smuzhiyun # Translate virtual/xxx entries to PN values 131*4882a593Smuzhiyun newabisafe = [] 132*4882a593Smuzhiyun for a in self.abisaferecipes: 133*4882a593Smuzhiyun if a in virtpnmap: 134*4882a593Smuzhiyun newabisafe.append(virtpnmap[a]) 135*4882a593Smuzhiyun else: 136*4882a593Smuzhiyun newabisafe.append(a) 137*4882a593Smuzhiyun self.abisaferecipes = newabisafe 138*4882a593Smuzhiyun newsafedeps = [] 139*4882a593Smuzhiyun for a in self.saferecipedeps: 140*4882a593Smuzhiyun a1, a2 = a.split("->") 141*4882a593Smuzhiyun if a1 in virtpnmap: 142*4882a593Smuzhiyun a1 = virtpnmap[a1] 143*4882a593Smuzhiyun if a2 in virtpnmap: 144*4882a593Smuzhiyun a2 = virtpnmap[a2] 145*4882a593Smuzhiyun newsafedeps.append(a1 + "->" + a2) 146*4882a593Smuzhiyun self.saferecipedeps = newsafedeps 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun def rundep_check(self, fn, recipename, task, dep, depname, dataCaches = None): 149*4882a593Smuzhiyun return sstate_rundepfilter(self, fn, recipename, task, dep, depname, dataCaches) 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun def get_taskdata(self): 152*4882a593Smuzhiyun return (self.lockedpnmap, self.lockedhashfn, self.lockedhashes) + super().get_taskdata() 153*4882a593Smuzhiyun 154*4882a593Smuzhiyun def set_taskdata(self, data): 155*4882a593Smuzhiyun self.lockedpnmap, self.lockedhashfn, self.lockedhashes = data[:3] 156*4882a593Smuzhiyun super().set_taskdata(data[3:]) 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun def dump_sigs(self, dataCache, options): 159*4882a593Smuzhiyun sigfile = os.getcwd() + "/locked-sigs.inc" 160*4882a593Smuzhiyun bb.plain("Writing locked sigs to %s" % sigfile) 161*4882a593Smuzhiyun self.dump_lockedsigs(sigfile) 162*4882a593Smuzhiyun return super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigs(dataCache, options) 163*4882a593Smuzhiyun 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun def get_taskhash(self, tid, deps, dataCaches): 166*4882a593Smuzhiyun if tid in self.lockedhashes: 167*4882a593Smuzhiyun if self.lockedhashes[tid]: 168*4882a593Smuzhiyun return self.lockedhashes[tid] 169*4882a593Smuzhiyun else: 170*4882a593Smuzhiyun return super().get_taskhash(tid, deps, dataCaches) 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun h = super().get_taskhash(tid, deps, dataCaches) 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun (mc, _, task, fn) = bb.runqueue.split_tid_mcfn(tid) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun recipename = dataCaches[mc].pkg_fn[fn] 177*4882a593Smuzhiyun self.lockedpnmap[fn] = recipename 178*4882a593Smuzhiyun self.lockedhashfn[fn] = dataCaches[mc].hashfn[fn] 179*4882a593Smuzhiyun 180*4882a593Smuzhiyun unlocked = False 181*4882a593Smuzhiyun if recipename in self.unlockedrecipes: 182*4882a593Smuzhiyun unlocked = True 183*4882a593Smuzhiyun else: 184*4882a593Smuzhiyun def recipename_from_dep(dep): 185*4882a593Smuzhiyun (depmc, _, _, depfn) = bb.runqueue.split_tid_mcfn(dep) 186*4882a593Smuzhiyun return dataCaches[depmc].pkg_fn[depfn] 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun # If any unlocked recipe is in the direct dependencies then the 189*4882a593Smuzhiyun # current recipe should be unlocked as well. 190*4882a593Smuzhiyun depnames = [ recipename_from_dep(x) for x in deps if mc == bb.runqueue.mc_from_tid(x)] 191*4882a593Smuzhiyun if any(x in y for y in depnames for x in self.unlockedrecipes): 192*4882a593Smuzhiyun self.unlockedrecipes[recipename] = '' 193*4882a593Smuzhiyun unlocked = True 194*4882a593Smuzhiyun 195*4882a593Smuzhiyun if not unlocked and recipename in self.lockedsigs: 196*4882a593Smuzhiyun if task in self.lockedsigs[recipename]: 197*4882a593Smuzhiyun h_locked = self.lockedsigs[recipename][task][0] 198*4882a593Smuzhiyun var = self.lockedsigs[recipename][task][1] 199*4882a593Smuzhiyun self.lockedhashes[tid] = h_locked 200*4882a593Smuzhiyun self._internal = True 201*4882a593Smuzhiyun unihash = self.get_unihash(tid) 202*4882a593Smuzhiyun self._internal = False 203*4882a593Smuzhiyun #bb.warn("Using %s %s %s" % (recipename, task, h)) 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun if h != h_locked and h_locked != unihash: 206*4882a593Smuzhiyun self.mismatch_msgs.append('The %s:%s sig is computed to be %s, but the sig is locked to %s in %s' 207*4882a593Smuzhiyun % (recipename, task, h, h_locked, var)) 208*4882a593Smuzhiyun 209*4882a593Smuzhiyun return h_locked 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun self.lockedhashes[tid] = False 212*4882a593Smuzhiyun #bb.warn("%s %s %s" % (recipename, task, h)) 213*4882a593Smuzhiyun return h 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun def get_stampfile_hash(self, tid): 216*4882a593Smuzhiyun if tid in self.lockedhashes and self.lockedhashes[tid]: 217*4882a593Smuzhiyun return self.lockedhashes[tid] 218*4882a593Smuzhiyun return super().get_stampfile_hash(tid) 219*4882a593Smuzhiyun 220*4882a593Smuzhiyun def get_unihash(self, tid): 221*4882a593Smuzhiyun if tid in self.lockedhashes and self.lockedhashes[tid] and not self._internal: 222*4882a593Smuzhiyun return self.lockedhashes[tid] 223*4882a593Smuzhiyun return super().get_unihash(tid) 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun def dump_sigtask(self, fn, task, stampbase, runtime): 226*4882a593Smuzhiyun tid = fn + ":" + task 227*4882a593Smuzhiyun if tid in self.lockedhashes and self.lockedhashes[tid]: 228*4882a593Smuzhiyun return 229*4882a593Smuzhiyun super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigtask(fn, task, stampbase, runtime) 230*4882a593Smuzhiyun 231*4882a593Smuzhiyun def dump_lockedsigs(self, sigfile, taskfilter=None): 232*4882a593Smuzhiyun types = {} 233*4882a593Smuzhiyun for tid in self.runtaskdeps: 234*4882a593Smuzhiyun if taskfilter: 235*4882a593Smuzhiyun if not tid in taskfilter: 236*4882a593Smuzhiyun continue 237*4882a593Smuzhiyun fn = bb.runqueue.fn_from_tid(tid) 238*4882a593Smuzhiyun t = self.lockedhashfn[fn].split(" ")[1].split(":")[5] 239*4882a593Smuzhiyun t = 't-' + t.replace('_', '-') 240*4882a593Smuzhiyun if t not in types: 241*4882a593Smuzhiyun types[t] = [] 242*4882a593Smuzhiyun types[t].append(tid) 243*4882a593Smuzhiyun 244*4882a593Smuzhiyun with open(sigfile, "w") as f: 245*4882a593Smuzhiyun l = sorted(types) 246*4882a593Smuzhiyun for t in l: 247*4882a593Smuzhiyun f.write('SIGGEN_LOCKEDSIGS_%s = "\\\n' % t) 248*4882a593Smuzhiyun types[t].sort() 249*4882a593Smuzhiyun sortedtid = sorted(types[t], key=lambda tid: self.lockedpnmap[bb.runqueue.fn_from_tid(tid)]) 250*4882a593Smuzhiyun for tid in sortedtid: 251*4882a593Smuzhiyun (_, _, task, fn) = bb.runqueue.split_tid_mcfn(tid) 252*4882a593Smuzhiyun if tid not in self.taskhash: 253*4882a593Smuzhiyun continue 254*4882a593Smuzhiyun f.write(" " + self.lockedpnmap[fn] + ":" + task + ":" + self.get_unihash(tid) + " \\\n") 255*4882a593Smuzhiyun f.write(' "\n') 256*4882a593Smuzhiyun f.write('SIGGEN_LOCKEDSIGS_TYPES:%s = "%s"' % (self.machine, " ".join(l))) 257*4882a593Smuzhiyun 258*4882a593Smuzhiyun def dump_siglist(self, sigfile, path_prefix_strip=None): 259*4882a593Smuzhiyun def strip_fn(fn): 260*4882a593Smuzhiyun nonlocal path_prefix_strip 261*4882a593Smuzhiyun if not path_prefix_strip: 262*4882a593Smuzhiyun return fn 263*4882a593Smuzhiyun 264*4882a593Smuzhiyun fn_exp = fn.split(":") 265*4882a593Smuzhiyun if fn_exp[-1].startswith(path_prefix_strip): 266*4882a593Smuzhiyun fn_exp[-1] = fn_exp[-1][len(path_prefix_strip):] 267*4882a593Smuzhiyun 268*4882a593Smuzhiyun return ":".join(fn_exp) 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun with open(sigfile, "w") as f: 271*4882a593Smuzhiyun tasks = [] 272*4882a593Smuzhiyun for taskitem in self.taskhash: 273*4882a593Smuzhiyun (fn, task) = taskitem.rsplit(":", 1) 274*4882a593Smuzhiyun pn = self.lockedpnmap[fn] 275*4882a593Smuzhiyun tasks.append((pn, task, strip_fn(fn), self.taskhash[taskitem])) 276*4882a593Smuzhiyun for (pn, task, fn, taskhash) in sorted(tasks): 277*4882a593Smuzhiyun f.write('%s:%s %s %s\n' % (pn, task, fn, taskhash)) 278*4882a593Smuzhiyun 279*4882a593Smuzhiyun def checkhashes(self, sq_data, missed, found, d): 280*4882a593Smuzhiyun warn_msgs = [] 281*4882a593Smuzhiyun error_msgs = [] 282*4882a593Smuzhiyun sstate_missing_msgs = [] 283*4882a593Smuzhiyun 284*4882a593Smuzhiyun for tid in sq_data['hash']: 285*4882a593Smuzhiyun if tid not in found: 286*4882a593Smuzhiyun for pn in self.lockedsigs: 287*4882a593Smuzhiyun taskname = bb.runqueue.taskname_from_tid(tid) 288*4882a593Smuzhiyun if sq_data['hash'][tid] in iter(self.lockedsigs[pn].values()): 289*4882a593Smuzhiyun if taskname == 'do_shared_workdir': 290*4882a593Smuzhiyun continue 291*4882a593Smuzhiyun sstate_missing_msgs.append("Locked sig is set for %s:%s (%s) yet not in sstate cache?" 292*4882a593Smuzhiyun % (pn, taskname, sq_data['hash'][tid])) 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun checklevel = d.getVar("SIGGEN_LOCKEDSIGS_TASKSIG_CHECK") 295*4882a593Smuzhiyun if checklevel == 'warn': 296*4882a593Smuzhiyun warn_msgs += self.mismatch_msgs 297*4882a593Smuzhiyun elif checklevel == 'error': 298*4882a593Smuzhiyun error_msgs += self.mismatch_msgs 299*4882a593Smuzhiyun 300*4882a593Smuzhiyun checklevel = d.getVar("SIGGEN_LOCKEDSIGS_SSTATE_EXISTS_CHECK") 301*4882a593Smuzhiyun if checklevel == 'warn': 302*4882a593Smuzhiyun warn_msgs += sstate_missing_msgs 303*4882a593Smuzhiyun elif checklevel == 'error': 304*4882a593Smuzhiyun error_msgs += sstate_missing_msgs 305*4882a593Smuzhiyun 306*4882a593Smuzhiyun if warn_msgs: 307*4882a593Smuzhiyun bb.warn("\n".join(warn_msgs)) 308*4882a593Smuzhiyun if error_msgs: 309*4882a593Smuzhiyun bb.fatal("\n".join(error_msgs)) 310*4882a593Smuzhiyun 311*4882a593Smuzhiyunclass SignatureGeneratorOEBasicHash(SignatureGeneratorOEBasicHashMixIn, bb.siggen.SignatureGeneratorBasicHash): 312*4882a593Smuzhiyun name = "OEBasicHash" 313*4882a593Smuzhiyun 314*4882a593Smuzhiyunclass SignatureGeneratorOEEquivHash(SignatureGeneratorOEBasicHashMixIn, bb.siggen.SignatureGeneratorUniHashMixIn, bb.siggen.SignatureGeneratorBasicHash): 315*4882a593Smuzhiyun name = "OEEquivHash" 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun def init_rundepcheck(self, data): 318*4882a593Smuzhiyun super().init_rundepcheck(data) 319*4882a593Smuzhiyun self.server = data.getVar('BB_HASHSERVE') 320*4882a593Smuzhiyun if not self.server: 321*4882a593Smuzhiyun bb.fatal("OEEquivHash requires BB_HASHSERVE to be set") 322*4882a593Smuzhiyun self.method = data.getVar('SSTATE_HASHEQUIV_METHOD') 323*4882a593Smuzhiyun if not self.method: 324*4882a593Smuzhiyun bb.fatal("OEEquivHash requires SSTATE_HASHEQUIV_METHOD to be set") 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun# Insert these classes into siggen's namespace so it can see and select them 327*4882a593Smuzhiyunbb.siggen.SignatureGeneratorOEBasic = SignatureGeneratorOEBasic 328*4882a593Smuzhiyunbb.siggen.SignatureGeneratorOEBasicHash = SignatureGeneratorOEBasicHash 329*4882a593Smuzhiyunbb.siggen.SignatureGeneratorOEEquivHash = SignatureGeneratorOEEquivHash 330*4882a593Smuzhiyun 331*4882a593Smuzhiyun 332*4882a593Smuzhiyundef find_siginfo(pn, taskname, taskhashlist, d): 333*4882a593Smuzhiyun """ Find signature data files for comparison purposes """ 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun import fnmatch 336*4882a593Smuzhiyun import glob 337*4882a593Smuzhiyun 338*4882a593Smuzhiyun if not taskname: 339*4882a593Smuzhiyun # We have to derive pn and taskname 340*4882a593Smuzhiyun key = pn 341*4882a593Smuzhiyun splitit = key.split('.bb:') 342*4882a593Smuzhiyun taskname = splitit[1] 343*4882a593Smuzhiyun pn = os.path.basename(splitit[0]).split('_')[0] 344*4882a593Smuzhiyun if key.startswith('virtual:native:'): 345*4882a593Smuzhiyun pn = pn + '-native' 346*4882a593Smuzhiyun 347*4882a593Smuzhiyun hashfiles = {} 348*4882a593Smuzhiyun filedates = {} 349*4882a593Smuzhiyun 350*4882a593Smuzhiyun def get_hashval(siginfo): 351*4882a593Smuzhiyun if siginfo.endswith('.siginfo'): 352*4882a593Smuzhiyun return siginfo.rpartition(':')[2].partition('_')[0] 353*4882a593Smuzhiyun else: 354*4882a593Smuzhiyun return siginfo.rpartition('.')[2] 355*4882a593Smuzhiyun 356*4882a593Smuzhiyun # First search in stamps dir 357*4882a593Smuzhiyun localdata = d.createCopy() 358*4882a593Smuzhiyun localdata.setVar('MULTIMACH_TARGET_SYS', '*') 359*4882a593Smuzhiyun localdata.setVar('PN', pn) 360*4882a593Smuzhiyun localdata.setVar('PV', '*') 361*4882a593Smuzhiyun localdata.setVar('PR', '*') 362*4882a593Smuzhiyun localdata.setVar('EXTENDPE', '') 363*4882a593Smuzhiyun stamp = localdata.getVar('STAMP') 364*4882a593Smuzhiyun if pn.startswith("gcc-source"): 365*4882a593Smuzhiyun # gcc-source shared workdir is a special case :( 366*4882a593Smuzhiyun stamp = localdata.expand("${STAMPS_DIR}/work-shared/gcc-${PV}-${PR}") 367*4882a593Smuzhiyun 368*4882a593Smuzhiyun filespec = '%s.%s.sigdata.*' % (stamp, taskname) 369*4882a593Smuzhiyun foundall = False 370*4882a593Smuzhiyun import glob 371*4882a593Smuzhiyun for fullpath in glob.glob(filespec): 372*4882a593Smuzhiyun match = False 373*4882a593Smuzhiyun if taskhashlist: 374*4882a593Smuzhiyun for taskhash in taskhashlist: 375*4882a593Smuzhiyun if fullpath.endswith('.%s' % taskhash): 376*4882a593Smuzhiyun hashfiles[taskhash] = fullpath 377*4882a593Smuzhiyun if len(hashfiles) == len(taskhashlist): 378*4882a593Smuzhiyun foundall = True 379*4882a593Smuzhiyun break 380*4882a593Smuzhiyun else: 381*4882a593Smuzhiyun try: 382*4882a593Smuzhiyun filedates[fullpath] = os.stat(fullpath).st_mtime 383*4882a593Smuzhiyun except OSError: 384*4882a593Smuzhiyun continue 385*4882a593Smuzhiyun hashval = get_hashval(fullpath) 386*4882a593Smuzhiyun hashfiles[hashval] = fullpath 387*4882a593Smuzhiyun 388*4882a593Smuzhiyun if not taskhashlist or (len(filedates) < 2 and not foundall): 389*4882a593Smuzhiyun # That didn't work, look in sstate-cache 390*4882a593Smuzhiyun hashes = taskhashlist or ['?' * 64] 391*4882a593Smuzhiyun localdata = bb.data.createCopy(d) 392*4882a593Smuzhiyun for hashval in hashes: 393*4882a593Smuzhiyun localdata.setVar('PACKAGE_ARCH', '*') 394*4882a593Smuzhiyun localdata.setVar('TARGET_VENDOR', '*') 395*4882a593Smuzhiyun localdata.setVar('TARGET_OS', '*') 396*4882a593Smuzhiyun localdata.setVar('PN', pn) 397*4882a593Smuzhiyun localdata.setVar('PV', '*') 398*4882a593Smuzhiyun localdata.setVar('PR', '*') 399*4882a593Smuzhiyun localdata.setVar('BB_TASKHASH', hashval) 400*4882a593Smuzhiyun localdata.setVar('SSTATE_CURRTASK', taskname[3:]) 401*4882a593Smuzhiyun swspec = localdata.getVar('SSTATE_SWSPEC') 402*4882a593Smuzhiyun if taskname in ['do_fetch', 'do_unpack', 'do_patch', 'do_populate_lic', 'do_preconfigure'] and swspec: 403*4882a593Smuzhiyun localdata.setVar('SSTATE_PKGSPEC', '${SSTATE_SWSPEC}') 404*4882a593Smuzhiyun elif pn.endswith('-native') or "-cross-" in pn or "-crosssdk-" in pn: 405*4882a593Smuzhiyun localdata.setVar('SSTATE_EXTRAPATH', "${NATIVELSBSTRING}/") 406*4882a593Smuzhiyun filespec = '%s.siginfo' % localdata.getVar('SSTATE_PKG') 407*4882a593Smuzhiyun 408*4882a593Smuzhiyun matchedfiles = glob.glob(filespec) 409*4882a593Smuzhiyun for fullpath in matchedfiles: 410*4882a593Smuzhiyun actual_hashval = get_hashval(fullpath) 411*4882a593Smuzhiyun if actual_hashval in hashfiles: 412*4882a593Smuzhiyun continue 413*4882a593Smuzhiyun hashfiles[hashval] = fullpath 414*4882a593Smuzhiyun if not taskhashlist: 415*4882a593Smuzhiyun try: 416*4882a593Smuzhiyun filedates[fullpath] = os.stat(fullpath).st_mtime 417*4882a593Smuzhiyun except: 418*4882a593Smuzhiyun continue 419*4882a593Smuzhiyun 420*4882a593Smuzhiyun if taskhashlist: 421*4882a593Smuzhiyun return hashfiles 422*4882a593Smuzhiyun else: 423*4882a593Smuzhiyun return filedates 424*4882a593Smuzhiyun 425*4882a593Smuzhiyunbb.siggen.find_siginfo = find_siginfo 426*4882a593Smuzhiyun 427*4882a593Smuzhiyun 428*4882a593Smuzhiyundef sstate_get_manifest_filename(task, d): 429*4882a593Smuzhiyun """ 430*4882a593Smuzhiyun Return the sstate manifest file path for a particular task. 431*4882a593Smuzhiyun Also returns the datastore that can be used to query related variables. 432*4882a593Smuzhiyun """ 433*4882a593Smuzhiyun d2 = d.createCopy() 434*4882a593Smuzhiyun extrainf = d.getVarFlag("do_" + task, 'stamp-extra-info') 435*4882a593Smuzhiyun if extrainf: 436*4882a593Smuzhiyun d2.setVar("SSTATE_MANMACH", extrainf) 437*4882a593Smuzhiyun return (d2.expand("${SSTATE_MANFILEPREFIX}.%s" % task), d2) 438*4882a593Smuzhiyun 439*4882a593Smuzhiyundef find_sstate_manifest(taskdata, taskdata2, taskname, d, multilibcache): 440*4882a593Smuzhiyun d2 = d 441*4882a593Smuzhiyun variant = '' 442*4882a593Smuzhiyun curr_variant = '' 443*4882a593Smuzhiyun if d.getVar("BBEXTENDCURR") == "multilib": 444*4882a593Smuzhiyun curr_variant = d.getVar("BBEXTENDVARIANT") 445*4882a593Smuzhiyun if "virtclass-multilib" not in d.getVar("OVERRIDES"): 446*4882a593Smuzhiyun curr_variant = "invalid" 447*4882a593Smuzhiyun if taskdata2.startswith("virtual:multilib"): 448*4882a593Smuzhiyun variant = taskdata2.split(":")[2] 449*4882a593Smuzhiyun if curr_variant != variant: 450*4882a593Smuzhiyun if variant not in multilibcache: 451*4882a593Smuzhiyun multilibcache[variant] = oe.utils.get_multilib_datastore(variant, d) 452*4882a593Smuzhiyun d2 = multilibcache[variant] 453*4882a593Smuzhiyun 454*4882a593Smuzhiyun if taskdata.endswith("-native"): 455*4882a593Smuzhiyun pkgarchs = ["${BUILD_ARCH}", "${BUILD_ARCH}_${ORIGNATIVELSBSTRING}"] 456*4882a593Smuzhiyun elif taskdata.startswith("nativesdk-"): 457*4882a593Smuzhiyun pkgarchs = ["${SDK_ARCH}_${SDK_OS}", "allarch"] 458*4882a593Smuzhiyun elif "-cross-canadian" in taskdata: 459*4882a593Smuzhiyun pkgarchs = ["${SDK_ARCH}_${SDK_ARCH}-${SDKPKGSUFFIX}"] 460*4882a593Smuzhiyun elif "-cross-" in taskdata: 461*4882a593Smuzhiyun pkgarchs = ["${BUILD_ARCH}"] 462*4882a593Smuzhiyun elif "-crosssdk" in taskdata: 463*4882a593Smuzhiyun pkgarchs = ["${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS}"] 464*4882a593Smuzhiyun else: 465*4882a593Smuzhiyun pkgarchs = ['${MACHINE_ARCH}'] 466*4882a593Smuzhiyun pkgarchs = pkgarchs + list(reversed(d2.getVar("PACKAGE_EXTRA_ARCHS").split())) 467*4882a593Smuzhiyun pkgarchs.append('allarch') 468*4882a593Smuzhiyun pkgarchs.append('${SDK_ARCH}_${SDK_ARCH}-${SDKPKGSUFFIX}') 469*4882a593Smuzhiyun 470*4882a593Smuzhiyun searched_manifests = [] 471*4882a593Smuzhiyun 472*4882a593Smuzhiyun for pkgarch in pkgarchs: 473*4882a593Smuzhiyun manifest = d2.expand("${SSTATE_MANIFESTS}/manifest-%s-%s.%s" % (pkgarch, taskdata, taskname)) 474*4882a593Smuzhiyun if os.path.exists(manifest): 475*4882a593Smuzhiyun return manifest, d2 476*4882a593Smuzhiyun searched_manifests.append(manifest) 477*4882a593Smuzhiyun bb.fatal("The sstate manifest for task '%s:%s' (multilib variant '%s') could not be found.\nThe pkgarchs considered were: %s.\nBut none of these manifests exists:\n %s" 478*4882a593Smuzhiyun % (taskdata, taskname, variant, d2.expand(", ".join(pkgarchs)),"\n ".join(searched_manifests))) 479*4882a593Smuzhiyun return None, d2 480*4882a593Smuzhiyun 481*4882a593Smuzhiyundef OEOuthashBasic(path, sigfile, task, d): 482*4882a593Smuzhiyun """ 483*4882a593Smuzhiyun Basic output hash function 484*4882a593Smuzhiyun 485*4882a593Smuzhiyun Calculates the output hash of a task by hashing all output file metadata, 486*4882a593Smuzhiyun and file contents. 487*4882a593Smuzhiyun """ 488*4882a593Smuzhiyun import hashlib 489*4882a593Smuzhiyun import stat 490*4882a593Smuzhiyun import pwd 491*4882a593Smuzhiyun import grp 492*4882a593Smuzhiyun import re 493*4882a593Smuzhiyun import fnmatch 494*4882a593Smuzhiyun 495*4882a593Smuzhiyun def update_hash(s): 496*4882a593Smuzhiyun s = s.encode('utf-8') 497*4882a593Smuzhiyun h.update(s) 498*4882a593Smuzhiyun if sigfile: 499*4882a593Smuzhiyun sigfile.write(s) 500*4882a593Smuzhiyun 501*4882a593Smuzhiyun h = hashlib.sha256() 502*4882a593Smuzhiyun prev_dir = os.getcwd() 503*4882a593Smuzhiyun corebase = d.getVar("COREBASE") 504*4882a593Smuzhiyun tmpdir = d.getVar("TMPDIR") 505*4882a593Smuzhiyun include_owners = os.environ.get('PSEUDO_DISABLED') == '0' 506*4882a593Smuzhiyun if "package_write_" in task or task == "package_qa": 507*4882a593Smuzhiyun include_owners = False 508*4882a593Smuzhiyun include_timestamps = False 509*4882a593Smuzhiyun include_root = True 510*4882a593Smuzhiyun if task == "package": 511*4882a593Smuzhiyun include_timestamps = True 512*4882a593Smuzhiyun include_root = False 513*4882a593Smuzhiyun hash_version = d.getVar('HASHEQUIV_HASH_VERSION') 514*4882a593Smuzhiyun extra_sigdata = d.getVar("HASHEQUIV_EXTRA_SIGDATA") 515*4882a593Smuzhiyun 516*4882a593Smuzhiyun filemaps = {} 517*4882a593Smuzhiyun for m in (d.getVar('SSTATE_HASHEQUIV_FILEMAP') or '').split(): 518*4882a593Smuzhiyun entry = m.split(":") 519*4882a593Smuzhiyun if len(entry) != 3 or entry[0] != task: 520*4882a593Smuzhiyun continue 521*4882a593Smuzhiyun filemaps.setdefault(entry[1], []) 522*4882a593Smuzhiyun filemaps[entry[1]].append(entry[2]) 523*4882a593Smuzhiyun 524*4882a593Smuzhiyun try: 525*4882a593Smuzhiyun os.chdir(path) 526*4882a593Smuzhiyun basepath = os.path.normpath(path) 527*4882a593Smuzhiyun 528*4882a593Smuzhiyun update_hash("OEOuthashBasic\n") 529*4882a593Smuzhiyun if hash_version: 530*4882a593Smuzhiyun update_hash(hash_version + "\n") 531*4882a593Smuzhiyun 532*4882a593Smuzhiyun if extra_sigdata: 533*4882a593Smuzhiyun update_hash(extra_sigdata + "\n") 534*4882a593Smuzhiyun 535*4882a593Smuzhiyun # It is only currently useful to get equivalent hashes for things that 536*4882a593Smuzhiyun # can be restored from sstate. Since the sstate object is named using 537*4882a593Smuzhiyun # SSTATE_PKGSPEC and the task name, those should be included in the 538*4882a593Smuzhiyun # output hash calculation. 539*4882a593Smuzhiyun update_hash("SSTATE_PKGSPEC=%s\n" % d.getVar('SSTATE_PKGSPEC')) 540*4882a593Smuzhiyun update_hash("task=%s\n" % task) 541*4882a593Smuzhiyun 542*4882a593Smuzhiyun for root, dirs, files in os.walk('.', topdown=True): 543*4882a593Smuzhiyun # Sort directories to ensure consistent ordering when recursing 544*4882a593Smuzhiyun dirs.sort() 545*4882a593Smuzhiyun files.sort() 546*4882a593Smuzhiyun 547*4882a593Smuzhiyun def process(path): 548*4882a593Smuzhiyun s = os.lstat(path) 549*4882a593Smuzhiyun 550*4882a593Smuzhiyun if stat.S_ISDIR(s.st_mode): 551*4882a593Smuzhiyun update_hash('d') 552*4882a593Smuzhiyun elif stat.S_ISCHR(s.st_mode): 553*4882a593Smuzhiyun update_hash('c') 554*4882a593Smuzhiyun elif stat.S_ISBLK(s.st_mode): 555*4882a593Smuzhiyun update_hash('b') 556*4882a593Smuzhiyun elif stat.S_ISSOCK(s.st_mode): 557*4882a593Smuzhiyun update_hash('s') 558*4882a593Smuzhiyun elif stat.S_ISLNK(s.st_mode): 559*4882a593Smuzhiyun update_hash('l') 560*4882a593Smuzhiyun elif stat.S_ISFIFO(s.st_mode): 561*4882a593Smuzhiyun update_hash('p') 562*4882a593Smuzhiyun else: 563*4882a593Smuzhiyun update_hash('-') 564*4882a593Smuzhiyun 565*4882a593Smuzhiyun def add_perm(mask, on, off='-'): 566*4882a593Smuzhiyun if mask & s.st_mode: 567*4882a593Smuzhiyun update_hash(on) 568*4882a593Smuzhiyun else: 569*4882a593Smuzhiyun update_hash(off) 570*4882a593Smuzhiyun 571*4882a593Smuzhiyun add_perm(stat.S_IRUSR, 'r') 572*4882a593Smuzhiyun add_perm(stat.S_IWUSR, 'w') 573*4882a593Smuzhiyun if stat.S_ISUID & s.st_mode: 574*4882a593Smuzhiyun add_perm(stat.S_IXUSR, 's', 'S') 575*4882a593Smuzhiyun else: 576*4882a593Smuzhiyun add_perm(stat.S_IXUSR, 'x') 577*4882a593Smuzhiyun 578*4882a593Smuzhiyun if include_owners: 579*4882a593Smuzhiyun # Group/other permissions are only relevant in pseudo context 580*4882a593Smuzhiyun add_perm(stat.S_IRGRP, 'r') 581*4882a593Smuzhiyun add_perm(stat.S_IWGRP, 'w') 582*4882a593Smuzhiyun if stat.S_ISGID & s.st_mode: 583*4882a593Smuzhiyun add_perm(stat.S_IXGRP, 's', 'S') 584*4882a593Smuzhiyun else: 585*4882a593Smuzhiyun add_perm(stat.S_IXGRP, 'x') 586*4882a593Smuzhiyun 587*4882a593Smuzhiyun add_perm(stat.S_IROTH, 'r') 588*4882a593Smuzhiyun add_perm(stat.S_IWOTH, 'w') 589*4882a593Smuzhiyun if stat.S_ISVTX & s.st_mode: 590*4882a593Smuzhiyun update_hash('t') 591*4882a593Smuzhiyun else: 592*4882a593Smuzhiyun add_perm(stat.S_IXOTH, 'x') 593*4882a593Smuzhiyun 594*4882a593Smuzhiyun try: 595*4882a593Smuzhiyun update_hash(" %10s" % pwd.getpwuid(s.st_uid).pw_name) 596*4882a593Smuzhiyun update_hash(" %10s" % grp.getgrgid(s.st_gid).gr_name) 597*4882a593Smuzhiyun except KeyError as e: 598*4882a593Smuzhiyun bb.warn("KeyError in %s" % path) 599*4882a593Smuzhiyun msg = ("KeyError: %s\nPath %s is owned by uid %d, gid %d, which doesn't match " 600*4882a593Smuzhiyun "any user/group on target. This may be due to host contamination." % (e, path, s.st_uid, s.st_gid)) 601*4882a593Smuzhiyun raise Exception(msg).with_traceback(e.__traceback__) 602*4882a593Smuzhiyun 603*4882a593Smuzhiyun if include_timestamps: 604*4882a593Smuzhiyun update_hash(" %10d" % s.st_mtime) 605*4882a593Smuzhiyun 606*4882a593Smuzhiyun update_hash(" ") 607*4882a593Smuzhiyun if stat.S_ISBLK(s.st_mode) or stat.S_ISCHR(s.st_mode): 608*4882a593Smuzhiyun update_hash("%9s" % ("%d.%d" % (os.major(s.st_rdev), os.minor(s.st_rdev)))) 609*4882a593Smuzhiyun else: 610*4882a593Smuzhiyun update_hash(" " * 9) 611*4882a593Smuzhiyun 612*4882a593Smuzhiyun filterfile = False 613*4882a593Smuzhiyun for entry in filemaps: 614*4882a593Smuzhiyun if fnmatch.fnmatch(path, entry): 615*4882a593Smuzhiyun filterfile = True 616*4882a593Smuzhiyun 617*4882a593Smuzhiyun update_hash(" ") 618*4882a593Smuzhiyun if stat.S_ISREG(s.st_mode) and not filterfile: 619*4882a593Smuzhiyun update_hash("%10d" % s.st_size) 620*4882a593Smuzhiyun else: 621*4882a593Smuzhiyun update_hash(" " * 10) 622*4882a593Smuzhiyun 623*4882a593Smuzhiyun update_hash(" ") 624*4882a593Smuzhiyun fh = hashlib.sha256() 625*4882a593Smuzhiyun if stat.S_ISREG(s.st_mode): 626*4882a593Smuzhiyun # Hash file contents 627*4882a593Smuzhiyun if filterfile: 628*4882a593Smuzhiyun # Need to ignore paths in crossscripts and postinst-useradd files. 629*4882a593Smuzhiyun with open(path, 'rb') as d: 630*4882a593Smuzhiyun chunk = d.read() 631*4882a593Smuzhiyun chunk = chunk.replace(bytes(basepath, encoding='utf8'), b'') 632*4882a593Smuzhiyun for entry in filemaps: 633*4882a593Smuzhiyun if not fnmatch.fnmatch(path, entry): 634*4882a593Smuzhiyun continue 635*4882a593Smuzhiyun for r in filemaps[entry]: 636*4882a593Smuzhiyun if r.startswith("regex-"): 637*4882a593Smuzhiyun chunk = re.sub(bytes(r[6:], encoding='utf8'), b'', chunk) 638*4882a593Smuzhiyun else: 639*4882a593Smuzhiyun chunk = chunk.replace(bytes(r, encoding='utf8'), b'') 640*4882a593Smuzhiyun fh.update(chunk) 641*4882a593Smuzhiyun else: 642*4882a593Smuzhiyun with open(path, 'rb') as d: 643*4882a593Smuzhiyun for chunk in iter(lambda: d.read(4096), b""): 644*4882a593Smuzhiyun fh.update(chunk) 645*4882a593Smuzhiyun update_hash(fh.hexdigest()) 646*4882a593Smuzhiyun else: 647*4882a593Smuzhiyun update_hash(" " * len(fh.hexdigest())) 648*4882a593Smuzhiyun 649*4882a593Smuzhiyun update_hash(" %s" % path) 650*4882a593Smuzhiyun 651*4882a593Smuzhiyun if stat.S_ISLNK(s.st_mode): 652*4882a593Smuzhiyun update_hash(" -> %s" % os.readlink(path)) 653*4882a593Smuzhiyun 654*4882a593Smuzhiyun update_hash("\n") 655*4882a593Smuzhiyun 656*4882a593Smuzhiyun # Process this directory and all its child files 657*4882a593Smuzhiyun if include_root or root != ".": 658*4882a593Smuzhiyun process(root) 659*4882a593Smuzhiyun for f in files: 660*4882a593Smuzhiyun if f == 'fixmepath': 661*4882a593Smuzhiyun continue 662*4882a593Smuzhiyun process(os.path.join(root, f)) 663*4882a593Smuzhiyun 664*4882a593Smuzhiyun for dir in dirs: 665*4882a593Smuzhiyun if os.path.islink(os.path.join(root, dir)): 666*4882a593Smuzhiyun process(os.path.join(root, dir)) 667*4882a593Smuzhiyun finally: 668*4882a593Smuzhiyun os.chdir(prev_dir) 669*4882a593Smuzhiyun 670*4882a593Smuzhiyun return h.hexdigest() 671*4882a593Smuzhiyun 672*4882a593Smuzhiyun 673