sstate.bbclass 55 KB

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