# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
#
# SPDX-License-Identifier: MIT
#
# vcontainer-tarball.bb
# ===========================================================================
# Standalone SDK-style tarball for vdkr/vpdmn container tools
# ===========================================================================
#
# This recipe uses Yocto's populate_sdk infrastructure to create a
# relocatable standalone distribution of vdkr (Docker) and vpdmn (Podman).
#
# USAGE:
#   MACHINE=qemux86-64 bitbake vcontainer-tarball
#   MACHINE=qemuarm64 bitbake vcontainer-tarball
#
# OUTPUT:
#   tmp/deploy/sdk/vcontainer-standalone-<arch>.tar.xz
#   tmp/deploy/sdk/vcontainer-standalone-<arch>.sh (self-extracting installer)
#
# ===========================================================================

SUMMARY = "Standalone SDK tarball for vdkr and vpdmn container tools"
DESCRIPTION = "A relocatable standalone distribution of vdkr (Docker) and \
               vpdmn (Podman) CLI tools using Yocto SDK infrastructure."
HOMEPAGE = "https://git.yoctoproject.org/meta-virtualization/"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

# Use our custom SDK installer template with vcontainer-specific messages
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
TOOLCHAIN_SHAR_EXT_TMPL = "${THISDIR}/files/toolchain-shar-extract.sh"

# Declare script sources so BitBake tracks changes and rebuilds when they change
SRC_URI = "\
    file://vrunner.sh \
    file://vrunner-backend-qemu.sh \
    file://vrunner-backend-xen.sh \
    file://vcontainer-common.sh \
    file://vdkr.sh \
    file://vpdmn.sh \
    file://toolchain-shar-extract.sh \
"

# No target sysroot - host tools only (like buildtools-tarball)
TOOLCHAIN_TARGET_TASK = ""
TARGET_ARCH = "none"
TARGET_OS = "none"

# Host tools to include via SDK
# Note: nativesdk-qemu-vcontainer is a minimal QEMU without OpenGL/virgl
# to avoid mesa -> llvm -> clang build dependency chain
TOOLCHAIN_HOST_TASK = "\
    nativesdk-sdk-provides-dummy \
    nativesdk-qemu-vcontainer \
    nativesdk-socat \
    nativesdk-expect \
    "

# SDK naming and metadata
TOOLCHAIN_OUTPUTNAME = "vcontainer-standalone"
SDK_TITLE = "vcontainer tools (vdkr/vpdmn)"

# SDK configuration (same pattern as buildtools-tarball)
MULTIMACH_TARGET_SYS = "${SDK_ARCH}-nativesdk${SDK_VENDOR}-${SDK_OS}"
PACKAGE_ARCH = "${SDK_ARCH}_${SDK_OS}"
PACKAGE_ARCHS = ""
SDK_PACKAGE_ARCHS += "vcontainer-dummy-${SDKPKGSUFFIX}"

RDEPENDS = "${TOOLCHAIN_HOST_TASK}"
EXCLUDE_FROM_WORLD = "1"

inherit populate_sdk
inherit toolchain-scripts-base
inherit nopackages
inherit container-registry

# Must be set AFTER inherit populate_sdk (class sets it to target arch)
REAL_MULTIMACH_TARGET_SYS = "none"

# Disable tasks we don't need
deltask install
deltask populate_sysroot

# No config site needed
TOOLCHAIN_NEED_CONFIGSITE_CACHE = ""
INHIBIT_DEFAULT_DEPS = "1"

do_populate_sdk[stamp-extra-info] = "${PACKAGE_ARCH}"

# ===========================================================================
# Architecture mapping
# ===========================================================================
VCONTAINER_TARGET_ARCH = "${@d.getVar('MACHINE').replace('qemuarm64', 'aarch64').replace('qemux86-64', 'x86_64')}"
VCONTAINER_KERNEL_NAME = "${@'Image' if d.getVar('MACHINE') == 'qemuarm64' else 'bzImage'}"
VCONTAINER_MC = "${@'vruntime-aarch64' if d.getVar('MACHINE') == 'qemuarm64' else 'vruntime-x86-64'}"

# ===========================================================================
# Multiconfig dependencies for blobs
# ===========================================================================
# By default, builds BOTH x86_64 and aarch64 via mcdepends.
# To limit to a single architecture, set in local.conf:
#   VCONTAINER_ARCHITECTURES = "x86_64"           # x86_64 only
#   VCONTAINER_ARCHITECTURES = "aarch64"          # aarch64 only
#
# Helper function (optional - for auto-detection if needed)
def get_available_architectures(d):
    import os
    topdir = d.getVar('TOPDIR')
    available = []
    arch_info = {
        'x86_64': ('vruntime-x86-64', 'qemux86-64'),
        'aarch64': ('vruntime-aarch64', 'qemuarm64'),
    }
    for arch, (mc, machine) in arch_info.items():
        deploy_dir = os.path.join(topdir, 'tmp-%s' % mc, 'deploy', 'images', machine, 'vdkr', arch)
        if os.path.isdir(deploy_dir):
            available.append(arch)
    # If nothing available yet, default to current MACHINE's arch (will be built)
    if not available:
        available.append(d.getVar('VCONTAINER_TARGET_ARCH'))
    return " ".join(sorted(available))

# Default to both architectures. Override in local.conf if you only want one:
#   VCONTAINER_ARCHITECTURES = "x86_64"
#   VCONTAINER_ARCHITECTURES = "aarch64"
VCONTAINER_ARCHITECTURES ?= "x86_64 aarch64"

# Conditionally set mcdepends based on available multiconfigs
# (avoids parse errors when BBMULTICONFIG is not set, e.g. yocto-check-layer)
python () {
    bbmulticonfig = (d.getVar('BBMULTICONFIG') or "").split()
    mcdeps = []
    for mc in ['vruntime-x86-64', 'vruntime-aarch64']:
        if mc in bbmulticonfig:
            mcdeps.append('mc::%s:vdkr-initramfs-create:do_deploy' % mc)
            mcdeps.append('mc::%s:vpdmn-initramfs-create:do_deploy' % mc)
    if mcdeps:
        d.setVarFlag('do_populate_sdk', 'mcdepends', ' '.join(mcdeps))

    # Build-time banner is in do_populate_sdk:append() below
}

# ===========================================================================
# Custom SDK files - environment script AND blobs/scripts
# ===========================================================================
# IMPORTANT: All custom files must be added in create_sdk_files:append()
# because SDK_POSTPROCESS_COMMAND runs archive_sdk BEFORE any appended commands.
# The order is: create_sdk_files -> check_sdk_sysroots -> archive_sdk -> ...
# ===========================================================================
create_sdk_files:append () {
    # Variables - these are Yocto variables expanded at parse time
    TOPDIR="${TOPDIR}"
    THISDIR="${THISDIR}"
    ARCHITECTURES="${VCONTAINER_ARCHITECTURES}"
    CONTAINER_REGISTRY_SECURE="${CONTAINER_REGISTRY_SECURE}"
    CONTAINER_REGISTRY_CA_CERT="${CONTAINER_REGISTRY_CA_CERT}"

    # SDK output directory
    SDK_OUT="${SDK_OUTPUT}/${SDKPATH}"

    bbnote "TOPDIR=${TOPDIR}"
    bbnote "THISDIR=${THISDIR}"
    bbnote "ARCHITECTURES=${ARCHITECTURES}"
    bbnote "SDK_OUT=${SDK_OUT}"

    # -----------------------------------------------------------------------
    # Step 1: Copy blobs and scripts (MUST happen before archive_sdk)
    # -----------------------------------------------------------------------
    VDKR_INCLUDED=0
    VPDMN_INCLUDED=0

    # Copy blobs for each architecture
    for ARCH in ${ARCHITECTURES}; do
        bbnote "Adding vcontainer blobs for ${ARCH}"

        # Determine multiconfig and kernel name for this arch
        case "${ARCH}" in
            aarch64)
                MC="vruntime-aarch64"
                MC_MACHINE="qemuarm64"
                KERNEL="Image"
                ;;
            x86_64)
                MC="vruntime-x86-64"
                MC_MACHINE="qemux86-64"
                KERNEL="bzImage"
                ;;
            *)
                bbwarn "Unknown architecture: ${ARCH}"
                continue
                ;;
        esac

        MC_DEPLOY="${TOPDIR}/tmp-${MC}/deploy/images/${MC_MACHINE}"
        bbnote "MC_DEPLOY=${MC_DEPLOY} for ${ARCH}"

        # Create blob directories
        mkdir -p "${SDK_OUT}/vdkr-blobs/${ARCH}"
        mkdir -p "${SDK_OUT}/vpdmn-blobs/${ARCH}"

        # Copy vdkr blobs
        VDKR_SRC="${MC_DEPLOY}/vdkr/${ARCH}"
        if [ -d "${VDKR_SRC}" ]; then
            for blob in ${KERNEL} initramfs.cpio.gz rootfs.img; do
                if [ -f "${VDKR_SRC}/${blob}" ]; then
                    cp "${VDKR_SRC}/${blob}" "${SDK_OUT}/vdkr-blobs/${ARCH}/"
                    bbnote "Copied vdkr blob: ${ARCH}/${blob}"
                else
                    bbfatal "vdkr blob not found: ${VDKR_SRC}/${blob}"
                fi
            done
            VDKR_INCLUDED=1
        else
            bbfatal "vdkr blobs not found for ${ARCH}. Build them first with:
  bitbake mc:${MC}:vdkr-initramfs-create"
        fi

        # Copy vpdmn blobs
        VPDMN_SRC="${MC_DEPLOY}/vpdmn/${ARCH}"
        if [ -d "${VPDMN_SRC}" ]; then
            for blob in ${KERNEL} initramfs.cpio.gz rootfs.img; do
                if [ -f "${VPDMN_SRC}/${blob}" ]; then
                    cp "${VPDMN_SRC}/${blob}" "${SDK_OUT}/vpdmn-blobs/${ARCH}/"
                    bbnote "Copied vpdmn blob: ${ARCH}/${blob}"
                else
                    bbfatal "vpdmn blob not found: ${VPDMN_SRC}/${blob}"
                fi
            done
            VPDMN_INCLUDED=1
        else
            bbfatal "vpdmn blobs not found for ${ARCH}. Build them first with:
  bitbake mc:${MC}:vpdmn-initramfs-create"
        fi
    done

    # Copy scripts from layer
    FILES_DIR="${THISDIR}/files"

    # Copy shared scripts
    for script in vrunner.sh vrunner-backend-qemu.sh vrunner-backend-xen.sh vcontainer-common.sh; do
        if [ -f "${FILES_DIR}/${script}" ]; then
            cp "${FILES_DIR}/${script}" "${SDK_OUT}/"
            chmod 755 "${SDK_OUT}/${script}"
            bbnote "Copied ${script}"
        else
            bbfatal "${script} not found in ${FILES_DIR}"
        fi
    done

    # Copy and set up vdkr
    if [ "${VDKR_INCLUDED}" = "1" ] && [ -f "${FILES_DIR}/vdkr.sh" ]; then
        cp "${FILES_DIR}/vdkr.sh" "${SDK_OUT}/vdkr"
        chmod 755 "${SDK_OUT}/vdkr"
        # Create symlinks for each included architecture
        for ARCH in ${ARCHITECTURES}; do
            if [ -d "${SDK_OUT}/vdkr-blobs/${ARCH}" ]; then
                ln -sf vdkr "${SDK_OUT}/vdkr-${ARCH}"
                bbnote "Created symlink vdkr-${ARCH}"
            fi
        done
        bbnote "Installed vdkr"
    fi

    # Copy and set up vpdmn
    if [ "${VPDMN_INCLUDED}" = "1" ] && [ -f "${FILES_DIR}/vpdmn.sh" ]; then
        cp "${FILES_DIR}/vpdmn.sh" "${SDK_OUT}/vpdmn"
        chmod 755 "${SDK_OUT}/vpdmn"
        # Create symlinks for each included architecture
        for ARCH in ${ARCHITECTURES}; do
            if [ -d "${SDK_OUT}/vpdmn-blobs/${ARCH}" ]; then
                ln -sf vpdmn "${SDK_OUT}/vpdmn-${ARCH}"
                bbnote "Created symlink vpdmn-${ARCH}"
            fi
        done
        bbnote "Installed vpdmn"
    fi

    # Copy CA certificate for secure registry mode (if available)
    SECURE_MODE="${CONTAINER_REGISTRY_SECURE}"
    CA_CERT="${CONTAINER_REGISTRY_CA_CERT}"
    if [ "${SECURE_MODE}" = "1" ] && [ -f "${CA_CERT}" ]; then
        mkdir -p "${SDK_OUT}/registry"
        cp "${CA_CERT}" "${SDK_OUT}/registry/ca.crt"
        bbnote "Included secure registry CA certificate"
    elif [ "${SECURE_MODE}" = "1" ]; then
        bbwarn "Secure registry mode enabled but CA cert not found at ${CA_CERT}"
        bbwarn "Run: bitbake container-registry-index -c generate_registry_script"
    fi

    # Create README
    cat > "${SDK_OUT}/README.txt" <<EOF
vcontainer Standalone SDK
=========================

This is a self-contained, relocatable distribution of vdkr (Docker) and
vpdmn (Podman) CLI tools for cross-architecture container operations.

Quick Start:
  source init-env.sh
  vdkr-x86_64 images      # Docker for x86_64
  vpdmn-aarch64 images    # Podman for aarch64

Architectures included: ${ARCHITECTURES}

Contents:
  init-env.sh           - Environment setup script
  vdkr, vdkr-<arch>     - Docker CLI wrapper
  vpdmn, vpdmn-<arch>   - Podman CLI wrapper
  vrunner.sh            - Shared QEMU runner
  vcontainer-common.sh  - Shared CLI code
  vdkr-blobs/           - Docker QEMU blobs (per-arch subdirectories)
  vpdmn-blobs/          - Podman QEMU blobs (per-arch subdirectories)
  sysroots/             - SDK binaries (QEMU, socat, libraries)
  registry/ca.crt       - CA cert for secure registry (if CONTAINER_REGISTRY_SECURE=1)

Secure Registry Usage:
  vdkr --secure-registry --ca-cert registry/ca.crt pull myimage

Requirements:
  - Linux x86_64 host
  - KVM support recommended (for performance)

For more information:
  https://git.yoctoproject.org/meta-virtualization/
EOF

    bbnote "vcontainer blobs and scripts added to SDK"

    # -----------------------------------------------------------------------
    # Step 2: Create environment script (after blobs so we can check for them)
    # -----------------------------------------------------------------------

    # Remove ALL default SDK files - we create our own
    rm -f ${SDK_OUTPUT}/${SDKPATH}/site-config-*
    rm -f ${SDK_OUTPUT}/${SDKPATH}/version-*
    rm -f ${SDK_OUTPUT}/${SDKPATH}/environment-setup-*

    # Create vcontainer-specific environment script
    # Keep the environment-setup-* naming so SDK installer works
    script=${SDK_OUTPUT}/${SDKPATH}/environment-setup-${REAL_MULTIMACH_TARGET_SYS}

    # Create environment script
    # Set OECORE_NATIVE_SYSROOT temporarily for SDK relocation, then unset it
    # (like buildtools-tarball does to avoid confusing other Yocto tools)
    cat > $script <<'HEADER'
#!/bin/bash
# vcontainer environment setup script
# Source this file: source environment-setup-none
# Or use the symlink: source init-env.sh

VCONTAINER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HEADER
    # Yocto variables (${SDK_SYS}, ${SDKPATHNATIVE}) expand at parse time
    # Shell variables use $VAR to avoid Yocto expansion
    echo 'export OECORE_NATIVE_SYSROOT="'"${SDKPATHNATIVE}"'"' >> $script
    echo 'export PATH="$VCONTAINER_DIR:$OECORE_NATIVE_SYSROOT/usr/bin:/usr/bin:/bin:$PATH"' >> $script
    cat >> $script <<'FOOTER'

echo "vcontainer environment configured."
echo ""
FOOTER

    # Add usage info based on what's included (check files we just copied)
    if [ -f "${SDK_OUT}/vdkr" ]; then
        cat >> $script <<'ENVEOF'
echo "vdkr (Docker) commands:"
echo "  vdkr images              # List docker images"
echo "  vdkr vimport ./oci/ app  # Import OCI directory"
ENVEOF
    fi

    if [ -f "${SDK_OUT}/vpdmn" ]; then
        cat >> $script <<'ENVEOF'
echo "vpdmn (Podman) commands:"
echo "  vpdmn images             # List podman images"
echo "  vpdmn vimport ./oci/ app # Import OCI directory"
ENVEOF
    fi

    cat >> $script <<ENVEOF
echo ""
echo "Architectures: ${VCONTAINER_ARCHITECTURES}"
ENVEOF

    # Unset OECORE_NATIVE_SYSROOT to avoid confusing other Yocto tools
    # (same pattern as buildtools-tarball)
    echo '' >> $script
    echo '# Clean up - unset to avoid confusing other Yocto tools' >> $script
    echo 'unset OECORE_NATIVE_SYSROOT' >> $script

    # Replace placeholder with actual SDK_SYS
    sed -i -e "s:@SDK_SYS@:${SDK_SYS}:g" $script
    chmod 755 $script

    # Create init-env.sh symlink for convenience
    ln -sf environment-setup-${REAL_MULTIMACH_TARGET_SYS} ${SDK_OUTPUT}/${SDKPATH}/init-env.sh

    # Create version file
    echo "vcontainer SDK version: ${PV}" > ${SDK_OUTPUT}/${SDKPATH}/version.txt
    echo "Built: $(date)" >> ${SDK_OUTPUT}/${SDKPATH}/version.txt
    echo "Architectures: ${VCONTAINER_ARCHITECTURES}" >> ${SDK_OUTPUT}/${SDKPATH}/version.txt

    # -----------------------------------------------------------------------
    # Step 3: Remove unnecessary files to reduce SDK size (~100MB savings)
    # -----------------------------------------------------------------------
    bbnote "Removing unnecessary files from SDK..."

    # Remove locale data (~97MB) - not needed for vcontainer tools
    rm -rf ${SDK_OUTPUT}/${SDKPATHNATIVE}/usr/lib/locale
    bbnote "Removed locale data"

    # Remove QEMU user-mode emulators (we only need system emulators)
    # Keep: qemu-system-aarch64, qemu-system-x86_64
    for qemu_bin in ${SDK_OUTPUT}/${SDKPATHNATIVE}/usr/bin/qemu-*; do
        case "$(basename $qemu_bin)" in
            qemu-system-aarch64|qemu-system-x86_64|qemu-img|qemu-nbd)
                # Keep these
                ;;
            qemu-system-*)
                # Remove other system emulators (handled by QEMU_TARGETS now, but just in case)
                rm -f "$qemu_bin"
                ;;
            *)
                # Remove user-mode emulators (qemu-aarch64, qemu-arm, etc.)
                rm -f "$qemu_bin"
                bbnote "Removed $(basename $qemu_bin)"
                ;;
        esac
    done

    bbnote "SDK size optimization complete"
}

create_sdk_files[vardeps] += "VCONTAINER_TARGET_ARCH VCONTAINER_KERNEL_NAME VCONTAINER_MC"

# ===========================================================================
# Substitute custom placeholders in installer script
# ===========================================================================
# This runs AFTER archive_sdk to replace placeholders in our custom template
SDK_POSTPROCESS_COMMAND += "substitute_vcontainer_vars;"

substitute_vcontainer_vars () {
    # Replace placeholders in the installer script
    # Use SDKDEPLOYDIR (work directory) not SDK_DEPLOY (final deploy location)
    sed -i -e "s|@VCONTAINER_ARCHITECTURES@|${VCONTAINER_ARCHITECTURES}|g" ${SDKDEPLOYDIR}/${TOOLCHAIN_OUTPUTNAME}.sh
    bbnote "Substituted VCONTAINER_ARCHITECTURES=${VCONTAINER_ARCHITECTURES} in installer"
}

substitute_vcontainer_vars[vardeps] += "VCONTAINER_ARCHITECTURES"

# ===========================================================================
# Print usage information after SDK is built
# ===========================================================================
python do_populate_sdk:append() {
    import os

    deploy_dir = d.getVar('SDK_DEPLOY')
    toolchain_outputname = d.getVar('TOOLCHAIN_OUTPUTNAME')
    architectures = d.getVar('VCONTAINER_ARCHITECTURES').split()

    # Find the installer script
    installer = os.path.join(deploy_dir, toolchain_outputname + '.sh')
    installer_size = 0
    if os.path.exists(installer):
        installer_size = os.path.getsize(installer) // (1024 * 1024)

    # Check what was included
    sdk_output = d.getVar('SDK_OUTPUT')
    sdkpath = d.getVar('SDKPATH')
    sdk_out = os.path.join(sdk_output, sdkpath.lstrip('/'))
    vdkr_included = os.path.exists(os.path.join(sdk_out, 'vdkr'))
    vpdmn_included = os.path.exists(os.path.join(sdk_out, 'vpdmn'))

    bb.plain("")
    bb.plain("=" * 70)
    bb.plain("vcontainer SDK tarball created:")
    bb.plain("  %s" % installer)
    bb.plain("  Size: %d MB" % installer_size)
    bb.plain("  Architectures: %s" % " ".join(architectures))
    bb.plain("  vdkr (Docker): %s" % ("included" if vdkr_included else "NOT included"))
    bb.plain("  vpdmn (Podman): %s" % ("included" if vpdmn_included else "NOT included"))
    bb.plain("")
    bb.plain("To extract and use:")
    bb.plain("  %s -d /tmp/vcontainer -y" % installer)
    bb.plain("  cd /tmp/vcontainer")
    bb.plain("  source init-env.sh")
    for arch in architectures:
        if vdkr_included:
            bb.plain("  vdkr-%s images      # Docker for %s" % (arch, arch))
        if vpdmn_included:
            bb.plain("  vpdmn-%s images     # Podman for %s" % (arch, arch))
    bb.plain("=" * 70)
}
