# Copyright 2020 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import multiprocessing
import os

#
# Exported functions
#
from pmb.config.load import load
from pmb.config.save import save
from pmb.config.merge_with_args import merge_with_args


#
# Exported variables (internal configuration)
#
version = "1.17.0"
pmb_src = os.path.normpath(os.path.realpath(__file__) + "/../../..")
apk_keys_path = pmb_src + "/pmb/data/keys"

# Update this frequently to prevent a MITM attack with an outdated version
# (which may contain a vulnerable apk/libressl, and allows an attacker to
# exploit the system!)
apk_tools_static_min_version = "2.10.5-r0"

# postmarketOS aports compatibility (checked against "version" in pmaports.cfg)
pmaports_min_version = "5"

# Version of the work folder (as asked during 'pmbootstrap init'). Increase
# this number, whenever migration is required and provide the migration code,
# see migrate_work_folder()).
work_version = 4

# Programs that pmbootstrap expects to be available from the host system. Keep
# in sync with README.md, and try to keep the list as small as possible. The
# idea is to run almost everything in Alpine chroots.
required_programs = ["git", "openssl", "ps"]

# Keys saved in the config file (mostly what we ask in 'pmbootstrap init')
config_keys = ["ccache_size", "device", "extra_packages", "hostname", "jobs",
               "kernel", "keymap", "nonfree_firmware", "nonfree_userland",
               "ssh_keys", "timezone", "ui", "user", "work", "aports"]

# Config file/commandline default values
# $WORK gets replaced with the actual value for args.work (which may be
# overridden on the commandline)
defaults = {
    "alpine_version": "edge",  # alternatively: latest-stable
    "aports": "$WORK/cache_git/pmaports",
    "ccache_size": "5G",
    # aes-xts-plain64 would be better, but this is not supported on LineageOS
    # kernel configs
    "cipher": "aes-cbc-plain64",
    "config": os.path.expanduser("~") + "/.config/pmbootstrap.cfg",
    "device": "samsung-i9100",
    "extra_packages": "none",
    "fork_alpine": False,
    "hostname": "",
    # A higher value is typically desired, but this can lead to VERY long open
    # times on slower devices due to host systems being MUCH faster than the
    # target device (see issue #429).
    "iter_time": "200",
    "jobs": str(multiprocessing.cpu_count() + 1),
    "kernel": "stable",
    "keymap": "",
    "log": "$WORK/log.txt",
    "mirror_alpine": "http://dl-cdn.alpinelinux.org/alpine/",
    "mirrors_postmarketos": ["http://postmarketos1.brixit.nl/postmarketos/master"],
    "nonfree_firmware": True,
    "nonfree_userland": False,
    "port_distccd": "33632",
    "ssh_keys": False,
    "timezone": "GMT",
    "ui": "weston",
    "user": "user",
    "work": os.path.expanduser("~") + "/.local/var/pmbootstrap",
}

#
# CHROOT
#

# Usually the ID for the first user created is 1000. However, we want
# pmbootstrap to work even if the 'user' account inside the chroots has
# another UID, so we force it to be different.
chroot_uid_user = "12345"

# The PATH variable used inside all chroots
chroot_path = ":".join([
    "/usr/lib/ccache/bin",
    "/usr/local/sbin",
    "/usr/local/bin",
    "/usr/sbin:/usr/bin",
    "/sbin",
    "/bin"
])

# The PATH variable used on the host, to find the "chroot" and "sh"
# executables. As pmbootstrap runs as user, not as root, the location
# for the chroot executable may not be in the PATH (Debian).
chroot_host_path = os.environ["PATH"] + ":/usr/sbin/"

# Folders, that get mounted inside the chroot
# $WORK gets replaced with args.work
# $ARCH gets replaced with the chroot architecture (eg. x86_64, armhf)
chroot_mount_bind = {
    "/proc": "/proc",
    "$WORK/cache_apk_$ARCH": "/var/cache/apk",
    "$WORK/cache_ccache_$ARCH": "/mnt/pmbootstrap-ccache",
    "$WORK/cache_distfiles": "/var/cache/distfiles",
    "$WORK/cache_git": "/mnt/pmbootstrap-git",
    "$WORK/cache_rust": "/mnt/pmbootstrap-rust",
    "$WORK/config_abuild": "/mnt/pmbootstrap-abuild-config",
    "$WORK/config_apk_keys": "/etc/apk/keys",
    "$WORK/packages": "/mnt/pmbootstrap-packages",
}

# Building chroots (all chroots, except for the rootfs_ chroot) get symlinks in
# the "pmos" user's home folder pointing to mountfolders from above.
# Rust packaging is new and still a bit weird in Alpine and postmarketOS. As of
# writing, we only have one package (squeekboard), and use cargo to download
# the source of all dependencies at build time and compile it. Usually, this is
# a no-go, but at least until this is resolved properly, let's cache the
# dependencies and downloads as suggested in "Caching the Cargo home in CI":
# https://doc.rust-lang.org/cargo/guide/cargo-home.html
chroot_home_symlinks = {
    "/mnt/pmbootstrap-abuild-config": "/home/pmos/.abuild",
    "/mnt/pmbootstrap-ccache": "/home/pmos/.ccache",
    "/mnt/pmbootstrap-packages": "/home/pmos/packages/pmos",
    "/mnt/pmbootstrap-rust/registry/index": "/home/pmos/.cargo/registry/index",
    "/mnt/pmbootstrap-rust/registry/cache": "/home/pmos/.cargo/registry/cache",
    "/mnt/pmbootstrap-rust/git/db": "/home/pmos/.cargo/git/db",
}

# Device nodes to be created in each chroot. Syntax for each entry:
# [permissions, type, major, minor, name]
chroot_device_nodes = [
    [666, "c", 1, 3, "null"],
    [666, "c", 1, 5, "zero"],
    [666, "c", 1, 7, "full"],
    [644, "c", 1, 8, "random"],
    [644, "c", 1, 9, "urandom"],
]

# Age in hours that we keep the APKINDEXes before downloading them again.
# You can force-update them with 'pmbootstrap update'.
apkindex_retention_time = 4


# When chroot is considered outdated (in seconds)
chroot_outdated = 3600 * 24 * 2

#
# BUILD
#
# Officially supported host/target architectures for postmarketOS. Only
# specify architectures supported by Alpine here. For cross-compiling,
# we need to generate the "musl-$ARCH", "binutils-$ARCH" and "gcc-$ARCH"
# packages (use "pmbootstrap aportgen musl-armhf" etc.).
build_device_architectures = ["armhf", "armv7", "aarch64", "x86_64", "x86"]

# Packages, that will be installed in a chroot before it builds packages
# for the first time
build_packages = ["abuild", "build-base", "ccache", "git"]

# fnmatch for supported pkgnames, that can be directly compiled inside
# the native chroot and a cross-compiler, without using distcc
build_cross_native = ["linux-*", "arch-bin-masquerade"]

# Necessary kernel config options
necessary_kconfig_options = {
    ">=0.0.0": {  # all versions
        "all": {  # all arches
            "ANDROID_PARANOID_NETWORK": False,
            "BLK_DEV_INITRD": True,
            "CGROUPS": True,
            "DEVTMPFS": True,
            "DM_CRYPT": True,
            "EXT4_FS": True,
            "KINETO_GAN": False,
            "PFT": False,
            "SYSVIPC": True,
            "VT": True,
            "USE_VFB": False,
        }
    },
    ">=4.0.0": {
        "all": {
            "UEVENT_HELPER": True,
        },
    },
    "<5.2.0": {
        "armhf armv7 x86": {
            "LBDAF": True
        }
    }
}

#
# PARSE
#

# Variables belonging to a package or subpackage in APKBUILD files
apkbuild_package_attributes = {
    "pkgdesc": {},
    "depends": {"array": True},
    "provides": {"array": True},
}

# Variables in APKBUILD files, that get parsed
apkbuild_attributes = {
    **apkbuild_package_attributes,

    "arch": {"array": True},
    "depends_dev": {"array": True},
    "makedepends": {"array": True},
    "checkdepends": {"array": True},
    "options": {"array": True},
    "pkgname": {},
    "pkgrel": {},
    "pkgver": {},
    "subpackages": {},
    "url": {},

    # cross-compilers
    "makedepends_build": {"array": True},
    "makedepends_host": {"array": True},

    # kernels
    "_flavor": {},
    "_device": {},
    "_kernver": {},

    # mesa
    "_llvmver": {},

    # Overridden packages
    "_pkgver": {},
    "_pkgname": {},

    # git commit
    "_commit": {},
    "source": {"array": True},
}

# Variables from deviceinfo. Reference: <https://postmarketos.org/deviceinfo>
deviceinfo_attributes = [
    # general
    "format_version",
    "name",
    "manufacturer",
    "codename",
    "year",
    "dtb",
    "modules_initfs",
    "arch",

    # device
    "keyboard",
    "external_storage",
    "screen_width",
    "screen_height",
    "dev_touchscreen",
    "dev_touchscreen_calibration",

    # bootloader
    "flash_method",
    "boot_filesystem",

    # flash
    "flash_heimdall_partition_kernel",
    "flash_heimdall_partition_initfs",
    "flash_heimdall_partition_system",
    "flash_fastboot_partition_kernel",
    "flash_fastboot_partition_system",
    "generate_legacy_uboot_initfs",
    "kernel_cmdline",
    "generate_bootimg",
    "bootimg_qcdt",
    "bootimg_dtb_second",
    "flash_offset_base",
    "flash_offset_kernel",
    "flash_offset_ramdisk",
    "flash_offset_second",
    "flash_offset_tags",
    "flash_pagesize",
    "flash_fastboot_max_size",
    "flash_sparse",
    "rootfs_image_sector_size",
    "sd_embed_firmware",
    "sd_embed_firmware_step_size",
    "partition_blacklist",
    "boot_part_start",

    # weston
    "weston_pixman_type",

    # keymaps
    "keymaps",
]

#
# INITFS
#
initfs_hook_prefix = "postmarketos-mkinitfs-hook-"
default_ip = "172.16.42.1"


#
# INSTALL
#

# Packages, that will be installed inside the native chroot to perform
# the installation to the device.
# util-linux: losetup, fallocate
install_native_packages = ["cryptsetup", "util-linux", "e2fsprogs", "parted", "dosfstools"]
install_device_packages = ["postmarketos-base"]

# Groups for the default user
install_user_groups = ["wheel", "video", "audio", "input", "plugdev", "netdev"]

#
# FLASH
#

flash_methods = ["fastboot", "heimdall", "0xffff", "uuu", "none"]

# These folders will be mounted at the same location into the native
# chroot, before the flash programs get started.
flash_mount_bind = [
    "/sys/bus/usb/devices/",
    "/sys/dev/",
    "/sys/devices/",
    "/dev/bus/usb/"
]

"""
Flasher abstraction. Allowed variables:

$BOOT: Path to the /boot partition
$FLAVOR: Kernel flavor
$IMAGE: Path to the combined boot/rootfs image
$IMAGE_SPLIT_BOOT: Path to the (split) boot image
$IMAGE_SPLIT_ROOT: Path to the (split) rootfs image
$PARTITION_KERNEL: Partition to flash the kernel/boot.img to
$PARTITION_SYSTEM: Partition to flash the rootfs to

Fastboot specific: $KERNEL_CMDLINE
Heimdall specific: $PARTITION_INITFS
uuu specific: $UUU_SCRIPT
"""
flashers = {
    "fastboot": {
        "depends": ["android-tools"],
        "actions": {
            "list_devices": [["fastboot", "devices", "-l"]],
            "flash_rootfs": [["fastboot", "flash", "$PARTITION_SYSTEM",
                              "$IMAGE"]],
            "flash_kernel": [["fastboot", "flash", "$PARTITION_KERNEL",
                              "$BOOT/boot.img-$FLAVOR"]],
            "boot": [["fastboot", "--cmdline", "$KERNEL_CMDLINE",
                      "boot", "$BOOT/boot.img-$FLAVOR"]],
        },
    },
    # Some devices provide Fastboot but using Android boot images is not practical
    # for them (e.g. because they support booting from FAT32 partitions directly
    # and/or the Android boot partition is too small). This can be implemented
    # using --split (separate image files for boot and rootfs).
    # This flasher allows flashing the split image files using Fastboot.
    "fastboot-bootpart": {
        "split": True,
        "depends": ["android-tools"],
        "actions": {
            "list_devices": [["fastboot", "devices", "-l"]],
            "flash_rootfs": [["fastboot", "flash", "$PARTITION_SYSTEM",
                              "$IMAGE_SPLIT_ROOT"]],
            "flash_kernel": [["fastboot", "flash", "$PARTITION_KERNEL",
                              "$IMAGE_SPLIT_BOOT"]],
            # TODO: Add support for boot
        },
    },
    # Some Samsung devices need the initramfs to be baked into the kernel (e.g.
    # i9070, i9100). We want the initramfs to be generated after the kernel was
    # built, so we put the real initramfs on another partition (e.g. RECOVERY)
    # and load it from the initramfs in the kernel. This method is called
    # "isorec" (isolated recovery), a term coined by Lanchon.
    "heimdall-isorec": {
        "depends": ["heimdall"],
        "actions": {
            "list_devices": [["heimdall", "detect"]],
            "flash_rootfs": [
                ["heimdall_wait_for_device.sh"],
                ["heimdall", "flash", "--$PARTITION_SYSTEM", "$IMAGE"]],
            "flash_kernel": [["heimdall_flash_kernel.sh",
                              "$BOOT/initramfs-$FLAVOR", "$PARTITION_INITFS",
                              "$BOOT/vmlinuz-$FLAVOR", "$PARTITION_KERNEL"]]
        },
    },
    # Some Samsung devices need a 'boot.img' file, just like the one generated
    # fastboot compatible devices. Example: s7562, n7100
    "heimdall-bootimg": {
        "depends": ["heimdall"],
        "actions": {
            "list_devices": [["heimdall", "detect"]],
            "flash_rootfs": [
                ["heimdall_wait_for_device.sh"],
                ["heimdall", "flash", "--$PARTITION_SYSTEM", "$IMAGE"]],
            "flash_kernel": [
                ["heimdall_wait_for_device.sh"],
                ["heimdall", "flash", "--$PARTITION_KERNEL", "$BOOT/boot.img-$FLAVOR"]],
        },
    },
    "adb": {
        "depends": ["android-tools"],
        "actions": {
            "list_devices": [["adb", "-P", "5038", "devices"]],
            "sideload": [["echo", "< wait for any device >"],
                         ["adb", "-P", "5038", "wait-for-usb-sideload"],
                         ["adb", "-P", "5038", "sideload",
                          "$RECOVERY_ZIP"]],
        }
    },
    "uuu": {
        "depends": ["uuu"],
        "actions": {
            "flash_rootfs": [
                # There's a bug(?) in uuu where it clobbers the path in the cmd
                # script if the script is not in pwd...
                ["cp", "$UUU_SCRIPT", "./flash_script.lst"],
                ["uuu", "flash_script.lst"],
            ],
        },
    }
}

#
# GIT
#
git_repos = {
    "aports_upstream": "https://gitlab.alpinelinux.org/alpine/aports.git",
    "pmaports": "https://gitlab.com/postmarketOS/pmaports.git",
}


#
# APORTGEN
#
aportgen = {
    "cross": {
        "prefixes": ["binutils", "busybox-static", "gcc", "musl", "grub-efi"],
        "confirm_overwrite": False,
    },
    "device/testing": {
        "prefixes": ["device", "linux"],
        "confirm_overwrite": True,
    }
}

#
# NEWAPKBUILD
# Options passed through to the "newapkbuild" command from Alpine Linux. They
# are duplicated here, so we can use Python's argparse for argument parsing and
# help page display. The -f (force) flag is not defined here, as we use that in
# the Python code only and don't pass it through.
#
newapkbuild_arguments_strings = [
    ["-n", "pkgname", "set package name (only use with SRCURL)"],
    ["-d", "pkgdesc", "set package description"],
    ["-l", "license", "set package license identifier from"
                      " <https://spdx.org/licenses/>"],
    ["-u", "url", "set package URL"],
]
newapkbuild_arguments_switches_pkgtypes = [
    ["-a", "autotools", "create autotools package (use ./configure ...)"],
    ["-C", "cmake", "create CMake package (assume cmake/ is there)"],
    ["-m", "meson", "create meson package (assume meson.build is there)"],
    ["-p", "perl", "create perl package (assume Makefile.PL is there)"],
    ["-y", "python", "create python package (assume setup.py is there)"],
]
newapkbuild_arguments_switches_other = [
    ["-s", "sourceforge", "use sourceforge source URL"],
    ["-c", "copy_samples", "copy a sample init.d, conf.d and install script"],
]

#
# UPGRADE
#
# Patterns of package names to ignore for automatic pmaport upgrading ("pmbootstrap aportupgrade --all")
upgrade_ignore = ["device-*", "firmware-*", "linux-*", "postmarketos-*", "*-aarch64", "*-armhf", "*-armv7"]
