1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: MIT
5#
6
7# These directories will be staged in the sysroot
8SYSROOT_DIRS = " \
9    ${includedir} \
10    ${libdir} \
11    ${base_libdir} \
12    ${nonarch_base_libdir} \
13    ${datadir} \
14    /sysroot-only \
15"
16
17# These directories are also staged in the sysroot when they contain files that
18# are usable on the build system
19SYSROOT_DIRS_NATIVE = " \
20    ${bindir} \
21    ${sbindir} \
22    ${base_bindir} \
23    ${base_sbindir} \
24    ${libexecdir} \
25    ${sysconfdir} \
26    ${localstatedir} \
27"
28SYSROOT_DIRS:append:class-native = " ${SYSROOT_DIRS_NATIVE}"
29SYSROOT_DIRS:append:class-cross = " ${SYSROOT_DIRS_NATIVE}"
30SYSROOT_DIRS:append:class-crosssdk = " ${SYSROOT_DIRS_NATIVE}"
31
32# These directories will not be staged in the sysroot
33SYSROOT_DIRS_IGNORE = " \
34    ${mandir} \
35    ${docdir} \
36    ${infodir} \
37    ${datadir}/X11/locale \
38    ${datadir}/applications \
39    ${datadir}/bash-completion \
40    ${datadir}/fonts \
41    ${datadir}/gtk-doc/html \
42    ${datadir}/installed-tests \
43    ${datadir}/locale \
44    ${datadir}/pixmaps \
45    ${datadir}/terminfo \
46    ${libdir}/${BPN}/ptest \
47"
48
49sysroot_stage_dir() {
50	src="$1"
51	dest="$2"
52	# if the src doesn't exist don't do anything
53	if [ ! -d "$src" ]; then
54		 return
55	fi
56
57	mkdir -p "$dest"
58	rdest=$(realpath --relative-to="$src" "$dest")
59	(
60		cd $src
61		find . -print0 | cpio --null -pdlu $rdest
62	)
63}
64
65sysroot_stage_dirs() {
66	from="$1"
67	to="$2"
68
69	for dir in ${SYSROOT_DIRS}; do
70		sysroot_stage_dir "$from$dir" "$to$dir"
71	done
72
73	# Remove directories we do not care about
74	for dir in ${SYSROOT_DIRS_IGNORE}; do
75		rm -rf "$to$dir"
76	done
77}
78
79sysroot_stage_all() {
80	sysroot_stage_dirs ${D} ${SYSROOT_DESTDIR}
81}
82
83python sysroot_strip () {
84    inhibit_sysroot = d.getVar('INHIBIT_SYSROOT_STRIP')
85    if inhibit_sysroot and oe.types.boolean(inhibit_sysroot):
86        return
87
88    dstdir = d.getVar('SYSROOT_DESTDIR')
89    pn = d.getVar('PN')
90    libdir = d.getVar("libdir")
91    base_libdir = d.getVar("base_libdir")
92    qa_already_stripped = 'already-stripped' in (d.getVar('INSANE_SKIP:' + pn) or "").split()
93    strip_cmd = d.getVar("STRIP")
94
95    max_process = oe.utils.get_bb_number_threads(d)
96    oe.package.strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, max_process,
97                           qa_already_stripped=qa_already_stripped)
98}
99
100do_populate_sysroot[dirs] = "${SYSROOT_DESTDIR}"
101
102addtask populate_sysroot after do_install
103
104SYSROOT_PREPROCESS_FUNCS ?= ""
105SYSROOT_DESTDIR = "${WORKDIR}/sysroot-destdir"
106
107python do_populate_sysroot () {
108    # SYSROOT 'version' 2
109    bb.build.exec_func("sysroot_stage_all", d)
110    bb.build.exec_func("sysroot_strip", d)
111    for f in (d.getVar('SYSROOT_PREPROCESS_FUNCS') or '').split():
112        bb.build.exec_func(f, d)
113    pn = d.getVar("PN")
114    multiprov = d.getVar("BB_MULTI_PROVIDER_ALLOWED").split()
115    provdir = d.expand("${SYSROOT_DESTDIR}${base_prefix}/sysroot-providers/")
116    bb.utils.mkdirhier(provdir)
117    for p in d.getVar("PROVIDES").split():
118        if p in multiprov:
119            continue
120        p = p.replace("/", "_")
121        with open(provdir + p, "w") as f:
122            f.write(pn)
123}
124
125do_populate_sysroot[vardeps] += "${SYSROOT_PREPROCESS_FUNCS}"
126do_populate_sysroot[vardepsexclude] += "BB_MULTI_PROVIDER_ALLOWED"
127
128POPULATESYSROOTDEPS = ""
129POPULATESYSROOTDEPS:class-target = "virtual/${HOST_PREFIX}binutils:do_populate_sysroot"
130POPULATESYSROOTDEPS:class-nativesdk = "virtual/${HOST_PREFIX}binutils:do_populate_sysroot"
131do_populate_sysroot[depends] += "${POPULATESYSROOTDEPS}"
132
133SSTATETASKS += "do_populate_sysroot"
134do_populate_sysroot[cleandirs] = "${SYSROOT_DESTDIR}"
135do_populate_sysroot[sstate-inputdirs] = "${SYSROOT_DESTDIR}"
136do_populate_sysroot[sstate-outputdirs] = "${COMPONENTS_DIR}/${PACKAGE_ARCH}/${PN}"
137do_populate_sysroot[sstate-fixmedir] = "${COMPONENTS_DIR}/${PACKAGE_ARCH}/${PN}"
138
139python do_populate_sysroot_setscene () {
140    sstate_setscene(d)
141}
142addtask do_populate_sysroot_setscene
143
144def staging_copyfile(c, target, dest, postinsts, seendirs):
145    import errno
146
147    destdir = os.path.dirname(dest)
148    if destdir not in seendirs:
149        bb.utils.mkdirhier(destdir)
150        seendirs.add(destdir)
151    if "/usr/bin/postinst-" in c:
152        postinsts.append(dest)
153    if os.path.islink(c):
154        linkto = os.readlink(c)
155        if os.path.lexists(dest):
156            if not os.path.islink(dest):
157                raise OSError(errno.EEXIST, "Link %s already exists as a file" % dest, dest)
158            if os.readlink(dest) == linkto:
159                return dest
160            raise OSError(errno.EEXIST, "Link %s already exists to a different location? (%s vs %s)" % (dest, os.readlink(dest), linkto), dest)
161        os.symlink(linkto, dest)
162        #bb.warn(c)
163    else:
164        try:
165            os.link(c, dest)
166        except OSError as err:
167            if err.errno == errno.EXDEV:
168                bb.utils.copyfile(c, dest)
169            else:
170                raise
171    return dest
172
173def staging_copydir(c, target, dest, seendirs):
174    if dest not in seendirs:
175        bb.utils.mkdirhier(dest)
176        seendirs.add(dest)
177
178def staging_processfixme(fixme, target, recipesysroot, recipesysrootnative, d):
179    import subprocess
180
181    if not fixme:
182        return
183    cmd = "sed -e 's:^[^/]*/:%s/:g' %s | xargs sed -i -e 's:FIXMESTAGINGDIRTARGET:%s:g; s:FIXMESTAGINGDIRHOST:%s:g'" % (target, " ".join(fixme), recipesysroot, recipesysrootnative)
184    for fixmevar in ['PSEUDO_SYSROOT', 'HOSTTOOLS_DIR', 'PKGDATA_DIR', 'PSEUDO_LOCALSTATEDIR', 'LOGFIFO']:
185        fixme_path = d.getVar(fixmevar)
186        cmd += " -e 's:FIXME_%s:%s:g'" % (fixmevar, fixme_path)
187    bb.debug(2, cmd)
188    subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
189
190
191def staging_populate_sysroot_dir(targetsysroot, nativesysroot, native, d):
192    import glob
193    import subprocess
194    import errno
195
196    fixme = []
197    postinsts = []
198    seendirs = set()
199    stagingdir = d.getVar("STAGING_DIR")
200    if native:
201        pkgarchs = ['${BUILD_ARCH}', '${BUILD_ARCH}_*']
202        targetdir = nativesysroot
203    else:
204        pkgarchs = ['${MACHINE_ARCH}']
205        pkgarchs = pkgarchs + list(reversed(d.getVar("PACKAGE_EXTRA_ARCHS").split()))
206        pkgarchs.append('allarch')
207        targetdir = targetsysroot
208
209    bb.utils.mkdirhier(targetdir)
210    for pkgarch in pkgarchs:
211        for manifest in glob.glob(d.expand("${SSTATE_MANIFESTS}/manifest-%s-*.populate_sysroot" % pkgarch)):
212            if manifest.endswith("-initial.populate_sysroot"):
213                # skip libgcc-initial due to file overlap
214                continue
215            if not native and (manifest.endswith("-native.populate_sysroot") or "nativesdk-" in manifest):
216                continue
217            if native and not (manifest.endswith("-native.populate_sysroot") or manifest.endswith("-cross.populate_sysroot") or "-cross-" in manifest):
218                continue
219            tmanifest = targetdir + "/" + os.path.basename(manifest)
220            if os.path.exists(tmanifest):
221                continue
222            try:
223                os.link(manifest, tmanifest)
224            except OSError as err:
225                if err.errno == errno.EXDEV:
226                    bb.utils.copyfile(manifest, tmanifest)
227                else:
228                    raise
229            with open(manifest, "r") as f:
230                for l in f:
231                    l = l.strip()
232                    if l.endswith("/fixmepath"):
233                        fixme.append(l)
234                        continue
235                    if l.endswith("/fixmepath.cmd"):
236                        continue
237                    dest = l.replace(stagingdir, "")
238                    dest = targetdir + "/" + "/".join(dest.split("/")[3:])
239                    if l.endswith("/"):
240                        staging_copydir(l, targetdir, dest, seendirs)
241                        continue
242                    try:
243                        staging_copyfile(l, targetdir, dest, postinsts, seendirs)
244                    except FileExistsError:
245                        continue
246
247    staging_processfixme(fixme, targetdir, targetsysroot, nativesysroot, d)
248    for p in sorted(postinsts):
249        bb.note("Running postinst {}, output:\n{}".format(p, subprocess.check_output(p, shell=True, stderr=subprocess.STDOUT)))
250
251#
252# Manifests here are complicated. The main sysroot area has the unpacked sstate
253# which us unrelocated and tracked by the main sstate manifests. Each recipe
254# specific sysroot has manifests for each dependency that is installed there.
255# The task hash is used to tell whether the data needs to be reinstalled. We
256# use a symlink to point to the currently installed hash. There is also a
257# "complete" stamp file which is used to mark if installation completed. If
258# something fails (e.g. a postinst), this won't get written and we would
259# remove and reinstall the dependency. This also means partially installed
260# dependencies should get cleaned up correctly.
261#
262
263python extend_recipe_sysroot() {
264    import copy
265    import subprocess
266    import errno
267    import collections
268    import glob
269
270    taskdepdata = d.getVar("BB_TASKDEPDATA", False)
271    mytaskname = d.getVar("BB_RUNTASK")
272    if mytaskname.endswith("_setscene"):
273        mytaskname = mytaskname.replace("_setscene", "")
274    workdir = d.getVar("WORKDIR")
275    #bb.warn(str(taskdepdata))
276    pn = d.getVar("PN")
277    stagingdir = d.getVar("STAGING_DIR")
278    sharedmanifests = d.getVar("COMPONENTS_DIR") + "/manifests"
279    # only needed by multilib cross-canadian since it redefines RECIPE_SYSROOT
280    manifestprefix = d.getVar("RECIPE_SYSROOT_MANIFEST_SUBDIR")
281    if manifestprefix:
282        sharedmanifests = sharedmanifests + "/" + manifestprefix
283    recipesysroot = d.getVar("RECIPE_SYSROOT")
284    recipesysrootnative = d.getVar("RECIPE_SYSROOT_NATIVE")
285
286    # Detect bitbake -b usage
287    nodeps = d.getVar("BB_LIMITEDDEPS") or False
288    if nodeps:
289        lock = bb.utils.lockfile(recipesysroot + "/sysroot.lock")
290        staging_populate_sysroot_dir(recipesysroot, recipesysrootnative, True, d)
291        staging_populate_sysroot_dir(recipesysroot, recipesysrootnative, False, d)
292        bb.utils.unlockfile(lock)
293        return
294
295    start = None
296    configuredeps = []
297    owntaskdeps = []
298    for dep in taskdepdata:
299        data = taskdepdata[dep]
300        if data[1] == mytaskname and data[0] == pn:
301            start = dep
302        elif data[0] == pn:
303            owntaskdeps.append(data[1])
304    if start is None:
305        bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")
306
307    # We need to figure out which sysroot files we need to expose to this task.
308    # This needs to match what would get restored from sstate, which is controlled
309    # ultimately by calls from bitbake to setscene_depvalid().
310    # That function expects a setscene dependency tree. We build a dependency tree
311    # condensed to inter-sstate task dependencies, similar to that used by setscene
312    # tasks. We can then call into setscene_depvalid() and decide
313    # which dependencies we can "see" and should expose in the recipe specific sysroot.
314    setscenedeps = copy.deepcopy(taskdepdata)
315
316    start = set([start])
317
318    sstatetasks = d.getVar("SSTATETASKS").split()
319    # Add recipe specific tasks referenced by setscene_depvalid()
320    sstatetasks.append("do_stash_locale")
321    sstatetasks.append("do_deploy")
322
323    def print_dep_tree(deptree):
324        data = ""
325        for dep in deptree:
326            deps = "    " + "\n    ".join(deptree[dep][3]) + "\n"
327            data = data + "%s:\n  %s\n  %s\n%s  %s\n  %s\n" % (deptree[dep][0], deptree[dep][1], deptree[dep][2], deps, deptree[dep][4], deptree[dep][5])
328        return data
329
330    #bb.note("Full dep tree is:\n%s" % print_dep_tree(taskdepdata))
331
332    #bb.note(" start2 is %s" % str(start))
333
334    # If start is an sstate task (like do_package) we need to add in its direct dependencies
335    # else the code below won't recurse into them.
336    for dep in set(start):
337        for dep2 in setscenedeps[dep][3]:
338            start.add(dep2)
339        start.remove(dep)
340
341    #bb.note(" start3 is %s" % str(start))
342
343    # Create collapsed do_populate_sysroot -> do_populate_sysroot tree
344    for dep in taskdepdata:
345        data = setscenedeps[dep]
346        if data[1] not in sstatetasks:
347            for dep2 in setscenedeps:
348                data2 = setscenedeps[dep2]
349                if dep in data2[3]:
350                    data2[3].update(setscenedeps[dep][3])
351                    data2[3].remove(dep)
352            if dep in start:
353                start.update(setscenedeps[dep][3])
354                start.remove(dep)
355            del setscenedeps[dep]
356
357    # Remove circular references
358    for dep in setscenedeps:
359        if dep in setscenedeps[dep][3]:
360            setscenedeps[dep][3].remove(dep)
361
362    #bb.note("Computed dep tree is:\n%s" % print_dep_tree(setscenedeps))
363    #bb.note(" start is %s" % str(start))
364
365    # Direct dependencies should be present and can be depended upon
366    for dep in sorted(set(start)):
367        if setscenedeps[dep][1] == "do_populate_sysroot":
368            if dep not in configuredeps:
369                configuredeps.append(dep)
370    bb.note("Direct dependencies are %s" % str(configuredeps))
371    #bb.note(" or %s" % str(start))
372
373    msgbuf = []
374    # Call into setscene_depvalid for each sub-dependency and only copy sysroot files
375    # for ones that would be restored from sstate.
376    done = list(start)
377    next = list(start)
378    while next:
379        new = []
380        for dep in next:
381            data = setscenedeps[dep]
382            for datadep in data[3]:
383                if datadep in done:
384                    continue
385                taskdeps = {}
386                taskdeps[dep] = setscenedeps[dep][:2]
387                taskdeps[datadep] = setscenedeps[datadep][:2]
388                retval = setscene_depvalid(datadep, taskdeps, [], d, msgbuf)
389                if retval:
390                    msgbuf.append("Skipping setscene dependency %s for installation into the sysroot" % datadep)
391                    continue
392                done.append(datadep)
393                new.append(datadep)
394                if datadep not in configuredeps and setscenedeps[datadep][1] == "do_populate_sysroot":
395                    configuredeps.append(datadep)
396                    msgbuf.append("Adding dependency on %s" % setscenedeps[datadep][0])
397                else:
398                    msgbuf.append("Following dependency on %s" % setscenedeps[datadep][0])
399        next = new
400
401    # This logging is too verbose for day to day use sadly
402    #bb.debug(2, "\n".join(msgbuf))
403
404    depdir = recipesysrootnative + "/installeddeps"
405    bb.utils.mkdirhier(depdir)
406    bb.utils.mkdirhier(sharedmanifests)
407
408    lock = bb.utils.lockfile(recipesysroot + "/sysroot.lock")
409
410    fixme = {}
411    seendirs = set()
412    postinsts = []
413    multilibs = {}
414    manifests = {}
415    # All files that we're going to be installing, to find conflicts.
416    fileset = {}
417
418    invalidate_tasks = set()
419    for f in os.listdir(depdir):
420        removed = []
421        if not f.endswith(".complete"):
422            continue
423        f = depdir + "/" + f
424        if os.path.islink(f) and not os.path.exists(f):
425            bb.note("%s no longer exists, removing from sysroot" % f)
426            lnk = os.readlink(f.replace(".complete", ""))
427            sstate_clean_manifest(depdir + "/" + lnk, d, canrace=True, prefix=workdir)
428            os.unlink(f)
429            os.unlink(f.replace(".complete", ""))
430            removed.append(os.path.basename(f.replace(".complete", "")))
431
432        # If we've removed files from the sysroot above, the task that installed them may still
433        # have a stamp file present for the task. This is probably invalid right now but may become
434        # valid again if the user were to change configuration back for example. Since we've removed
435        # the files a task might need, remove the stamp file too to force it to rerun.
436        # YOCTO #14790
437        if removed:
438            for i in glob.glob(depdir + "/index.*"):
439                if i.endswith("." + mytaskname):
440                    continue
441                with open(i, "r") as f:
442                    for l in f:
443                        if l.startswith("TaskDeps:"):
444                            continue
445                        l = l.strip()
446                        if l in removed:
447                            invalidate_tasks.add(i.rsplit(".", 1)[1])
448                            break
449    for t in invalidate_tasks:
450        bb.note("Invalidating stamps for task %s" % t)
451        bb.build.clean_stamp(t, d)
452
453    installed = []
454    for dep in configuredeps:
455        c = setscenedeps[dep][0]
456        if mytaskname in ["do_sdk_depends", "do_populate_sdk_ext"] and c.endswith("-initial"):
457            bb.note("Skipping initial setscene dependency %s for installation into the sysroot" % c)
458            continue
459        installed.append(c)
460
461    # We want to remove anything which this task previously installed but is no longer a dependency
462    taskindex = depdir + "/" + "index." + mytaskname
463    if os.path.exists(taskindex):
464        potential = []
465        with open(taskindex, "r") as f:
466            for l in f:
467                l = l.strip()
468                if l not in installed:
469                    fl = depdir + "/" + l
470                    if not os.path.exists(fl):
471                        # Was likely already uninstalled
472                        continue
473                    potential.append(l)
474        # We need to ensure no other task needs this dependency. We hold the sysroot
475        # lock so we ca search the indexes to check
476        if potential:
477            for i in glob.glob(depdir + "/index.*"):
478                if i.endswith("." + mytaskname):
479                    continue
480                with open(i, "r") as f:
481                    for l in f:
482                        if l.startswith("TaskDeps:"):
483                            prevtasks = l.split()[1:]
484                            if mytaskname in prevtasks:
485                                # We're a dependency of this task so we can clear items out the sysroot
486                                break
487                        l = l.strip()
488                        if l in potential:
489                            potential.remove(l)
490        for l in potential:
491            fl = depdir + "/" + l
492            bb.note("Task %s no longer depends on %s, removing from sysroot" % (mytaskname, l))
493            lnk = os.readlink(fl)
494            sstate_clean_manifest(depdir + "/" + lnk, d, canrace=True, prefix=workdir)
495            os.unlink(fl)
496            os.unlink(fl + ".complete")
497
498    msg_exists = []
499    msg_adding = []
500
501    # Handle all removals first since files may move between recipes
502    for dep in configuredeps:
503        c = setscenedeps[dep][0]
504        if c not in installed:
505            continue
506        taskhash = setscenedeps[dep][5]
507        taskmanifest = depdir + "/" + c + "." + taskhash
508
509        if os.path.exists(depdir + "/" + c):
510            lnk = os.readlink(depdir + "/" + c)
511            if lnk == c + "." + taskhash and os.path.exists(depdir + "/" + c + ".complete"):
512                continue
513            else:
514                bb.note("%s exists in sysroot, but is stale (%s vs. %s), removing." % (c, lnk, c + "." + taskhash))
515                sstate_clean_manifest(depdir + "/" + lnk, d, canrace=True, prefix=workdir)
516                os.unlink(depdir + "/" + c)
517                if os.path.lexists(depdir + "/" + c + ".complete"):
518                    os.unlink(depdir + "/" + c + ".complete")
519        elif os.path.lexists(depdir + "/" + c):
520            os.unlink(depdir + "/" + c)
521
522    binfiles = {}
523    # Now handle installs
524    for dep in sorted(configuredeps):
525        c = setscenedeps[dep][0]
526        if c not in installed:
527            continue
528        taskhash = setscenedeps[dep][5]
529        taskmanifest = depdir + "/" + c + "." + taskhash
530
531        if os.path.exists(depdir + "/" + c):
532            lnk = os.readlink(depdir + "/" + c)
533            if lnk == c + "." + taskhash and os.path.exists(depdir + "/" + c + ".complete"):
534                msg_exists.append(c)
535                continue
536
537        msg_adding.append(c)
538
539        os.symlink(c + "." + taskhash, depdir + "/" + c)
540
541        manifest, d2 = oe.sstatesig.find_sstate_manifest(c, setscenedeps[dep][2], "populate_sysroot", d, multilibs)
542        if d2 is not d:
543            # If we don't do this, the recipe sysroot will be placed in the wrong WORKDIR for multilibs
544            # We need a consistent WORKDIR for the image
545            d2.setVar("WORKDIR", d.getVar("WORKDIR"))
546        destsysroot = d2.getVar("RECIPE_SYSROOT")
547        # We put allarch recipes into the default sysroot
548        if manifest and "allarch" in manifest:
549            destsysroot = d.getVar("RECIPE_SYSROOT")
550
551        native = False
552        if c.endswith("-native") or "-cross-" in c or "-crosssdk" in c:
553            native = True
554
555        if manifest:
556            newmanifest = collections.OrderedDict()
557            targetdir = destsysroot
558            if native:
559                targetdir = recipesysrootnative
560            if targetdir not in fixme:
561                fixme[targetdir] = []
562            fm = fixme[targetdir]
563
564            with open(manifest, "r") as f:
565                manifests[dep] = manifest
566                for l in f:
567                    l = l.strip()
568                    if l.endswith("/fixmepath"):
569                        fm.append(l)
570                        continue
571                    if l.endswith("/fixmepath.cmd"):
572                        continue
573                    dest = l.replace(stagingdir, "")
574                    dest = "/" + "/".join(dest.split("/")[3:])
575                    newmanifest[l] = targetdir + dest
576
577                    # Check if files have already been installed by another
578                    # recipe and abort if they have, explaining what recipes are
579                    # conflicting.
580                    hashname = targetdir + dest
581                    if not hashname.endswith("/"):
582                        if hashname in fileset:
583                            bb.fatal("The file %s is installed by both %s and %s, aborting" % (dest, c, fileset[hashname]))
584                        else:
585                            fileset[hashname] = c
586
587            # Having multiple identical manifests in each sysroot eats diskspace so
588            # create a shared pool of them and hardlink if we can.
589            # We create the manifest in advance so that if something fails during installation,
590            # or the build is interrupted, subsequent exeuction can cleanup.
591            sharedm = sharedmanifests + "/" + os.path.basename(taskmanifest)
592            if not os.path.exists(sharedm):
593                smlock = bb.utils.lockfile(sharedm + ".lock")
594                # Can race here. You'd think it just means we may not end up with all copies hardlinked to each other
595                # but python can lose file handles so we need to do this under a lock.
596                if not os.path.exists(sharedm):
597                    with open(sharedm, 'w') as m:
598                       for l in newmanifest:
599                           dest = newmanifest[l]
600                           m.write(dest.replace(workdir + "/", "") + "\n")
601                bb.utils.unlockfile(smlock)
602            try:
603                os.link(sharedm, taskmanifest)
604            except OSError as err:
605                if err.errno == errno.EXDEV:
606                    bb.utils.copyfile(sharedm, taskmanifest)
607                else:
608                    raise
609            # Finally actually install the files
610            for l in newmanifest:
611                    dest = newmanifest[l]
612                    if l.endswith("/"):
613                        staging_copydir(l, targetdir, dest, seendirs)
614                        continue
615                    if "/bin/" in l or "/sbin/" in l:
616                        # defer /*bin/* files until last in case they need libs
617                        binfiles[l] = (targetdir, dest)
618                    else:
619                        staging_copyfile(l, targetdir, dest, postinsts, seendirs)
620
621    # Handle deferred binfiles
622    for l in binfiles:
623        (targetdir, dest) = binfiles[l]
624        staging_copyfile(l, targetdir, dest, postinsts, seendirs)
625
626    bb.note("Installed into sysroot: %s" % str(msg_adding))
627    bb.note("Skipping as already exists in sysroot: %s" % str(msg_exists))
628
629    for f in fixme:
630        staging_processfixme(fixme[f], f, recipesysroot, recipesysrootnative, d)
631
632    for p in sorted(postinsts):
633        bb.note("Running postinst {}, output:\n{}".format(p, subprocess.check_output(p, shell=True, stderr=subprocess.STDOUT)))
634
635    for dep in manifests:
636        c = setscenedeps[dep][0]
637        os.symlink(manifests[dep], depdir + "/" + c + ".complete")
638
639    with open(taskindex, "w") as f:
640        f.write("TaskDeps: " + " ".join(owntaskdeps) + "\n")
641        for l in sorted(installed):
642            f.write(l + "\n")
643
644    bb.utils.unlockfile(lock)
645}
646extend_recipe_sysroot[vardepsexclude] += "MACHINE_ARCH PACKAGE_EXTRA_ARCHS SDK_ARCH BUILD_ARCH SDK_OS BB_TASKDEPDATA"
647
648do_prepare_recipe_sysroot[deptask] = "do_populate_sysroot"
649python do_prepare_recipe_sysroot () {
650    bb.build.exec_func("extend_recipe_sysroot", d)
651}
652addtask do_prepare_recipe_sysroot before do_configure after do_fetch
653
654python staging_taskhandler() {
655    bbtasks = e.tasklist
656    for task in bbtasks:
657        deps = d.getVarFlag(task, "depends")
658        if task != 'do_prepare_recipe_sysroot' and (task == "do_configure" or (deps and "populate_sysroot" in deps)):
659            d.prependVarFlag(task, "prefuncs", "extend_recipe_sysroot ")
660}
661staging_taskhandler[eventmask] = "bb.event.RecipeTaskPreProcess"
662addhandler staging_taskhandler
663
664
665#
666# Target build output, stored in do_populate_sysroot or do_package can depend
667# not only upon direct dependencies but also indirect ones. A good example is
668# linux-libc-headers. The toolchain depends on this but most target recipes do
669# not. There are some headers which are not used by the toolchain build and do
670# not change the toolchain task output, hence the task hashes can change without
671# changing the sysroot output of that recipe yet they can influence others.
672#
673# A specific example is rtc.h which can change rtcwake.c in util-linux but is not
674# used in the glibc or gcc build. To account for this, we need to account for the
675# populate_sysroot hashes in the task output hashes.
676#
677python target_add_sysroot_deps () {
678    current_task = "do_" + d.getVar("BB_CURRENTTASK")
679    if current_task not in ["do_populate_sysroot", "do_package"]:
680        return
681
682    pn = d.getVar("PN")
683    if pn.endswith("-native"):
684        return
685
686    taskdepdata = d.getVar("BB_TASKDEPDATA", False)
687    deps = {}
688    for dep in taskdepdata.values():
689        if dep[1] == "do_populate_sysroot" and not dep[0].endswith(("-native", "-initial")) and "-cross-" not in dep[0] and dep[0] != pn:
690            deps[dep[0]] = dep[6]
691
692    d.setVar("HASHEQUIV_EXTRA_SIGDATA", "\n".join("%s: %s" % (k, deps[k]) for k in sorted(deps.keys())))
693}
694SSTATECREATEFUNCS += "target_add_sysroot_deps"
695
696