## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-

import os.path

from waflib import Options

def options(opt):
    opt.add_option('--force-planetlab',
                   help=('Forces compilation of PlanetLab even if not supported by the local system'),
                   dest='force_planetlab', default=False, action="store_true")

def configure(conf):
    conf.env['ENABLE_FDNETDEV'] = False

    if conf.env['ENABLE_THREADING']:
        # Check for system dependencies
        have_sysioctl = conf.check_nonfatal(header_name='sys/ioctl.h', 
            define_name = 'HAVE_SYS_IOCTL_H')
        have_netif =  conf.check_nonfatal(header_name='net/if.h',
            define_name = 'HAVE_IF_NETS_H')

        # Enable the FdNetDevice module. 
        # Besides threading support, we also require ethernet.h
        conf.env['ENABLE_FDNETDEV'] = conf.check_nonfatal(header_name='net/ethernet.h',
                                                          define_name='HAVE_NET_ETHERNET_H')
        if conf.env['ENABLE_FDNETDEV']:
            conf.report_optional_feature("FdNetDevice", 
                                         "File descriptor NetDevice",
                                         True,
                                         "FdNetDevice module enabled")

            # Check if dpdk environment variables are set. Also check if we have correct paths set.
            env_rte_sdk = os.environ.get('RTE_SDK', '')
            env_rte_target = os.environ.get('RTE_TARGET', '')
            have_dpdk_src = env_rte_sdk != '' and env_rte_target != '' and \
                            os.path.exists(os.path.join(env_rte_sdk, env_rte_target, 'include/rte_eal.h')) and \
                            os.path.exists(os.path.join(env_rte_sdk, env_rte_target, 'lib/librte_eal.so'))

            have_dpdk_pkg = False
            if not have_dpdk_src:
                # Check if libdpdk is installed as package
                have_dpdk_pkg = conf.check_cfg(package='libdpdk', uselib_store='DPDK',
                                  args=['--cflags', '--libs'], mandatory=False)

            conf.env['ENABLE_DPDKNETDEV'] = have_dpdk_pkg or have_dpdk_src

            if conf.env['ENABLE_DPDKNETDEV']:
                # Set DPDK Lib Mode. pkg if the package if installed or src if built from source.
                dpdk_lib_mode = 'pkg' if have_dpdk_pkg else 'src'
                conf.env.append_value('DPDK_LIB_MODE', [dpdk_lib_mode])
                conf.env.append_value('DEFINES', ['HAVE_DPDK_USER_H'])
                if have_dpdk_pkg:
                    conf.report_optional_feature("DpdkNetDevice",
                                                "DPDK NetDevice",
                                                True,
                                                "DPDKNetDevice module enabled (from libdpdk)")
                else:
                    conf.report_optional_feature("DpdkNetDevice",
                                                "DPDK NetDevice",
                                                True,
                                                "DPDKNetDevice module enabled (from source)")
            else:
                conf.report_optional_feature("DpdkNetDevice",
                                            "DPDK NetDevice",
                                            False,
                                            "libdpdk not found, $RTE_SDK and/or $RTE_TARGET environment variable not set or incorrect")
        else:
            conf.report_optional_feature("FdNetDevice", 
                                         "File descriptor NetDevice",
                                         False,
                                         "<net/ethernet.h> include not detected")

    else:
        conf.report_optional_feature("FdNetDevice", 
            "File descriptor NetDevice",
            False,
            "needs threading support which is not available")

    if conf.env['ENABLE_FDNETDEV']:
        blddir = os.path.abspath(os.path.join(conf.bldnode.abspath(), conf.variant))
        dir = os.path.abspath(os.path.join(blddir, "src/fd-net-device"))
        conf.env.append_value('NS3_EXECUTABLE_PATH', dir)

        if conf.env['ENABLE_DPDKNETDEV']:
            if 'src' in conf.env['DPDK_LIB_MODE']:
                dpdk_build = os.path.join(os.environ['RTE_SDK'], os.environ['RTE_TARGET'])
                conf.env.append_value('CXXFLAGS', ['-I' + os.path.join(dpdk_build, 'include'), '-mssse3'])
                conf.env.append_value('LINKFLAGS', ['-I' + os.path.join(dpdk_build, 'include')])
                conf.env.append_value('LINKFLAGS', ['-L' + os.path.join(dpdk_build, 'lib')])
                conf.env.SHLIB_MARKER += ',-lrte_eal,-lrte_ethdev,-lrte_pmd_virtio,-lrte_pmd_e1000,-lrte_pmd_ixgbe,-lrte_pmd_i40e,-lnuma,-ldl,-lrte_mempool,-lrte_mbuf,-lrte_ring,-lrte_kvargs,-lrte_net'
        else:
            # Add this module to the list of modules that won't be built
            # if they are enabled.
            conf.env['MODULES_NOT_BUILT'].append('dpdk-net-device')

    else:
        # Add this module to the list of modules that won't be built
        # if they are enabled.
        conf.env['MODULES_NOT_BUILT'].append('fd-net-device')

    # Next, check for whether specialized FdNetDevice features are enabled
    # such as tap device support, raw socket support, netmap support, and planetlab
    
    if conf.env['ENABLE_FDNETDEV']:
        conf.env['ENABLE_TAP'] = conf.check_nonfatal(
           header_name='linux/if_tun.h', 
           define_name='HAVE_IF_TUN_H') and have_sysioctl and have_netif

        if conf.env['ENABLE_TAP']:
            conf.report_optional_feature("TapFdNetDevice", 
                "Tap FdNetDevice", 
                True,
                "Tap support enabled")
        else:
            conf.report_optional_feature("TapFdNetDevice", 
                "Tap FdNetDevice", 
                False,
                "needs linux/if_tun.h")

        # Enable use of raw socket (EMU) helper.
        conf.env['ENABLE_EMU'] = conf.check_nonfatal(
           header_name='netpacket/packet.h',
           define_name='HAVE_PACKET_H') and have_sysioctl and have_netif

        if conf.env['ENABLE_EMU']:
            conf.report_optional_feature("EmuFdNetDevice", 
                "Emulation FdNetDevice", 
                True,
                "Emulation support enabled")
        else:
            conf.report_optional_feature("EmuFdNetDevice", 
                "Emulation FdNetDevice", 
                False,
                "needs netpacket/packet.h")

        # Enable use of netmap EMU support
        conf.env['ENABLE_NETMAP_EMU'] = conf.check_nonfatal(
           header_name='net/netmap_user.h',
           define_name='HAVE_NETMAP_USER_H') and have_sysioctl and have_netif

        if conf.env['ENABLE_NETMAP_EMU']:
            conf.report_optional_feature("NetmapFdNetDevice",
                "Netmap emulation FdNetDevice",
                True,
                "Netmap emulation support enabled")
        else:
            conf.report_optional_feature("NetmapFdNetDevice",
                "Netmap emulation FdNetDevice",
                False,
                "needs net/netmap_user.h")

        # Enable use of PlanetLab TAP helper
        # TODO: How to validate 
        (sysname, nodename, release, version, machine) = os.uname()
        if release.find('onelab') != -1 or Options.options.force_planetlab:
            conf.env['ENABLE_PLANETLAB'] = True

        if conf.env['ENABLE_PLANETLAB']:
            conf.report_optional_feature("PlanetLabFdNetDevice", 
                "PlanetLab FdNetDevice", 
                True,
                "Planetlab support enabled")
        else:
            conf.report_optional_feature("PlanetLabFdNetDevice", 
                "PlanetLab FdNetDevice", 
                False,
                "PlanetLab operating system not detected (see option --force-planetlab)")

def build(bld):
    # Don't do anything for this module if emu's not enabled.
    if not bld.env['ENABLE_FDNETDEV']:
        return

    module = bld.create_ns3_module('fd-net-device', ['network'])
    module.source = [
        'model/fd-net-device.cc',
        'helper/fd-net-device-helper.cc',
        'helper/encode-decode.cc',
        'helper/creator-utils.cc',
        ]

    headers = bld(features='ns3header')
    headers.module = 'fd-net-device'
    headers.source = [
        'model/fd-net-device.h',
        'helper/fd-net-device-helper.h',
        ]

    if bld.env['ENABLE_TAP']:
        if not bld.env['PLATFORM'].startswith('freebsd'):
            module.source.extend([
                'helper/tap-fd-net-device-helper.cc',
            ])

            headers.source.extend([
                'helper/tap-fd-net-device-helper.h',
            ])

            creator = bld.create_suid_program('tap-device-creator')
            creator.source = [
                'helper/tap-device-creator.cc',
                'helper/encode-decode.cc',
                'helper/creator-utils.cc',
            ]

        module.env.append_value("DEFINES", 
           "TAP_DEV_CREATOR=\"%s\"" % (creator.target,))

    if bld.env['ENABLE_DPDKNETDEV']:
        module.use.append('DPDK')
        module.source.extend([
            'helper/dpdk-net-device-helper.cc',
            'model/dpdk-net-device.cc'
        ])
        headers.source.extend([
            'helper/dpdk-net-device-helper.h',
            'model/dpdk-net-device.h'
        ])
        if 'src' in bld.env['DPDK_LIB_MODE']:
            # Add DPDK libraries to library path
            dpdk_build = os.path.join(os.environ['RTE_SDK'], os.environ['RTE_TARGET'])
            if os.environ.get('LD_LIBRARY_PATH', '') == '':
                os.environ['LD_LIBRARY_PATH'] = os.path.join(dpdk_build, 'lib')
            else:
                os.environ['LD_LIBRARY_PATH'] += ':' + os.path.join(dpdk_build, 'lib')
            # Add $RTE_SDK/usertools to PATH env
            if os.environ.get('PATH', '') == '':
                os.environ['PATH'] = os.path.join(os.environ['RTE_SDK'], 'usertools')
            else:
                os.environ['PATH'] += ':' + os.path.join(os.environ['RTE_SDK'], 'usertools')

    if bld.env['ENABLE_EMU']:
        module.source.extend([
            'helper/emu-fd-net-device-helper.cc',
        ])

        headers.source.extend([
            'helper/emu-fd-net-device-helper.h',
        ])

        creator = bld.create_suid_program('raw-sock-creator')
        creator.source = [
           'helper/raw-sock-creator.cc',
           'helper/encode-decode.cc',
           'helper/creator-utils.cc',
        ]

        module.env.append_value("DEFINES", 
           "RAW_SOCK_CREATOR=\"%s\"" % (creator.target,))

    if bld.env['ENABLE_NETMAP_EMU']:
        module.source.extend([
            'model/netmap-net-device.cc',
            'helper/netmap-net-device-helper.cc',
        ])

        headers.source.extend([
            'model/netmap-net-device.h',
            'helper/netmap-net-device-helper.h',
        ])

        creator = bld.create_suid_program('netmap-device-creator')
        creator.source = [
           'helper/netmap-device-creator.cc',
           'helper/encode-decode.cc',
           'helper/creator-utils.cc',
        ]

        module.env.append_value("DEFINES",
           "NETMAP_DEV_CREATOR=\"%s\"" % (creator.target,))

    if bld.env['ENABLE_PLANETLAB']:
        module.source.extend([
            'helper/planetlab-fd-net-device-helper.cc',
        ])

        headers.source.extend([
           'helper/planetlab-fd-net-device-helper.h',
        ])

        creator = bld.create_suid_program('planetlab-tap-creator')
        creator.source = [
           'helper/planetlab-tap-creator.cc',
           'helper/encode-decode.cc',
           'helper/creator-utils.cc',
        ]

        module.env.append_value("DEFINES", 
           "PLANETLAB_TAP_CREATOR=\"%s\"" % (creator.target,))

    if bld.env['ENABLE_EXAMPLES']:
        bld.recurse('examples')

    bld.ns3_python_bindings()

