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