import os
import infra

BASIC_CONFIG = \
    """
    BR2_TARGET_ROOTFS_CPIO=y
    # BR2_TARGET_ROOTFS_TAR is not set
    """


def has_broken_links(path):
    for root, dirs, files in os.walk(path):
        for f in files:
            fpath = os.path.join(root, f)
            if not os.path.exists(fpath):
                return True
    return False


class TestExternalToolchain(infra.basetest.BRTest):
    def common_check(self):
        # Check for broken symlinks
        for d in ["lib", "usr/lib"]:
            path = os.path.join(self.builddir, "staging", d)
            self.assertFalse(has_broken_links(path))
            path = os.path.join(self.builddir, "target", d)
            self.assertFalse(has_broken_links(path))

        with open(os.path.join(self.builddir, ".config"), 'r') as configf:
            configlines = [line.strip() for line in configf.readlines()]

        if "BR2_BINFMT_ELF=y" in configlines:
            interp = infra.get_elf_prog_interpreter(self.builddir,
                                                    self.toolchain_prefix,
                                                    "bin/busybox")
            interp_path = os.path.join(self.builddir, "target", interp[1:])
            self.assertTrue(os.path.exists(interp_path))


class TestExternalToolchainArmGnu(TestExternalToolchain):
    config = BASIC_CONFIG + \
        """
        BR2_arm=y
        BR2_cortex_a8=y
        BR2_TOOLCHAIN_EXTERNAL=y
        BR2_TOOLCHAIN_EXTERNAL_ARM_ARM=y
        """
    toolchain_prefix = "arm-none-linux-gnueabihf"

    def test_run(self):
        TestExternalToolchain.common_check(self)

        # Check the architecture variant
        arch = infra.get_file_arch(self.builddir,
                                   self.toolchain_prefix,
                                   "lib/libc.so.6")
        self.assertEqual(arch, "v7")
        isa = infra.get_elf_arch_tag(self.builddir,
                                     self.toolchain_prefix,
                                     "lib/libc.so.6",
                                     "Tag_THUMB_ISA_use")
        self.assertEqual(isa, "Thumb-2")

        # Boot the system
        img = os.path.join(self.builddir, "images", "rootfs.cpio")
        self.emulator.boot(arch="armv7",
                           kernel="builtin",
                           options=["-initrd", img])
        self.emulator.login()


class TestExternalToolchainBuildrootMusl(TestExternalToolchain):
    busybox_fragment = \
        infra.filepath("conf/busybox-for-old-toolchains.cfg")
    config = BASIC_CONFIG + \
        f"""
        BR2_arm=y
        BR2_cortex_a9=y
        BR2_ARM_ENABLE_VFP=y
        BR2_TOOLCHAIN_EXTERNAL=y
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y
        BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y
        BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/br-arm-cortex-a9-musl-2017.05-1078-g95b1dae.tar.bz2"
        BR2_TOOLCHAIN_EXTERNAL_GCC_6=y
        BR2_TOOLCHAIN_EXTERNAL_HEADERS_4_12=y
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM_MUSL=y
        BR2_TOOLCHAIN_EXTERNAL_CXX=y
        BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="{busybox_fragment}"
        """
    toolchain_prefix = "arm-linux"

    def test_run(self):
        TestExternalToolchain.common_check(self)
        img = os.path.join(self.builddir, "images", "rootfs.cpio")
        self.emulator.boot(arch="armv7",
                           kernel="builtin",
                           options=["-initrd", img])
        self.emulator.login()


class TestExternalToolchainCtngMusl(TestExternalToolchain):
    busybox_fragment = \
        infra.filepath("conf/busybox-for-old-toolchains.cfg")
    config = BASIC_CONFIG + \
        f"""
        BR2_arm=y
        BR2_cortex_a9=y
        BR2_ARM_ENABLE_VFP=y
        BR2_TOOLCHAIN_EXTERNAL=y
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y
        BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y
        BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.net/toolchains/tarballs/arm-ctng-linux-musleabihf.tar.xz"
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM_PREFIX="arm-ctng-linux-musleabihf"
        BR2_TOOLCHAIN_EXTERNAL_GCC_7=y
        BR2_TOOLCHAIN_EXTERNAL_HEADERS_3_10=y
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM_MUSL=y
        BR2_TOOLCHAIN_EXTERNAL_CXX=y
        BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="{busybox_fragment}"
        """
    toolchain_prefix = "arm-ctng-linux-musleabihf"

    def test_run(self):
        TestExternalToolchain.common_check(self)
        img = os.path.join(self.builddir, "images", "rootfs.cpio")
        self.emulator.boot(arch="armv7",
                           kernel="builtin",
                           options=["-initrd", img])
        self.emulator.login()


class TestExternalToolchainBuildrootuClibc(TestExternalToolchain):
    busybox_fragment = \
        infra.filepath("conf/busybox-for-old-toolchains.cfg")
    config = BASIC_CONFIG + \
        f"""
        BR2_arm=y
        BR2_TOOLCHAIN_EXTERNAL=y
        BR2_TOOLCHAIN_EXTERNAL_CUSTOM=y
        BR2_TOOLCHAIN_EXTERNAL_DOWNLOAD=y
        BR2_TOOLCHAIN_EXTERNAL_URL="http://autobuild.buildroot.org/toolchains/tarballs/br-arm-full-2017.05-1078-g95b1dae.tar.bz2"
        BR2_TOOLCHAIN_EXTERNAL_GCC_4_9=y
        BR2_TOOLCHAIN_EXTERNAL_HEADERS_3_10=y
        BR2_TOOLCHAIN_EXTERNAL_LOCALE=y
        # BR2_TOOLCHAIN_EXTERNAL_HAS_THREADS_DEBUG is not set
        BR2_TOOLCHAIN_EXTERNAL_CXX=y
        BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="{busybox_fragment}"
        """
    toolchain_prefix = "arm-linux"

    def test_run(self):
        TestExternalToolchain.common_check(self)
        img = os.path.join(self.builddir, "images", "rootfs.cpio")
        self.emulator.boot(arch="armv7",
                           kernel="builtin",
                           options=["-initrd", img])
        self.emulator.login()


class TestExternalToolchainCCache(TestExternalToolchainBuildrootuClibc):
    extraconfig = \
        """
        BR2_CCACHE=y
        BR2_CCACHE_DIR="{builddir}/ccache-dir"
        """

    def __init__(self, names):
        super(TestExternalToolchainBuildrootuClibc, self).__init__(names)
        self.config += self.extraconfig.format(builddir=self.builddir)
