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