#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: MIT
#

WORKDIR_PKGDATA = "${WORKDIR}/pkgdata-sysroot"

def package_populate_pkgdata_dir(pkgdatadir, d):
    import glob

    postinsts = []
    seendirs = set()
    stagingdir = d.getVar("PKGDATA_DIR")
    pkgarchs = ['${MACHINE_ARCH}']
    pkgarchs = pkgarchs + list(reversed(d.getVar("PACKAGE_EXTRA_ARCHS").split()))
    pkgarchs.append('allarch')

    bb.utils.mkdirhier(pkgdatadir)
    for pkgarch in pkgarchs:
        for manifest in glob.glob(d.expand("${SSTATE_MANIFESTS}/manifest-%s-*.packagedata" % pkgarch)):
            with open(manifest, "r") as f:
                for l in f:
                    l = l.strip()
                    dest = l.replace(stagingdir, "")
                    if l.endswith("/"):
                        staging_copydir(l, pkgdatadir, dest, seendirs)
                        continue
                    try:
                        staging_copyfile(l, pkgdatadir, dest, postinsts, seendirs)
                    except FileExistsError:
                        continue

python package_prepare_pkgdata() {
    import copy
    import glob

    taskdepdata = d.getVar("BB_TASKDEPDATA", False)
    mytaskname = d.getVar("BB_RUNTASK")
    if mytaskname.endswith("_setscene"):
        mytaskname = mytaskname.replace("_setscene", "")
    workdir = d.getVar("WORKDIR")
    pn = d.getVar("PN")
    stagingdir = d.getVar("PKGDATA_DIR")
    pkgdatadir = d.getVar("WORKDIR_PKGDATA")

    # Detect bitbake -b usage
    nodeps = d.getVar("BB_LIMITEDDEPS") or False
    if nodeps:
        staging_package_populate_pkgdata_dir(pkgdatadir, d)
        return

    start = None
    configuredeps = []
    for dep in taskdepdata:
        data = taskdepdata[dep]
        if data[1] == mytaskname and data[0] == pn:
            start = dep
            break
    if start is None:
        bb.fatal("Couldn't find ourself in BB_TASKDEPDATA?")

    # We need to figure out which sysroot files we need to expose to this task.
    # This needs to match what would get restored from sstate, which is controlled
    # ultimately by calls from bitbake to setscene_depvalid().
    # That function expects a setscene dependency tree. We build a dependency tree
    # condensed to inter-sstate task dependencies, similar to that used by setscene
    # tasks. We can then call into setscene_depvalid() and decide
    # which dependencies we can "see" and should expose in the recipe specific sysroot.
    setscenedeps = copy.deepcopy(taskdepdata)

    start = set([start])

    sstatetasks = d.getVar("SSTATETASKS").split()
    # Add recipe specific tasks referenced by setscene_depvalid()
    sstatetasks.append("do_stash_locale")

    # If start is an sstate task (like do_package) we need to add in its direct dependencies
    # else the code below won't recurse into them.
    for dep in set(start):
        for dep2 in setscenedeps[dep][3]:
            start.add(dep2)
        start.remove(dep)

    # Create collapsed do_populate_sysroot -> do_populate_sysroot tree
    for dep in taskdepdata:
        data = setscenedeps[dep]
        if data[1] not in sstatetasks:
            for dep2 in setscenedeps:
                data2 = setscenedeps[dep2]
                if dep in data2[3]:
                    data2[3].update(setscenedeps[dep][3])
                    data2[3].remove(dep)
            if dep in start:
                start.update(setscenedeps[dep][3])
                start.remove(dep)
            del setscenedeps[dep]

    # Remove circular references
    for dep in setscenedeps:
        if dep in setscenedeps[dep][3]:
            setscenedeps[dep][3].remove(dep)

    # Direct dependencies should be present and can be depended upon
    for dep in set(start):
        if setscenedeps[dep][1] == "do_packagedata":
            if dep not in configuredeps:
                configuredeps.append(dep)

    msgbuf = []
    # Call into setscene_depvalid for each sub-dependency and only copy sysroot files
    # for ones that would be restored from sstate.
    done = list(start)
    next = list(start)
    while next:
        new = []
        for dep in next:
            data = setscenedeps[dep]
            for datadep in data[3]:
                if datadep in done:
                    continue
                taskdeps = {}
                taskdeps[dep] = setscenedeps[dep][:2]
                taskdeps[datadep] = setscenedeps[datadep][:2]
                retval = setscene_depvalid(datadep, taskdeps, [], d, msgbuf)
                done.append(datadep)
                new.append(datadep)
                if retval:
                    msgbuf.append("Skipping setscene dependency %s" % datadep)
                    continue
                if datadep not in configuredeps and setscenedeps[datadep][1] == "do_packagedata":
                    configuredeps.append(datadep)
                    msgbuf.append("Adding dependency on %s" % setscenedeps[datadep][0])
                else:
                    msgbuf.append("Following dependency on %s" % setscenedeps[datadep][0])
        next = new

    # This logging is too verbose for day to day use sadly
    #bb.debug(2, "\n".join(msgbuf))

    seendirs = set()
    postinsts = []
    multilibs = {}
    manifests = {}

    msg_adding = []

    for dep in configuredeps:
        c = setscenedeps[dep][0]
        msg_adding.append(c)

        manifest, d2 = oe.sstatesig.find_sstate_manifest(c, setscenedeps[dep][2], "packagedata", d, multilibs)
        destsysroot = pkgdatadir

        if manifest:
            targetdir = destsysroot
            with open(manifest, "r") as f:
                manifests[dep] = manifest
                for l in f:
                    l = l.strip()
                    dest = targetdir + l.replace(stagingdir, "")
                    if l.endswith("/"):
                        staging_copydir(l, targetdir, dest, seendirs)
                        continue
                    staging_copyfile(l, targetdir, dest, postinsts, seendirs)

    bb.note("Installed into pkgdata-sysroot: %s" % str(msg_adding))

}
package_prepare_pkgdata[cleandirs] = "${WORKDIR_PKGDATA}"
package_prepare_pkgdata[vardepsexclude] += "MACHINE_ARCH PACKAGE_EXTRA_ARCHS SDK_ARCH BUILD_ARCH SDK_OS BB_TASKDEPDATA SSTATETASKS"


