sstate.bbclass 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  1. #
  2. # Copyright OpenEmbedded Contributors
  3. #
  4. # SPDX-License-Identifier: MIT
  5. #
  6. SSTATE_VERSION = "14"
  7. SSTATE_ZSTD_CLEVEL ??= "8"
  8. SSTATE_MANIFESTS ?= "${TMPDIR}/sstate-control"
  9. SSTATE_MANFILEPREFIX = "${SSTATE_MANIFESTS}/manifest-${SSTATE_MANMACH}-${PN}"
  10. def generate_sstatefn(spec, hash, taskname, siginfo, d):
  11. if taskname is None:
  12. return ""
  13. extension = ".tar.zst"
  14. # 8 chars reserved for siginfo
  15. limit = 254 - 8
  16. if siginfo:
  17. limit = 254
  18. extension = ".tar.zst.siginfo"
  19. if not hash:
  20. hash = "INVALID"
  21. fn = spec + hash + "_" + taskname + extension
  22. # If the filename is too long, attempt to reduce it
  23. if len(fn) > limit:
  24. components = spec.split(":")
  25. # Fields 0,5,6 are mandatory, 1 is most useful, 2,3,4 are just for information
  26. # 7 is for the separators
  27. avail = (limit - len(hash + "_" + taskname + extension) - len(components[0]) - len(components[1]) - len(components[5]) - len(components[6]) - 7) // 3
  28. components[2] = components[2][:avail]
  29. components[3] = components[3][:avail]
  30. components[4] = components[4][:avail]
  31. spec = ":".join(components)
  32. fn = spec + hash + "_" + taskname + extension
  33. if len(fn) > limit:
  34. bb.fatal("Unable to reduce sstate name to less than 255 chararacters")
  35. return hash[:2] + "/" + hash[2:4] + "/" + fn
  36. SSTATE_PKGARCH = "${PACKAGE_ARCH}"
  37. SSTATE_PKGSPEC = "sstate:${PN}:${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}:${PV}:${PR}:${SSTATE_PKGARCH}:${SSTATE_VERSION}:"
  38. SSTATE_SWSPEC = "sstate:${PN}::${PV}:${PR}::${SSTATE_VERSION}:"
  39. SSTATE_PKGNAME = "${SSTATE_EXTRAPATH}${@generate_sstatefn(d.getVar('SSTATE_PKGSPEC'), d.getVar('BB_UNIHASH'), d.getVar('SSTATE_CURRTASK'), False, d)}"
  40. SSTATE_PKG = "${SSTATE_DIR}/${SSTATE_PKGNAME}"
  41. SSTATE_EXTRAPATH = ""
  42. SSTATE_EXTRAPATHWILDCARD = ""
  43. SSTATE_PATHSPEC = "${SSTATE_DIR}/${SSTATE_EXTRAPATHWILDCARD}*/*/${SSTATE_PKGSPEC}*_${SSTATE_PATH_CURRTASK}.tar.zst*"
  44. # explicitly make PV to depend on evaluated value of PV variable
  45. PV[vardepvalue] = "${PV}"
  46. # We don't want the sstate to depend on things like the distro string
  47. # of the system, we let the sstate paths take care of this.
  48. SSTATE_EXTRAPATH[vardepvalue] = ""
  49. SSTATE_EXTRAPATHWILDCARD[vardepvalue] = ""
  50. # Avoid docbook/sgml catalog warnings for now
  51. SSTATE_ALLOW_OVERLAP_FILES += "${STAGING_ETCDIR_NATIVE}/sgml ${STAGING_DATADIR_NATIVE}/sgml"
  52. # sdk-provides-dummy-nativesdk and nativesdk-buildtools-perl-dummy overlap for different SDKMACHINE
  53. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_RPM}/sdk_provides_dummy_nativesdk/ ${DEPLOY_DIR_IPK}/sdk-provides-dummy-nativesdk/"
  54. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_RPM}/buildtools_dummy_nativesdk/ ${DEPLOY_DIR_IPK}/buildtools-dummy-nativesdk/"
  55. # target-sdk-provides-dummy overlaps that allarch is disabled when multilib is used
  56. SSTATE_ALLOW_OVERLAP_FILES += "${COMPONENTS_DIR}/sdk-provides-dummy-target/ ${DEPLOY_DIR_RPM}/sdk_provides_dummy_target/ ${DEPLOY_DIR_IPK}/sdk-provides-dummy-target/"
  57. # Archive the sources for many architectures in one deploy folder
  58. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_SRC}"
  59. # ovmf/grub-efi/systemd-boot/intel-microcode multilib recipes can generate identical overlapping files
  60. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_IMAGE}/ovmf"
  61. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_IMAGE}/grub-efi"
  62. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_IMAGE}/systemd-boot"
  63. SSTATE_ALLOW_OVERLAP_FILES += "${DEPLOY_DIR_IMAGE}/microcode"
  64. SSTATE_SCAN_FILES ?= "*.la *-config *_config postinst-*"
  65. SSTATE_SCAN_CMD ??= 'find ${SSTATE_BUILDDIR} \( -name "${@"\" -o -name \"".join(d.getVar("SSTATE_SCAN_FILES").split())}" \) -type f'
  66. SSTATE_SCAN_CMD_NATIVE ??= 'grep -Irl -e ${RECIPE_SYSROOT} -e ${RECIPE_SYSROOT_NATIVE} -e ${HOSTTOOLS_DIR} ${SSTATE_BUILDDIR}'
  67. SSTATE_HASHEQUIV_FILEMAP ?= " \
  68. populate_sysroot:*/postinst-useradd-*:${TMPDIR} \
  69. populate_sysroot:*/postinst-useradd-*:${COREBASE} \
  70. populate_sysroot:*/postinst-useradd-*:regex-\s(PATH|PSEUDO_INCLUDE_PATHS|HOME|LOGNAME|OMP_NUM_THREADS|USER)=.*\s \
  71. populate_sysroot:*/crossscripts/*:${TMPDIR} \
  72. populate_sysroot:*/crossscripts/*:${COREBASE} \
  73. "
  74. BB_HASHFILENAME = "False ${SSTATE_PKGSPEC} ${SSTATE_SWSPEC}"
  75. SSTATE_ARCHS_TUNEPKG ??= "${TUNE_PKGARCH}"
  76. SSTATE_ARCHS = " \
  77. ${BUILD_ARCH} \
  78. ${BUILD_ARCH}_${ORIGNATIVELSBSTRING} \
  79. ${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS} \
  80. ${SDK_ARCH}_${SDK_OS} \
  81. ${SDK_ARCH}_${SDK_ARCH}-${SDKPKGSUFFIX} \
  82. allarch \
  83. ${SSTATE_ARCHS_TUNEPKG} \
  84. ${PACKAGE_EXTRA_ARCHS} \
  85. ${MACHINE_ARCH}"
  86. SSTATE_ARCHS[vardepsexclude] = "ORIGNATIVELSBSTRING"
  87. SSTATE_MANMACH ?= "${SSTATE_PKGARCH}"
  88. SSTATECREATEFUNCS += "sstate_hardcode_path"
  89. SSTATECREATEFUNCS[vardeps] = "SSTATE_SCAN_FILES"
  90. SSTATEPOSTCREATEFUNCS = ""
  91. SSTATEPREINSTFUNCS = ""
  92. SSTATEPOSTUNPACKFUNCS = "sstate_hardcode_path_unpack"
  93. EXTRA_STAGING_FIXMES ?= "HOSTTOOLS_DIR"
  94. # Check whether sstate exists for tasks that support sstate and are in the
  95. # locked signatures file.
  96. SIGGEN_LOCKEDSIGS_SSTATE_EXISTS_CHECK ?= 'error'
  97. # Check whether the task's computed hash matches the task's hash in the
  98. # locked signatures file.
  99. SIGGEN_LOCKEDSIGS_TASKSIG_CHECK ?= "error"
  100. # The GnuPG key ID and passphrase to use to sign sstate archives (or unset to
  101. # not sign)
  102. SSTATE_SIG_KEY ?= ""
  103. SSTATE_SIG_PASSPHRASE ?= ""
  104. # Whether to verify the GnUPG signatures when extracting sstate archives
  105. SSTATE_VERIFY_SIG ?= "0"
  106. # List of signatures to consider valid.
  107. SSTATE_VALID_SIGS ??= ""
  108. SSTATE_VALID_SIGS[vardepvalue] = ""
  109. SSTATE_HASHEQUIV_METHOD ?= "oe.sstatesig.OEOuthashBasic"
  110. SSTATE_HASHEQUIV_METHOD[doc] = "The fully-qualified function used to calculate \
  111. the output hash for a task, which in turn is used to determine equivalency. \
  112. "
  113. SSTATE_HASHEQUIV_REPORT_TASKDATA ?= "0"
  114. SSTATE_HASHEQUIV_REPORT_TASKDATA[doc] = "Report additional useful data to the \
  115. hash equivalency server, such as PN, PV, taskname, etc. This information \
  116. is very useful for developers looking at task data, but may leak sensitive \
  117. data if the equivalence server is public. \
  118. "
  119. python () {
  120. if bb.data.inherits_class('native', d):
  121. d.setVar('SSTATE_PKGARCH', d.getVar('BUILD_ARCH', False))
  122. elif bb.data.inherits_class('crosssdk', d):
  123. d.setVar('SSTATE_PKGARCH', d.expand("${BUILD_ARCH}_${SDK_ARCH}_${SDK_OS}"))
  124. elif bb.data.inherits_class('cross', d):
  125. d.setVar('SSTATE_PKGARCH', d.expand("${BUILD_ARCH}"))
  126. elif bb.data.inherits_class('nativesdk', d):
  127. d.setVar('SSTATE_PKGARCH', d.expand("${SDK_ARCH}_${SDK_OS}"))
  128. elif bb.data.inherits_class('cross-canadian', d):
  129. d.setVar('SSTATE_PKGARCH', d.expand("${SDK_ARCH}_${PACKAGE_ARCH}"))
  130. elif bb.data.inherits_class('allarch', d) and d.getVar("PACKAGE_ARCH") == "all":
  131. d.setVar('SSTATE_PKGARCH', "allarch")
  132. else:
  133. d.setVar('SSTATE_MANMACH', d.expand("${PACKAGE_ARCH}"))
  134. if bb.data.inherits_class('native', d) or bb.data.inherits_class('crosssdk', d) or bb.data.inherits_class('cross', d):
  135. d.setVar('SSTATE_EXTRAPATH', "${NATIVELSBSTRING}/")
  136. d.setVar('BB_HASHFILENAME', "True ${SSTATE_PKGSPEC} ${SSTATE_SWSPEC}")
  137. d.setVar('SSTATE_EXTRAPATHWILDCARD', "${NATIVELSBSTRING}/")
  138. unique_tasks = sorted(set((d.getVar('SSTATETASKS') or "").split()))
  139. d.setVar('SSTATETASKS', " ".join(unique_tasks))
  140. for task in unique_tasks:
  141. d.prependVarFlag(task, 'prefuncs', "sstate_task_prefunc ")
  142. # Generally sstate should be last, execpt for buildhistory functions
  143. postfuncs = (d.getVarFlag(task, 'postfuncs') or "").split()
  144. newpostfuncs = [p for p in postfuncs if "buildhistory" not in p] + ["sstate_task_postfunc"] + [p for p in postfuncs if "buildhistory" in p]
  145. d.setVarFlag(task, 'postfuncs', " ".join(newpostfuncs))
  146. d.setVarFlag(task, 'network', '1')
  147. d.setVarFlag(task + "_setscene", 'network', '1')
  148. }
  149. def sstate_init(task, d):
  150. ss = {}
  151. ss['task'] = task
  152. ss['dirs'] = []
  153. ss['plaindirs'] = []
  154. ss['lockfiles'] = []
  155. ss['lockfiles-shared'] = []
  156. return ss
  157. def sstate_state_fromvars(d, task = None):
  158. if task is None:
  159. task = d.getVar('BB_CURRENTTASK')
  160. if not task:
  161. bb.fatal("sstate code running without task context?!")
  162. task = task.replace("_setscene", "")
  163. if task.startswith("do_"):
  164. task = task[3:]
  165. inputs = (d.getVarFlag("do_" + task, 'sstate-inputdirs') or "").split()
  166. outputs = (d.getVarFlag("do_" + task, 'sstate-outputdirs') or "").split()
  167. plaindirs = (d.getVarFlag("do_" + task, 'sstate-plaindirs') or "").split()
  168. lockfiles = (d.getVarFlag("do_" + task, 'sstate-lockfile') or "").split()
  169. lockfilesshared = (d.getVarFlag("do_" + task, 'sstate-lockfile-shared') or "").split()
  170. fixmedir = d.getVarFlag("do_" + task, 'sstate-fixmedir') or ""
  171. if not task or len(inputs) != len(outputs):
  172. bb.fatal("sstate variables not setup correctly?!")
  173. if task == "populate_lic":
  174. d.setVar("SSTATE_PKGSPEC", "${SSTATE_SWSPEC}")
  175. d.setVar("SSTATE_EXTRAPATH", "")
  176. d.setVar('SSTATE_EXTRAPATHWILDCARD', "")
  177. ss = sstate_init(task, d)
  178. for i in range(len(inputs)):
  179. sstate_add(ss, inputs[i], outputs[i], d)
  180. ss['lockfiles'] = lockfiles
  181. ss['lockfiles-shared'] = lockfilesshared
  182. ss['plaindirs'] = plaindirs
  183. ss['fixmedir'] = fixmedir
  184. return ss
  185. def sstate_add(ss, source, dest, d):
  186. if not source.endswith("/"):
  187. source = source + "/"
  188. if not dest.endswith("/"):
  189. dest = dest + "/"
  190. source = os.path.normpath(source)
  191. dest = os.path.normpath(dest)
  192. srcbase = os.path.basename(source)
  193. ss['dirs'].append([srcbase, source, dest])
  194. return ss
  195. def sstate_install(ss, d):
  196. import oe.path
  197. import oe.sstatesig
  198. import subprocess
  199. def prepdir(dir):
  200. # remove dir if it exists, ensure any parent directories do exist
  201. if os.path.exists(dir):
  202. oe.path.remove(dir)
  203. bb.utils.mkdirhier(dir)
  204. oe.path.remove(dir)
  205. sstateinst = d.getVar("SSTATE_INSTDIR")
  206. for state in ss['dirs']:
  207. prepdir(state[1])
  208. bb.utils.rename(sstateinst + state[0], state[1])
  209. sharedfiles = []
  210. shareddirs = []
  211. bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
  212. manifest, d2 = oe.sstatesig.sstate_get_manifest_filename(ss['task'], d)
  213. if os.access(manifest, os.R_OK):
  214. bb.fatal("Package already staged (%s)?!" % manifest)
  215. d.setVar("SSTATE_INST_POSTRM", manifest + ".postrm")
  216. locks = []
  217. for lock in ss['lockfiles-shared']:
  218. locks.append(bb.utils.lockfile(lock, True))
  219. for lock in ss['lockfiles']:
  220. locks.append(bb.utils.lockfile(lock))
  221. for state in ss['dirs']:
  222. bb.debug(2, "Staging files from %s to %s" % (state[1], state[2]))
  223. for walkroot, dirs, files in os.walk(state[1]):
  224. for file in files:
  225. srcpath = os.path.join(walkroot, file)
  226. dstpath = srcpath.replace(state[1], state[2])
  227. #bb.debug(2, "Staging %s to %s" % (srcpath, dstpath))
  228. sharedfiles.append(dstpath)
  229. for dir in dirs:
  230. srcdir = os.path.join(walkroot, dir)
  231. dstdir = srcdir.replace(state[1], state[2])
  232. #bb.debug(2, "Staging %s to %s" % (srcdir, dstdir))
  233. if os.path.islink(srcdir):
  234. sharedfiles.append(dstdir)
  235. continue
  236. if not dstdir.endswith("/"):
  237. dstdir = dstdir + "/"
  238. shareddirs.append(dstdir)
  239. # Check the file list for conflicts against files which already exist
  240. overlap_allowed = (d.getVar("SSTATE_ALLOW_OVERLAP_FILES") or "").split()
  241. match = []
  242. for f in sharedfiles:
  243. if os.path.exists(f):
  244. f = os.path.normpath(f)
  245. realmatch = True
  246. for w in overlap_allowed:
  247. w = os.path.normpath(w)
  248. if f.startswith(w):
  249. realmatch = False
  250. break
  251. if realmatch:
  252. match.append(f)
  253. sstate_search_cmd = "grep -rlF '%s' %s --exclude=index-* | sed -e 's:^.*/::'" % (f, d.expand("${SSTATE_MANIFESTS}"))
  254. search_output = subprocess.Popen(sstate_search_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0]
  255. if search_output:
  256. match.append(" (matched in %s)" % search_output.decode('utf-8').rstrip())
  257. else:
  258. match.append(" (not matched to any task)")
  259. if match:
  260. bb.fatal("Recipe %s is trying to install files into a shared " \
  261. "area when those files already exist. The files and the manifests listing " \
  262. "them are:\n %s\n"
  263. "Please adjust the recipes so only one recipe provides a given file. " % \
  264. (d.getVar('PN'), "\n ".join(match)))
  265. if ss['fixmedir'] and os.path.exists(ss['fixmedir'] + "/fixmepath.cmd"):
  266. sharedfiles.append(ss['fixmedir'] + "/fixmepath.cmd")
  267. sharedfiles.append(ss['fixmedir'] + "/fixmepath")
  268. # Write out the manifest
  269. with open(manifest, "w") as f:
  270. for file in sharedfiles:
  271. f.write(file + "\n")
  272. # We want to ensure that directories appear at the end of the manifest
  273. # so that when we test to see if they should be deleted any contents
  274. # added by the task will have been removed first.
  275. dirs = sorted(shareddirs, key=len)
  276. # Must remove children first, which will have a longer path than the parent
  277. for di in reversed(dirs):
  278. f.write(di + "\n")
  279. # Append to the list of manifests for this PACKAGE_ARCH
  280. i = d2.expand("${SSTATE_MANIFESTS}/index-${SSTATE_MANMACH}")
  281. l = bb.utils.lockfile(i + ".lock")
  282. filedata = d.getVar("STAMP") + " " + d2.getVar("SSTATE_MANFILEPREFIX") + " " + d.getVar("WORKDIR") + "\n"
  283. manifests = []
  284. if os.path.exists(i):
  285. with open(i, "r") as f:
  286. manifests = f.readlines()
  287. # We append new entries, we don't remove older entries which may have the same
  288. # manifest name but different versions from stamp/workdir. See below.
  289. if filedata not in manifests:
  290. with open(i, "a+") as f:
  291. f.write(filedata)
  292. bb.utils.unlockfile(l)
  293. # Run the actual file install
  294. for state in ss['dirs']:
  295. if os.path.exists(state[1]):
  296. oe.path.copyhardlinktree(state[1], state[2])
  297. for plain in ss['plaindirs']:
  298. workdir = d.getVar('WORKDIR')
  299. sharedworkdir = os.path.join(d.getVar('TMPDIR'), "work-shared")
  300. src = sstateinst + "/" + plain.replace(workdir, '')
  301. if sharedworkdir in plain:
  302. src = sstateinst + "/" + plain.replace(sharedworkdir, '')
  303. dest = plain
  304. bb.utils.mkdirhier(src)
  305. prepdir(dest)
  306. bb.utils.rename(src, dest)
  307. for lock in locks:
  308. bb.utils.unlockfile(lock)
  309. sstate_install[vardepsexclude] += "SSTATE_ALLOW_OVERLAP_FILES SSTATE_MANMACH SSTATE_MANFILEPREFIX STAMP"
  310. def sstate_installpkg(ss, d):
  311. from oe.gpg_sign import get_signer
  312. sstateinst = d.expand("${WORKDIR}/sstate-install-%s/" % ss['task'])
  313. d.setVar("SSTATE_CURRTASK", ss['task'])
  314. sstatefetch = d.getVar('SSTATE_PKGNAME')
  315. sstatepkg = d.getVar('SSTATE_PKG')
  316. verify_sig = bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG"), False)
  317. if not os.path.exists(sstatepkg) or (verify_sig and not os.path.exists(sstatepkg + '.sig')):
  318. pstaging_fetch(sstatefetch, d)
  319. if not os.path.isfile(sstatepkg):
  320. bb.note("Sstate package %s does not exist" % sstatepkg)
  321. return False
  322. sstate_clean(ss, d)
  323. d.setVar('SSTATE_INSTDIR', sstateinst)
  324. if verify_sig:
  325. if not os.path.isfile(sstatepkg + '.sig'):
  326. bb.warn("No signature file for sstate package %s, skipping acceleration..." % sstatepkg)
  327. return False
  328. signer = get_signer(d, 'local')
  329. if not signer.verify(sstatepkg + '.sig', d.getVar("SSTATE_VALID_SIGS")):
  330. bb.warn("Cannot verify signature on sstate package %s, skipping acceleration..." % sstatepkg)
  331. return False
  332. # Empty sstateinst directory, ensure its clean
  333. if os.path.exists(sstateinst):
  334. oe.path.remove(sstateinst)
  335. bb.utils.mkdirhier(sstateinst)
  336. sstateinst = d.getVar("SSTATE_INSTDIR")
  337. d.setVar('SSTATE_FIXMEDIR', ss['fixmedir'])
  338. for f in (d.getVar('SSTATEPREINSTFUNCS') or '').split() + ['sstate_unpack_package']:
  339. # All hooks should run in the SSTATE_INSTDIR
  340. bb.build.exec_func(f, d, (sstateinst,))
  341. return sstate_installpkgdir(ss, d)
  342. def sstate_installpkgdir(ss, d):
  343. import oe.path
  344. import subprocess
  345. sstateinst = d.getVar("SSTATE_INSTDIR")
  346. d.setVar('SSTATE_FIXMEDIR', ss['fixmedir'])
  347. for f in (d.getVar('SSTATEPOSTUNPACKFUNCS') or '').split():
  348. # All hooks should run in the SSTATE_INSTDIR
  349. bb.build.exec_func(f, d, (sstateinst,))
  350. sstate_install(ss, d)
  351. return True
  352. python sstate_hardcode_path_unpack () {
  353. # Fixup hardcoded paths
  354. #
  355. # Note: The logic below must match the reverse logic in
  356. # sstate_hardcode_path(d)
  357. import subprocess
  358. sstateinst = d.getVar('SSTATE_INSTDIR')
  359. sstatefixmedir = d.getVar('SSTATE_FIXMEDIR')
  360. fixmefn = sstateinst + "fixmepath"
  361. if os.path.isfile(fixmefn):
  362. staging_target = d.getVar('RECIPE_SYSROOT')
  363. staging_host = d.getVar('RECIPE_SYSROOT_NATIVE')
  364. if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross-canadian', d):
  365. sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRHOST:%s:g'" % (staging_host)
  366. elif bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d):
  367. sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g'" % (staging_target, staging_host)
  368. else:
  369. sstate_sed_cmd = "sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g'" % (staging_target)
  370. extra_staging_fixmes = d.getVar('EXTRA_STAGING_FIXMES') or ''
  371. for fixmevar in extra_staging_fixmes.split():
  372. fixme_path = d.getVar(fixmevar)
  373. sstate_sed_cmd += " -e 's:FIXME_%s:%s:g'" % (fixmevar, fixme_path)
  374. # Add sstateinst to each filename in fixmepath, use xargs to efficiently call sed
  375. sstate_hardcode_cmd = "sed -e 's:^:%s:g' %s | xargs %s" % (sstateinst, fixmefn, sstate_sed_cmd)
  376. # Defer do_populate_sysroot relocation command
  377. if sstatefixmedir:
  378. bb.utils.mkdirhier(sstatefixmedir)
  379. with open(sstatefixmedir + "/fixmepath.cmd", "w") as f:
  380. sstate_hardcode_cmd = sstate_hardcode_cmd.replace(fixmefn, sstatefixmedir + "/fixmepath")
  381. sstate_hardcode_cmd = sstate_hardcode_cmd.replace(sstateinst, "FIXMEFINALSSTATEINST")
  382. sstate_hardcode_cmd = sstate_hardcode_cmd.replace(staging_host, "FIXMEFINALSSTATEHOST")
  383. sstate_hardcode_cmd = sstate_hardcode_cmd.replace(staging_target, "FIXMEFINALSSTATETARGET")
  384. f.write(sstate_hardcode_cmd)
  385. bb.utils.copyfile(fixmefn, sstatefixmedir + "/fixmepath")
  386. return
  387. bb.note("Replacing fixme paths in sstate package: %s" % (sstate_hardcode_cmd))
  388. subprocess.check_call(sstate_hardcode_cmd, shell=True)
  389. # Need to remove this or we'd copy it into the target directory and may
  390. # conflict with another writer
  391. os.remove(fixmefn)
  392. }
  393. def sstate_clean_cachefile(ss, d):
  394. import oe.path
  395. if d.getVarFlag('do_%s' % ss['task'], 'task'):
  396. d.setVar("SSTATE_PATH_CURRTASK", ss['task'])
  397. sstatepkgfile = d.getVar('SSTATE_PATHSPEC')
  398. bb.note("Removing %s" % sstatepkgfile)
  399. oe.path.remove(sstatepkgfile)
  400. def sstate_clean_cachefiles(d):
  401. for task in (d.getVar('SSTATETASKS') or "").split():
  402. ld = d.createCopy()
  403. ss = sstate_state_fromvars(ld, task)
  404. sstate_clean_cachefile(ss, ld)
  405. def sstate_clean_manifest(manifest, d, canrace=False, prefix=None):
  406. import oe.path
  407. with open(manifest) as mfile:
  408. entries = mfile.readlines()
  409. for entry in entries:
  410. entry = entry.strip()
  411. if prefix and not entry.startswith("/"):
  412. entry = prefix + "/" + entry
  413. bb.debug(2, "Removing manifest: %s" % entry)
  414. # We can race against another package populating directories as we're removing them
  415. # so we ignore errors here.
  416. try:
  417. if entry.endswith("/"):
  418. if os.path.islink(entry[:-1]):
  419. os.remove(entry[:-1])
  420. elif os.path.exists(entry) and len(os.listdir(entry)) == 0 and not canrace:
  421. # Removing directories whilst builds are in progress exposes a race. Only
  422. # do it in contexts where it is safe to do so.
  423. os.rmdir(entry[:-1])
  424. else:
  425. os.remove(entry)
  426. except OSError:
  427. pass
  428. postrm = manifest + ".postrm"
  429. if os.path.exists(manifest + ".postrm"):
  430. import subprocess
  431. os.chmod(postrm, 0o755)
  432. subprocess.check_call(postrm, shell=True)
  433. oe.path.remove(postrm)
  434. oe.path.remove(manifest)
  435. def sstate_clean(ss, d):
  436. import oe.path
  437. import glob
  438. d2 = d.createCopy()
  439. stamp_clean = d.getVar("STAMPCLEAN")
  440. extrainf = d.getVarFlag("do_" + ss['task'], 'stamp-extra-info')
  441. if extrainf:
  442. d2.setVar("SSTATE_MANMACH", extrainf)
  443. wildcard_stfile = "%s.do_%s*.%s" % (stamp_clean, ss['task'], extrainf)
  444. else:
  445. wildcard_stfile = "%s.do_%s*" % (stamp_clean, ss['task'])
  446. manifest = d2.expand("${SSTATE_MANFILEPREFIX}.%s" % ss['task'])
  447. if os.path.exists(manifest):
  448. locks = []
  449. for lock in ss['lockfiles-shared']:
  450. locks.append(bb.utils.lockfile(lock))
  451. for lock in ss['lockfiles']:
  452. locks.append(bb.utils.lockfile(lock))
  453. sstate_clean_manifest(manifest, d, canrace=True)
  454. for lock in locks:
  455. bb.utils.unlockfile(lock)
  456. # Remove the current and previous stamps, but keep the sigdata.
  457. #
  458. # The glob() matches do_task* which may match multiple tasks, for
  459. # example: do_package and do_package_write_ipk, so we need to
  460. # exactly match *.do_task.* and *.do_task_setscene.*
  461. rm_stamp = '.do_%s.' % ss['task']
  462. rm_setscene = '.do_%s_setscene.' % ss['task']
  463. # For BB_SIGNATURE_HANDLER = "noop"
  464. rm_nohash = ".do_%s" % ss['task']
  465. for stfile in glob.glob(wildcard_stfile):
  466. # Keep the sigdata
  467. if ".sigdata." in stfile or ".sigbasedata." in stfile:
  468. continue
  469. # Preserve taint files in the stamps directory
  470. if stfile.endswith('.taint'):
  471. continue
  472. if rm_stamp in stfile or rm_setscene in stfile or \
  473. stfile.endswith(rm_nohash):
  474. oe.path.remove(stfile)
  475. sstate_clean[vardepsexclude] = "SSTATE_MANFILEPREFIX"
  476. CLEANFUNCS += "sstate_cleanall"
  477. python sstate_cleanall() {
  478. bb.note("Removing shared state for package %s" % d.getVar('PN'))
  479. manifest_dir = d.getVar('SSTATE_MANIFESTS')
  480. if not os.path.exists(manifest_dir):
  481. return
  482. tasks = d.getVar('SSTATETASKS').split()
  483. for name in tasks:
  484. ld = d.createCopy()
  485. shared_state = sstate_state_fromvars(ld, name)
  486. sstate_clean(shared_state, ld)
  487. }
  488. python sstate_hardcode_path () {
  489. import subprocess, platform
  490. # Need to remove hardcoded paths and fix these when we install the
  491. # staging packages.
  492. #
  493. # Note: the logic in this function needs to match the reverse logic
  494. # in sstate_installpkg(ss, d)
  495. staging_target = d.getVar('RECIPE_SYSROOT')
  496. staging_host = d.getVar('RECIPE_SYSROOT_NATIVE')
  497. sstate_builddir = d.getVar('SSTATE_BUILDDIR')
  498. sstate_sed_cmd = "sed -i -e 's:%s:FIXMESTAGINGDIRHOST:g'" % staging_host
  499. if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross-canadian', d):
  500. sstate_grep_cmd = "grep -l -e '%s'" % (staging_host)
  501. elif bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d):
  502. sstate_grep_cmd = "grep -l -e '%s' -e '%s'" % (staging_target, staging_host)
  503. sstate_sed_cmd += " -e 's:%s:FIXMESTAGINGDIRTARGET:g'" % staging_target
  504. else:
  505. sstate_grep_cmd = "grep -l -e '%s' -e '%s'" % (staging_target, staging_host)
  506. sstate_sed_cmd += " -e 's:%s:FIXMESTAGINGDIRTARGET:g'" % staging_target
  507. extra_staging_fixmes = d.getVar('EXTRA_STAGING_FIXMES') or ''
  508. for fixmevar in extra_staging_fixmes.split():
  509. fixme_path = d.getVar(fixmevar)
  510. sstate_sed_cmd += " -e 's:%s:FIXME_%s:g'" % (fixme_path, fixmevar)
  511. sstate_grep_cmd += " -e '%s'" % (fixme_path)
  512. fixmefn = sstate_builddir + "fixmepath"
  513. sstate_scan_cmd = d.getVar('SSTATE_SCAN_CMD')
  514. sstate_filelist_cmd = "tee %s" % (fixmefn)
  515. # fixmepath file needs relative paths, drop sstate_builddir prefix
  516. sstate_filelist_relative_cmd = "sed -i -e 's:^%s::g' %s" % (sstate_builddir, fixmefn)
  517. xargs_no_empty_run_cmd = '--no-run-if-empty'
  518. if platform.system() == 'Darwin':
  519. xargs_no_empty_run_cmd = ''
  520. # Limit the fixpaths and sed operations based on the initial grep search
  521. # This has the side effect of making sure the vfs cache is hot
  522. sstate_hardcode_cmd = "%s | xargs %s | %s | xargs %s %s" % (sstate_scan_cmd, sstate_grep_cmd, sstate_filelist_cmd, xargs_no_empty_run_cmd, sstate_sed_cmd)
  523. bb.note("Removing hardcoded paths from sstate package: '%s'" % (sstate_hardcode_cmd))
  524. subprocess.check_output(sstate_hardcode_cmd, shell=True, cwd=sstate_builddir)
  525. # If the fixmefn is empty, remove it..
  526. if os.stat(fixmefn).st_size == 0:
  527. os.remove(fixmefn)
  528. else:
  529. bb.note("Replacing absolute paths in fixmepath file: '%s'" % (sstate_filelist_relative_cmd))
  530. subprocess.check_output(sstate_filelist_relative_cmd, shell=True)
  531. }
  532. def sstate_package(ss, d):
  533. import oe.path
  534. import time
  535. tmpdir = d.getVar('TMPDIR')
  536. sstatebuild = d.expand("${WORKDIR}/sstate-build-%s/" % ss['task'])
  537. sde = int(d.getVar("SOURCE_DATE_EPOCH") or time.time())
  538. d.setVar("SSTATE_CURRTASK", ss['task'])
  539. bb.utils.remove(sstatebuild, recurse=True)
  540. bb.utils.mkdirhier(sstatebuild)
  541. exit = False
  542. for state in ss['dirs']:
  543. if not os.path.exists(state[1]):
  544. continue
  545. srcbase = state[0].rstrip("/").rsplit('/', 1)[0]
  546. # Find and error for absolute symlinks. We could attempt to relocate but its not
  547. # clear where the symlink is relative to in this context. We could add that markup
  548. # to sstate tasks but there aren't many of these so better just avoid them entirely.
  549. for walkroot, dirs, files in os.walk(state[1]):
  550. for file in files + dirs:
  551. srcpath = os.path.join(walkroot, file)
  552. if not os.path.islink(srcpath):
  553. continue
  554. link = os.readlink(srcpath)
  555. if not os.path.isabs(link):
  556. continue
  557. if not link.startswith(tmpdir):
  558. continue
  559. bb.error("sstate found an absolute path symlink %s pointing at %s. Please replace this with a relative link." % (srcpath, link))
  560. exit = True
  561. bb.debug(2, "Preparing tree %s for packaging at %s" % (state[1], sstatebuild + state[0]))
  562. bb.utils.rename(state[1], sstatebuild + state[0])
  563. if exit:
  564. bb.fatal("Failing task due to absolute path symlinks")
  565. workdir = d.getVar('WORKDIR')
  566. sharedworkdir = os.path.join(d.getVar('TMPDIR'), "work-shared")
  567. for plain in ss['plaindirs']:
  568. pdir = plain.replace(workdir, sstatebuild)
  569. if sharedworkdir in plain:
  570. pdir = plain.replace(sharedworkdir, sstatebuild)
  571. bb.utils.mkdirhier(plain)
  572. bb.utils.mkdirhier(pdir)
  573. bb.utils.rename(plain, pdir)
  574. d.setVar('SSTATE_BUILDDIR', sstatebuild)
  575. d.setVar('SSTATE_INSTDIR', sstatebuild)
  576. if d.getVar('SSTATE_SKIP_CREATION') == '1':
  577. return
  578. sstate_create_package = ['sstate_report_unihash', 'sstate_create_and_sign_package']
  579. for f in (d.getVar('SSTATECREATEFUNCS') or '').split() + \
  580. sstate_create_package + \
  581. (d.getVar('SSTATEPOSTCREATEFUNCS') or '').split():
  582. # All hooks should run in SSTATE_BUILDDIR.
  583. bb.build.exec_func(f, d, (sstatebuild,))
  584. # SSTATE_PKG may have been changed by sstate_report_unihash
  585. siginfo = d.getVar('SSTATE_PKG') + ".siginfo"
  586. if not os.path.exists(siginfo):
  587. bb.siggen.dump_this_task(siginfo, d)
  588. else:
  589. try:
  590. os.utime(siginfo, None)
  591. except PermissionError:
  592. pass
  593. except OSError as e:
  594. # Handle read-only file systems gracefully
  595. import errno
  596. if e.errno != errno.EROFS:
  597. raise e
  598. return
  599. sstate_package[vardepsexclude] += "SSTATE_SIG_KEY SSTATE_PKG"
  600. def pstaging_fetch(sstatefetch, d):
  601. import bb.fetch2
  602. # Only try and fetch if the user has configured a mirror
  603. mirrors = d.getVar('SSTATE_MIRRORS')
  604. if not mirrors:
  605. return
  606. # Copy the data object and override DL_DIR and SRC_URI
  607. localdata = bb.data.createCopy(d)
  608. dldir = localdata.expand("${SSTATE_DIR}")
  609. localdata.delVar('MIRRORS')
  610. localdata.setVar('FILESPATH', dldir)
  611. localdata.setVar('DL_DIR', dldir)
  612. localdata.setVar('PREMIRRORS', mirrors)
  613. # if BB_NO_NETWORK is set but we also have SSTATE_MIRROR_ALLOW_NETWORK,
  614. # we'll want to allow network access for the current set of fetches.
  615. if bb.utils.to_boolean(localdata.getVar('BB_NO_NETWORK')) and \
  616. bb.utils.to_boolean(localdata.getVar('SSTATE_MIRROR_ALLOW_NETWORK')):
  617. localdata.delVar('BB_NO_NETWORK')
  618. # Try a fetch from the sstate mirror, if it fails just return and
  619. # we will build the package
  620. uris = ['file://{0};downloadfilename={0}'.format(sstatefetch),
  621. 'file://{0}.siginfo;downloadfilename={0}.siginfo'.format(sstatefetch)]
  622. if bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG"), False):
  623. uris += ['file://{0}.sig;downloadfilename={0}.sig'.format(sstatefetch)]
  624. with bb.utils.umask(bb.utils.to_filemode(d.getVar("OE_SHARED_UMASK"))):
  625. bb.utils.mkdirhier(dldir)
  626. for srcuri in uris:
  627. localdata.delVar('SRC_URI')
  628. localdata.setVar('SRC_URI', srcuri)
  629. try:
  630. fetcher = bb.fetch2.Fetch([srcuri], localdata, cache=False)
  631. fetcher.checkstatus()
  632. fetcher.download()
  633. except bb.fetch2.BBFetchException:
  634. pass
  635. def sstate_setscene(d):
  636. shared_state = sstate_state_fromvars(d)
  637. accelerate = sstate_installpkg(shared_state, d)
  638. if not accelerate:
  639. msg = "No sstate archive obtainable, will run full task instead."
  640. bb.warn(msg)
  641. raise bb.BBHandledException(msg)
  642. python sstate_task_prefunc () {
  643. shared_state = sstate_state_fromvars(d)
  644. sstate_clean(shared_state, d)
  645. }
  646. sstate_task_prefunc[dirs] = "${WORKDIR}"
  647. python sstate_task_postfunc () {
  648. shared_state = sstate_state_fromvars(d)
  649. shared_umask = bb.utils.to_filemode(d.getVar("OE_SHARED_UMASK"))
  650. omask = os.umask(shared_umask)
  651. if omask != shared_umask:
  652. bb.note("Using umask %0o (not %0o) for sstate packaging" % (shared_umask, omask))
  653. sstate_package(shared_state, d)
  654. os.umask(omask)
  655. sstateinst = d.getVar("SSTATE_INSTDIR")
  656. d.setVar('SSTATE_FIXMEDIR', shared_state['fixmedir'])
  657. sstate_installpkgdir(shared_state, d)
  658. bb.utils.remove(d.getVar("SSTATE_BUILDDIR"), recurse=True)
  659. }
  660. sstate_task_postfunc[dirs] = "${WORKDIR}"
  661. # Create a sstate package
  662. # If enabled, sign the package.
  663. # Package and signature are created in a sub-directory
  664. # and renamed in place once created.
  665. python sstate_create_and_sign_package () {
  666. from pathlib import Path
  667. # Best effort touch
  668. def touch(file):
  669. try:
  670. file.touch()
  671. except:
  672. pass
  673. def update_file(src, dst, force=False):
  674. if dst.is_symlink() and not dst.exists():
  675. force=True
  676. try:
  677. # This relies on that src is a temporary file that can be renamed
  678. # or left as is.
  679. if force:
  680. src.rename(dst)
  681. else:
  682. os.link(src, dst)
  683. return True
  684. except:
  685. pass
  686. if dst.exists():
  687. touch(dst)
  688. return False
  689. sign_pkg = (
  690. bb.utils.to_boolean(d.getVar("SSTATE_VERIFY_SIG")) and
  691. bool(d.getVar("SSTATE_SIG_KEY"))
  692. )
  693. sstate_pkg = Path(d.getVar("SSTATE_PKG"))
  694. sstate_pkg_sig = Path(str(sstate_pkg) + ".sig")
  695. if sign_pkg:
  696. if sstate_pkg.exists() and sstate_pkg_sig.exists():
  697. touch(sstate_pkg)
  698. touch(sstate_pkg_sig)
  699. return
  700. else:
  701. if sstate_pkg.exists():
  702. touch(sstate_pkg)
  703. return
  704. # Create the required sstate directory if it is not present.
  705. if not sstate_pkg.parent.is_dir():
  706. shared_umask = bb.utils.to_filemode(d.getVar("OE_SHARED_UMASK"))
  707. with bb.utils.umask(shared_umask):
  708. bb.utils.mkdirhier(str(sstate_pkg.parent))
  709. if sign_pkg:
  710. from tempfile import TemporaryDirectory
  711. with TemporaryDirectory(dir=sstate_pkg.parent) as tmp_dir:
  712. tmp_pkg = Path(tmp_dir) / sstate_pkg.name
  713. sstate_archive_package(tmp_pkg, d)
  714. from oe.gpg_sign import get_signer
  715. signer = get_signer(d, 'local')
  716. signer.detach_sign(str(tmp_pkg), d.getVar('SSTATE_SIG_KEY'), None,
  717. d.getVar('SSTATE_SIG_PASSPHRASE'), armor=False)
  718. tmp_pkg_sig = Path(tmp_dir) / sstate_pkg_sig.name
  719. if not update_file(tmp_pkg_sig, sstate_pkg_sig):
  720. # If the created signature file could not be copied into place,
  721. # then we should not use the sstate package either.
  722. return
  723. # If the .sig file was updated, then the sstate package must also
  724. # be updated.
  725. update_file(tmp_pkg, sstate_pkg, force=True)
  726. else:
  727. from tempfile import NamedTemporaryFile
  728. with NamedTemporaryFile(prefix=sstate_pkg.name, dir=sstate_pkg.parent) as tmp_pkg_fd:
  729. tmp_pkg = tmp_pkg_fd.name
  730. sstate_archive_package(tmp_pkg, d)
  731. update_file(tmp_pkg, sstate_pkg)
  732. # update_file() may have renamed tmp_pkg, which must exist when the
  733. # NamedTemporaryFile() context handler ends.
  734. touch(Path(tmp_pkg))
  735. }
  736. # Function to generate a sstate package from the current directory.
  737. # The calling function handles moving the sstate package into the final
  738. # destination.
  739. def sstate_archive_package(sstate_pkg, d):
  740. import subprocess
  741. cmd = [
  742. "tar",
  743. "-I", d.expand("pzstd -${SSTATE_ZSTD_CLEVEL} -p${ZSTD_THREADS}"),
  744. "-cS",
  745. "-f", sstate_pkg,
  746. ]
  747. # tar refuses to create an empty archive unless told explicitly
  748. files = sorted(os.listdir("."))
  749. if not files:
  750. files = ["--files-from=/dev/null"]
  751. try:
  752. subprocess.run(cmd + files, check=True)
  753. except subprocess.CalledProcessError as e:
  754. # Ignore error 1 as this is caused by files changing
  755. # (link count increasing from hardlinks being created).
  756. if e.returncode != 1:
  757. raise
  758. os.chmod(sstate_pkg, 0o664)
  759. python sstate_report_unihash() {
  760. report_unihash = getattr(bb.parse.siggen, 'report_unihash', None)
  761. if report_unihash:
  762. ss = sstate_state_fromvars(d)
  763. report_unihash(os.getcwd(), ss['task'], d)
  764. }
  765. #
  766. # Shell function to decompress and prepare a package for installation
  767. # Will be run from within SSTATE_INSTDIR.
  768. #
  769. sstate_unpack_package () {
  770. ZSTD="zstd -T${ZSTD_THREADS}"
  771. # Use pzstd if available
  772. if [ -x "$(command -v pzstd)" ]; then
  773. ZSTD="pzstd -p ${ZSTD_THREADS}"
  774. fi
  775. tar -I "$ZSTD" -xvpf ${SSTATE_PKG}
  776. # update .siginfo atime on local/NFS mirror if it is a symbolic link
  777. [ ! -h ${SSTATE_PKG}.siginfo ] || [ ! -e ${SSTATE_PKG}.siginfo ] || touch -a ${SSTATE_PKG}.siginfo 2>/dev/null || true
  778. # update each symbolic link instead of any referenced file
  779. touch --no-dereference ${SSTATE_PKG} 2>/dev/null || true
  780. [ ! -e ${SSTATE_PKG}.sig ] || touch --no-dereference ${SSTATE_PKG}.sig 2>/dev/null || true
  781. [ ! -e ${SSTATE_PKG}.siginfo ] || touch --no-dereference ${SSTATE_PKG}.siginfo 2>/dev/null || true
  782. }
  783. BB_HASHCHECK_FUNCTION = "sstate_checkhashes"
  784. def sstate_checkhashes(sq_data, d, siginfo=False, currentcount=0, summary=True, **kwargs):
  785. import itertools
  786. found = set()
  787. missed = set()
  788. def gethash(task):
  789. return sq_data['unihash'][task]
  790. def getpathcomponents(task, d):
  791. # Magic data from BB_HASHFILENAME
  792. splithashfn = sq_data['hashfn'][task].split(" ")
  793. spec = splithashfn[1]
  794. if splithashfn[0] == "True":
  795. extrapath = d.getVar("NATIVELSBSTRING") + "/"
  796. else:
  797. extrapath = ""
  798. tname = bb.runqueue.taskname_from_tid(task)[3:]
  799. if tname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and splithashfn[2]:
  800. spec = splithashfn[2]
  801. extrapath = ""
  802. return spec, extrapath, tname
  803. def getsstatefile(tid, siginfo, d):
  804. spec, extrapath, tname = getpathcomponents(tid, d)
  805. return extrapath + generate_sstatefn(spec, gethash(tid), tname, siginfo, d)
  806. for tid in sq_data['hash']:
  807. sstatefile = d.expand("${SSTATE_DIR}/" + getsstatefile(tid, siginfo, d))
  808. if os.path.exists(sstatefile):
  809. oe.utils.touch(sstatefile)
  810. found.add(tid)
  811. bb.debug(2, "SState: Found valid sstate file %s" % sstatefile)
  812. else:
  813. missed.add(tid)
  814. bb.debug(2, "SState: Looked for but didn't find file %s" % sstatefile)
  815. foundLocal = len(found)
  816. mirrors = d.getVar("SSTATE_MIRRORS")
  817. if mirrors:
  818. # Copy the data object and override DL_DIR and SRC_URI
  819. localdata = bb.data.createCopy(d)
  820. dldir = localdata.expand("${SSTATE_DIR}")
  821. localdata.delVar('MIRRORS')
  822. localdata.setVar('FILESPATH', dldir)
  823. localdata.setVar('DL_DIR', dldir)
  824. localdata.setVar('PREMIRRORS', mirrors)
  825. bb.debug(2, "SState using premirror of: %s" % mirrors)
  826. # if BB_NO_NETWORK is set but we also have SSTATE_MIRROR_ALLOW_NETWORK,
  827. # we'll want to allow network access for the current set of fetches.
  828. if bb.utils.to_boolean(localdata.getVar('BB_NO_NETWORK')) and \
  829. bb.utils.to_boolean(localdata.getVar('SSTATE_MIRROR_ALLOW_NETWORK')):
  830. localdata.delVar('BB_NO_NETWORK')
  831. from bb.fetch2 import FetchConnectionCache
  832. def checkstatus_init():
  833. while not connection_cache_pool.full():
  834. connection_cache_pool.put(FetchConnectionCache())
  835. def checkstatus_end():
  836. while not connection_cache_pool.empty():
  837. connection_cache = connection_cache_pool.get()
  838. connection_cache.close_connections()
  839. def checkstatus(arg):
  840. (tid, sstatefile) = arg
  841. connection_cache = connection_cache_pool.get()
  842. localdata2 = bb.data.createCopy(localdata)
  843. srcuri = "file://" + sstatefile
  844. localdata2.setVar('SRC_URI', srcuri)
  845. bb.debug(2, "SState: Attempting to fetch %s" % srcuri)
  846. import traceback
  847. try:
  848. fetcher = bb.fetch2.Fetch(srcuri.split(), localdata2,
  849. connection_cache=connection_cache)
  850. fetcher.checkstatus()
  851. bb.debug(2, "SState: Successful fetch test for %s" % srcuri)
  852. found.add(tid)
  853. missed.remove(tid)
  854. except bb.fetch2.FetchError as e:
  855. bb.debug(2, "SState: Unsuccessful fetch test for %s (%s)\n%s" % (srcuri, repr(e), traceback.format_exc()))
  856. except Exception as e:
  857. bb.error("SState: cannot test %s: %s\n%s" % (srcuri, repr(e), traceback.format_exc()))
  858. connection_cache_pool.put(connection_cache)
  859. if progress:
  860. bb.event.fire(bb.event.ProcessProgress(msg, next(cnt_tasks_done)), d)
  861. bb.event.check_for_interrupts()
  862. tasklist = []
  863. for tid in missed:
  864. sstatefile = d.expand(getsstatefile(tid, siginfo, d))
  865. tasklist.append((tid, sstatefile))
  866. if tasklist:
  867. nproc = min(int(d.getVar("BB_NUMBER_THREADS")), len(tasklist))
  868. ## thread-safe counter
  869. cnt_tasks_done = itertools.count(start = 1)
  870. progress = len(tasklist) >= 100
  871. if progress:
  872. msg = "Checking sstate mirror object availability"
  873. bb.event.fire(bb.event.ProcessStarted(msg, len(tasklist)), d)
  874. # Have to setup the fetcher environment here rather than in each thread as it would race
  875. fetcherenv = bb.fetch2.get_fetcher_environment(d)
  876. with bb.utils.environment(**fetcherenv):
  877. bb.event.enable_threadlock()
  878. import concurrent.futures
  879. from queue import Queue
  880. connection_cache_pool = Queue(nproc)
  881. checkstatus_init()
  882. with concurrent.futures.ThreadPoolExecutor(max_workers=nproc) as executor:
  883. executor.map(checkstatus, tasklist.copy())
  884. checkstatus_end()
  885. bb.event.disable_threadlock()
  886. if progress:
  887. bb.event.fire(bb.event.ProcessFinished(msg), d)
  888. inheritlist = d.getVar("INHERIT")
  889. if "toaster" in inheritlist:
  890. evdata = {'missed': [], 'found': []};
  891. for tid in missed:
  892. sstatefile = d.expand(getsstatefile(tid, False, d))
  893. evdata['missed'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) )
  894. for tid in found:
  895. sstatefile = d.expand(getsstatefile(tid, False, d))
  896. evdata['found'].append((bb.runqueue.fn_from_tid(tid), bb.runqueue.taskname_from_tid(tid), gethash(tid), sstatefile ) )
  897. bb.event.fire(bb.event.MetadataEvent("MissedSstate", evdata), d)
  898. if summary:
  899. # Print some summary statistics about the current task completion and how much sstate
  900. # reuse there was. Avoid divide by zero errors.
  901. total = len(sq_data['hash'])
  902. complete = 0
  903. if currentcount:
  904. complete = (len(found) + currentcount) / (total + currentcount) * 100
  905. match = 0
  906. if total:
  907. match = len(found) / total * 100
  908. bb.plain("Sstate summary: Wanted %d Local %d Mirrors %d Missed %d Current %d (%d%% match, %d%% complete)" %
  909. (total, foundLocal, len(found)-foundLocal, len(missed), currentcount, match, complete))
  910. if hasattr(bb.parse.siggen, "checkhashes"):
  911. bb.parse.siggen.checkhashes(sq_data, missed, found, d)
  912. return found
  913. setscene_depvalid[vardepsexclude] = "SSTATE_EXCLUDEDEPS_SYSROOT _SSTATE_EXCLUDEDEPS_SYSROOT"
  914. BB_SETSCENE_DEPVALID = "setscene_depvalid"
  915. def setscene_depvalid(task, taskdependees, notneeded, d, log=None):
  916. # taskdependees is a dict of tasks which depend on task, each being a 3 item list of [PN, TASKNAME, FILENAME]
  917. # task is included in taskdependees too
  918. # Return - False - We need this dependency
  919. # - True - We can skip this dependency
  920. import re
  921. def logit(msg, log):
  922. if log is not None:
  923. log.append(msg)
  924. else:
  925. bb.debug(2, msg)
  926. logit("Considering setscene task: %s" % (str(taskdependees[task])), log)
  927. directtasks = ["do_populate_lic", "do_deploy_source_date_epoch", "do_shared_workdir", "do_stash_locale", "do_gcc_stash_builddir", "do_create_spdx", "do_deploy_archives"]
  928. def isNativeCross(x):
  929. return x.endswith("-native") or "-cross-" in x or "-crosssdk" in x or x.endswith("-cross")
  930. # We only need to trigger deploy_source_date_epoch through direct dependencies
  931. if taskdependees[task][1] in directtasks:
  932. return True
  933. # We only need to trigger packagedata through direct dependencies
  934. # but need to preserve packagedata on packagedata links
  935. if taskdependees[task][1] == "do_packagedata":
  936. for dep in taskdependees:
  937. if taskdependees[dep][1] == "do_packagedata":
  938. return False
  939. return True
  940. for dep in taskdependees:
  941. logit(" considering dependency: %s" % (str(taskdependees[dep])), log)
  942. if task == dep:
  943. continue
  944. if dep in notneeded:
  945. continue
  946. # do_package_write_* and do_package doesn't need do_package
  947. if taskdependees[task][1] == "do_package" and taskdependees[dep][1] in ['do_package', 'do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm', 'do_packagedata', 'do_package_qa']:
  948. continue
  949. # do_package_write_* need do_populate_sysroot as they're mainly postinstall dependencies
  950. if taskdependees[task][1] == "do_populate_sysroot" and taskdependees[dep][1] in ['do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm']:
  951. return False
  952. # do_package/packagedata/package_qa/deploy don't need do_populate_sysroot
  953. if taskdependees[task][1] == "do_populate_sysroot" and taskdependees[dep][1] in ['do_package', 'do_packagedata', 'do_package_qa', 'do_deploy']:
  954. continue
  955. # Native/Cross packages don't exist and are noexec anyway
  956. if isNativeCross(taskdependees[dep][0]) and taskdependees[dep][1] in ['do_package_write_deb', 'do_package_write_ipk', 'do_package_write_rpm', 'do_packagedata', 'do_package', 'do_package_qa']:
  957. continue
  958. # Consider sysroot depending on sysroot tasks
  959. if taskdependees[task][1] == 'do_populate_sysroot' and taskdependees[dep][1] == 'do_populate_sysroot':
  960. # Allow excluding certain recursive dependencies. If a recipe needs it should add a
  961. # specific dependency itself, rather than relying on one of its dependees to pull
  962. # them in.
  963. # See also http://lists.openembedded.org/pipermail/openembedded-core/2018-January/146324.html
  964. not_needed = False
  965. excludedeps = d.getVar('_SSTATE_EXCLUDEDEPS_SYSROOT')
  966. if excludedeps is None:
  967. # Cache the regular expressions for speed
  968. excludedeps = []
  969. for excl in (d.getVar('SSTATE_EXCLUDEDEPS_SYSROOT') or "").split():
  970. excludedeps.append((re.compile(excl.split('->', 1)[0]), re.compile(excl.split('->', 1)[1])))
  971. d.setVar('_SSTATE_EXCLUDEDEPS_SYSROOT', excludedeps)
  972. for excl in excludedeps:
  973. if excl[0].match(taskdependees[dep][0]):
  974. if excl[1].match(taskdependees[task][0]):
  975. not_needed = True
  976. break
  977. if not_needed:
  978. continue
  979. # For meta-extsdk-toolchain we want all sysroot dependencies
  980. if taskdependees[dep][0] == 'meta-extsdk-toolchain':
  981. return False
  982. # Native/Cross populate_sysroot need their dependencies
  983. if isNativeCross(taskdependees[task][0]) and isNativeCross(taskdependees[dep][0]):
  984. return False
  985. # Target populate_sysroot depended on by cross tools need to be installed
  986. if isNativeCross(taskdependees[dep][0]):
  987. return False
  988. # Native/cross tools depended upon by target sysroot are not needed
  989. # Add an exception for shadow-native as required by useradd.bbclass
  990. if isNativeCross(taskdependees[task][0]) and taskdependees[task][0] != 'shadow-native':
  991. continue
  992. # Target populate_sysroot need their dependencies
  993. return False
  994. if taskdependees[dep][1] in directtasks:
  995. continue
  996. # Safe fallthrough default
  997. logit(" Default setscene dependency fall through due to dependency: %s" % (str(taskdependees[dep])), log)
  998. return False
  999. return True
  1000. addhandler sstate_eventhandler
  1001. sstate_eventhandler[eventmask] = "bb.build.TaskSucceeded"
  1002. python sstate_eventhandler() {
  1003. d = e.data
  1004. writtensstate = d.getVar('SSTATE_CURRTASK')
  1005. if not writtensstate:
  1006. taskname = d.getVar("BB_RUNTASK")[3:]
  1007. spec = d.getVar('SSTATE_PKGSPEC')
  1008. swspec = d.getVar('SSTATE_SWSPEC')
  1009. if taskname in ["fetch", "unpack", "patch", "populate_lic", "preconfigure"] and swspec:
  1010. d.setVar("SSTATE_PKGSPEC", "${SSTATE_SWSPEC}")
  1011. d.setVar("SSTATE_EXTRAPATH", "")
  1012. d.setVar("SSTATE_CURRTASK", taskname)
  1013. siginfo = d.getVar('SSTATE_PKG') + ".siginfo"
  1014. if not os.path.exists(siginfo):
  1015. bb.siggen.dump_this_task(siginfo, d)
  1016. else:
  1017. oe.utils.touch(siginfo)
  1018. }
  1019. SSTATE_PRUNE_OBSOLETEWORKDIR ?= "1"
  1020. #
  1021. # Event handler which removes manifests and stamps file for recipes which are no
  1022. # longer 'reachable' in a build where they once were. 'Reachable' refers to
  1023. # whether a recipe is parsed so recipes in a layer which was removed would no
  1024. # longer be reachable. Switching between systemd and sysvinit where recipes
  1025. # became skipped would be another example.
  1026. #
  1027. # Also optionally removes the workdir of those tasks/recipes
  1028. #
  1029. addhandler sstate_eventhandler_reachablestamps
  1030. sstate_eventhandler_reachablestamps[eventmask] = "bb.event.ReachableStamps"
  1031. python sstate_eventhandler_reachablestamps() {
  1032. import glob
  1033. d = e.data
  1034. stamps = e.stamps.values()
  1035. removeworkdir = (d.getVar("SSTATE_PRUNE_OBSOLETEWORKDIR", False) == "1")
  1036. preservestampfile = d.expand('${SSTATE_MANIFESTS}/preserve-stamps')
  1037. preservestamps = []
  1038. if os.path.exists(preservestampfile):
  1039. with open(preservestampfile, 'r') as f:
  1040. preservestamps = f.readlines()
  1041. seen = []
  1042. # The machine index contains all the stamps this machine has ever seen in this build directory.
  1043. # We should only remove things which this machine once accessed but no longer does.
  1044. machineindex = set()
  1045. bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
  1046. mi = d.expand("${SSTATE_MANIFESTS}/index-machine-${MACHINE}")
  1047. if os.path.exists(mi):
  1048. with open(mi, "r") as f:
  1049. machineindex = set(line.strip() for line in f.readlines())
  1050. for a in sorted(list(set(d.getVar("SSTATE_ARCHS").split()))):
  1051. toremove = []
  1052. i = d.expand("${SSTATE_MANIFESTS}/index-" + a)
  1053. if not os.path.exists(i):
  1054. continue
  1055. manseen = set()
  1056. ignore = []
  1057. with open(i, "r") as f:
  1058. lines = f.readlines()
  1059. for l in reversed(lines):
  1060. try:
  1061. (stamp, manifest, workdir) = l.split()
  1062. # The index may have multiple entries for the same manifest as the code above only appends
  1063. # new entries and there may be an entry with matching manifest but differing version in stamp/workdir.
  1064. # The last entry in the list is the valid one, any earlier entries with matching manifests
  1065. # should be ignored.
  1066. if manifest in manseen:
  1067. ignore.append(l)
  1068. continue
  1069. manseen.add(manifest)
  1070. if stamp not in stamps and stamp not in preservestamps and stamp in machineindex:
  1071. toremove.append(l)
  1072. if stamp not in seen:
  1073. bb.debug(2, "Stamp %s is not reachable, removing related manifests" % stamp)
  1074. seen.append(stamp)
  1075. except ValueError:
  1076. bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i))
  1077. if toremove:
  1078. msg = "Removing %d recipes from the %s sysroot" % (len(toremove), a)
  1079. bb.event.fire(bb.event.ProcessStarted(msg, len(toremove)), d)
  1080. removed = 0
  1081. for r in toremove:
  1082. (stamp, manifest, workdir) = r.split()
  1083. for m in glob.glob(manifest + ".*"):
  1084. if m.endswith(".postrm"):
  1085. continue
  1086. sstate_clean_manifest(m, d)
  1087. bb.utils.remove(stamp + "*")
  1088. if removeworkdir:
  1089. bb.utils.remove(workdir, recurse = True)
  1090. lines.remove(r)
  1091. removed = removed + 1
  1092. bb.event.fire(bb.event.ProcessProgress(msg, removed), d)
  1093. bb.event.check_for_interrupts()
  1094. bb.event.fire(bb.event.ProcessFinished(msg), d)
  1095. with open(i, "w") as f:
  1096. for l in lines:
  1097. if l in ignore:
  1098. continue
  1099. f.write(l)
  1100. machineindex |= set(stamps)
  1101. with open(mi, "w") as f:
  1102. for l in machineindex:
  1103. f.write(l + "\n")
  1104. if preservestamps:
  1105. os.remove(preservestampfile)
  1106. }
  1107. #
  1108. # Bitbake can generate an event showing which setscene tasks are 'stale',
  1109. # i.e. which ones will be rerun. These are ones where a stamp file is present but
  1110. # it is stable (e.g. taskhash doesn't match). With that list we can go through
  1111. # the manifests for matching tasks and "uninstall" those manifests now. We do
  1112. # this now rather than mid build since the distribution of files between sstate
  1113. # objects may have changed, new tasks may run first and if those new tasks overlap
  1114. # with the stale tasks, we'd see overlapping files messages and failures. Thankfully
  1115. # removing these files is fast.
  1116. #
  1117. addhandler sstate_eventhandler_stalesstate
  1118. sstate_eventhandler_stalesstate[eventmask] = "bb.event.StaleSetSceneTasks"
  1119. python sstate_eventhandler_stalesstate() {
  1120. d = e.data
  1121. tasks = e.tasks
  1122. bb.utils.mkdirhier(d.expand("${SSTATE_MANIFESTS}"))
  1123. for a in list(set(d.getVar("SSTATE_ARCHS").split())):
  1124. toremove = []
  1125. i = d.expand("${SSTATE_MANIFESTS}/index-" + a)
  1126. if not os.path.exists(i):
  1127. continue
  1128. with open(i, "r") as f:
  1129. lines = f.readlines()
  1130. for l in lines:
  1131. try:
  1132. (stamp, manifest, workdir) = l.split()
  1133. for tid in tasks:
  1134. for s in tasks[tid]:
  1135. if s.startswith(stamp):
  1136. taskname = bb.runqueue.taskname_from_tid(tid)[3:]
  1137. manname = manifest + "." + taskname
  1138. if os.path.exists(manname):
  1139. bb.debug(2, "Sstate for %s is stale, removing related manifest %s" % (tid, manname))
  1140. toremove.append((manname, tid, tasks[tid]))
  1141. break
  1142. except ValueError:
  1143. bb.fatal("Invalid line '%s' in sstate manifest '%s'" % (l, i))
  1144. if toremove:
  1145. msg = "Removing %d stale sstate objects for arch %s" % (len(toremove), a)
  1146. bb.event.fire(bb.event.ProcessStarted(msg, len(toremove)), d)
  1147. removed = 0
  1148. for (manname, tid, stamps) in toremove:
  1149. sstate_clean_manifest(manname, d)
  1150. for stamp in stamps:
  1151. bb.utils.remove(stamp)
  1152. removed = removed + 1
  1153. bb.event.fire(bb.event.ProcessProgress(msg, removed), d)
  1154. bb.event.check_for_interrupts()
  1155. bb.event.fire(bb.event.ProcessFinished(msg), d)
  1156. }