from waflib.ConfigSet import ConfigSet
from waflib import Context, Errors, Task, TaskGen

class js_tooling(Task.Task):
    run_str = '${CC} ${CFLAGS} ${DEFINES_ST:DEFINES} ${CPPPATH_ST:INCPATHS} ${SRC} -o ${TGT[0].abspath()}'


@TaskGen.feature('js_tooling')
@TaskGen.before_method('process_source', 'process_rule')
def process_js_tooling(self):
    task = self.create_task('js_tooling',
                            self.jerry_sources,
                            self.js_tooling_target)
    task.dep_nodes = self.js_deps


JERRY_RUNTIME_HEAP_SIZE_KB = 32  # KB
JERRY_COMPILER_HEAP_SIZE_KB = 512  # KB
JERRY_CORE_DIRS = ['jerry-core',
                   'jerry-core/jcontext',
                   'jerry-core/jmem',
                   'jerry-core/jrt',
                   'jerry-core/lit',
                   'jerry-core/vm',
                   'jerry-core/ecma/builtin-objects',
                   'jerry-core/ecma/base',
                   'jerry-core/ecma/operations',
                   'jerry-core/parser/js',
                   'jerry-core/parser/regexp',
                   ]

def _get_jerry_include_dirs(bld):
    include_dirs = JERRY_CORE_DIRS
    if bld.variant == 'test':
        include_dirs.append('third-party/valgrind')
    return include_dirs

def jerry_build_js_compiler(bld):
    # Set the output node for JS tooling in the primary build ConfigSet
    bld.env.JS_TOOLING_SCRIPT = bld.path.get_bld().make_node('js_tooling/generate_snapshot.js')

    env = bld.all_envs['emscripten']

    # A few values to experiment with, while 'speed' is nice for experimentation,
    # and None seems to be ok, we should probably stick to 'size' at the cost of
    # a longer build time once this whole task got cleaned up
    optimize = 'size'
    # optimize = 'size'
    # optimize = 'speed'
    if optimize == 'size':
        env.append_value('CFLAGS', ['-Oz'])
        env.append_value('CFLAGS', ['--llvm-lto', '3'])
        env.append_value('CFLAGS', ['--closure', '1'])
    elif optimize == 'speed':
        env.append_value('CFLAGS', ['-O3'])
    else:
        env.append_value('CFLAGS', ['-O0'])
        env.append_value('CFLAGS', ['-g3'])

    fw_path = bld.path.parent.parent


    env['CPPPATH_ST'] = '-I%s'

    include_dirs = _get_jerry_include_dirs(bld)
    env.append_value('INCPATHS', [bld.path.find_node(d).abspath() for d in include_dirs])
    env.append_value('INCPATHS', fw_path.abspath())

    env['DEFINES_ST'] = '-D%s'

    env.append_value('DEFINES', [
        'JERRY_ENABLE_LOG',
        'CONFIG_MEM_HEAP_AREA_SIZE={}'.format(JERRY_COMPILER_HEAP_SIZE_KB * 1024),
    ])

    bytecode_version = bld.capability('JAVASCRIPT_BYTECODE_VERSION')
    env.append_value('DEFINES',
                     'CAPABILITY_JAVASCRIPT_BYTECODE_VERSION={}'.format(bytecode_version))
    env.append_value('DEFINES', 'JERRY_RETURN_ADDRESS=0') # provide proper error handling w fake

    # Emscripten specific arguments
    post_file = bld.path.make_node('js_tooling/_js_tooling.js').abspath()
    env.append_value('CFLAGS', ['--post-js', post_file])

    transform_js_file = bld.path.make_node('js_tooling/transform_js.py').abspath()
    env.append_value('CFLAGS', ['--js-transform', transform_js_file])

    # tell Emscripten to create a single file
    env.append_value('CFLAGS', ['--memory-init-file', '0'])

    # Uncomment this line to get verbose output for emscripten
    # env.append_value('CFLAGS', ['-v'])

    # keep these functions so that they are not optimized out
    exported_functions = ("_jerry_init",
                          "_jerry_cleanup",
                          "_jerry_parse_and_save_snapshot_from_zt_utf8_string",
                          "_legacy_defective_checksum_memory",
                          "_rocky_fill_header",
                          "_jerry_port_set_errormsg_handler")
    env.append_value('CFLAGS', ['-s', 'EXPORTED_FUNCTIONS=[{}]'.format(
        ', '.join(('"' + f + '"' for f in exported_functions)))])

    # so we can call jerry_port_set_errormsg_handler() with a JS function pointer
    env.append_value('CFLAGS', ['-s', 'RESERVED_FUNCTION_POINTERS=1'])

    env.append_value('CFLAGS', ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0'])

    sources = bld.path.ant_glob('jerry-core/**/*.c')
    sources += bld.path.ant_glob('js_tooling/*.c')
    sources += fw_path.ant_glob('util/legacy_checksum.c')

    js_deps = [bld.path.find_node(x) for x in ('js_tooling/_js_tooling.js', 'js_tooling/transform_js.py')]

    output_node = bld.path.get_bld().make_node('js_tooling/js_tooling.js')
    output_node.parent.mkdir()
    bld(features=['js_tooling', 'use'],
        jerry_sources=sources,
        js_tooling_target=output_node,
        js_deps=js_deps,
        use=['jerry_common_config'],
        env=env)

    bld.path.find_node('js_tooling').get_bld().mkdir()
    for resource in ('index.js', 'generate_snapshot.js', 'package.json', 'test.html'):
        src_node = bld.path.find_node('js_tooling/{}'.format(resource))
        bld(rule='cp ${SRC} ${TGT}', source=src_node, target=src_node.get_bld(), env=env)

    bld(rule='${NPM} install .',
        source=[output_node, bld.path.get_bld().make_node('js_tooling/package.json')],
        target=bld.path.get_bld().make_node('js_tooling/node_modules/'),
        cwd=bld.path.get_bld().find_node('js_tooling/').abspath(),
        env=env)


def configure(conf):
    conf.load('c_inject_include_files')

    # Create a new environment for emscipten and configure it with the location of our emcc binary
    prev_env = conf.variant
    conf.setenv('emscripten')

    missing_emcscripten = """
    'emcc' cannot be found on the system.

    Please install Emscripten using the instructions on the wiki: https://pebbletechnology.atlassian.net/wiki/display/DEV/Emscripten
    """

    conf.find_program('emcc', errmsg=missing_emcscripten, var='CC')
    conf.find_program('em-config')
    conf.find_program('npm', var='NPM')

    conf.setenv(prev_env)


def build(bld):
    def run_cmd(cmd):
        try:
            return bld.cmd_and_log(cmd, quiet=Context.BOTH).strip()
        except Errors.WafError as e:
            print e
            return "unknown"

    # Build jerry_common_config.
    # These are shared defines for jerry-core, jerry-libm, js_tooling and
    # jerry-port (part of applib).
    #############################################
    wrap_quotes = lambda s: '"{}"'.format(s)
    jerry_common_defines_dict = {
      'JERRY_BRANCH_NAME': wrap_quotes(run_cmd('git symbolic-ref -q HEAD')),
      'JERRY_COMMIT_HASH': wrap_quotes(run_cmd('git rev-parse HEAD')),
      'JERRY_BUILD_DATE': wrap_quotes(run_cmd('date +%d/%m/%Y')),
      'CONFIG_ECMA_NUMBER_TYPE': 'CONFIG_ECMA_NUMBER_FLOAT64',
      'CONFIG_DISABLE_PRINT_BUILTIN': '',
      'JERRY_DISABLE_HEAVY_DEBUG': '',
      'JERRY_ENABLE_SNAPSHOT_SAVE': '',
      'JERRY_ENABLE_ERROR_MESSAGES': '1',
    }
    jerry_common_defines = [
      '{}={}'.format(k, v) for k, v in jerry_common_defines_dict.iteritems()]
    bld(export_defines=jerry_common_defines, name='jerry_common_config')

    # Build jerry_runtime_config.
    # These are not used by js_tooling
    #############################################
    jerry_common_runtime_defines_dict = {
        'CONFIG_MEM_HEAP_AREA_SIZE': JERRY_RUNTIME_HEAP_SIZE_KB * 1024,
        'JMEM_STATS': '1',
    }
    jerry_common_runtime_defines = [
        '{}={}'.format(k, v) for k, v in jerry_common_runtime_defines_dict.iteritems()]
    bld(export_defines=jerry_common_runtime_defines, name='jerry_runtime_config')


    # Build jerry_port_includes
    #############################################
    bld(export_includes=_get_jerry_include_dirs(bld), name='jerry_port_includes')

    # If the current build doesn't want javascript, just build a header-only library so the
    # headers will continue to be available
    if not bld.capability('HAS_JAVASCRIPT') or bld.variant in ('prf', 'applib', 'test_rocky_emx'):
        bld(export_includes=['jerry-core'], name='jerry_core')
        return


    # Define flags common to both jerry-core and jerry-libm
    #############################################

    # cflags from Makefile.pebble, used in both jerry-libm and jerry-core
    pebble_cflags = ['-Wno-error=format',
                     '-Wno-error=unused-parameter',
                     '-Wno-error=unused-variable',
                     '-Wno-error=unused-function',
                     '-Wno-pedantic']

    if bld.variant == 'test':
        pebble_cflags += ['-Wno-conversion',
                          '-Wno-unknown-warning-option']

    # cflags from COMPILE_FLAGS_JERRY CMakeLists.txt, passed to both compile and link stages
    # in the original build script for the jerry-core sources
    compile_flags_jerry = ['-fno-builtin',
                           '-fno-stack-protector',
                           '-g',
                           '-gdwarf-4',
                           '-Wall',
                           '-Werror=all',
                           '-Wextra',
                           '-Werror=extra',
                           '-Wformat-nonliteral',
                           '-Werror=format-nonliteral',
                           '-Winit-self',
                           '-Werror=init-self',
                           '-Wconversion',
                           '-Werror=conversion',
                           '-Wsign-conversion',
                           '-Werror=sign-conversion',
                           '-Wformat-security',
                           '-Werror=format-security',
                           '-Wmissing-declarations',
                           '-Werror=missing-declarations',
                           '-Wno-stack-protector',
                           '-Wno-attributes',
                           '-Wlogical-op',
                           '-Werror=logical-op',
                           '-Wno-strict-aliasing'
                           ]

    # cflags from C_FLAGS_JERRY CMakeLists.txt, passed to only the compile stage
    # in the original build script to both jerry-core and fblibm
    c_flags_jerry = ['-std=c99']


    # Build the jerry-core static library
    #############################################
    jerry_core_env = bld.env.derive()

    all_jerry_flags = compile_flags_jerry + c_flags_jerry + pebble_cflags
    if bld.variant == 'test':
       all_jerry_flags.append('-DJERRY_VALGRIND')
    jerry_core_env.append_value('CFLAGS', all_jerry_flags)

    jerry_core_env.append_value('DEFINES', [
        'CONFIG_ECMA_LCACHE_DISABLE',  # PBL-40394: Hash table didn't fit.
        'JERRY_ENABLE_SNAPSHOT_EXEC',
        'JERRY_NDEBUG'
    ])

    jerry_core_include_dirs = _get_jerry_include_dirs(bld)

    jerry_core_env.append_value('INCLUDES', [bld.path.find_node(d).abspath()
                                             for d in jerry_core_include_dirs])
    jerry_core_source_excl = [
      'jcontext/jcontext.c',  # implementation provided by our own jerry_port.c
    ]
    jerry_core_sources = sum(
      [bld.path.ant_glob(d + '/*.c', excl=' '.join(jerry_core_source_excl))
       for d in JERRY_CORE_DIRS], [])

    pbl_jcontext_inc_h_node = bld.srcnode.find_node(
      'src/fw/applib/rockyjs/pbl_jcontext.inc.h')
    pbl_jcontext_inc_h_rel_path = pbl_jcontext_inc_h_node.path_from(bld.path)

    bld.stlib(source=jerry_core_sources,
              target='jerry_core',
              export_includes=['jerry-core'],
              use=['jerry_common_config', 'jerry_runtime_config'],
              inject_include_files=[pbl_jcontext_inc_h_rel_path],
              env=jerry_core_env)


    # Build the jerry-libm static library
    #############################################
    jerry_libm_env = bld.env.derive()

    compile_flags_libm = ['-Wno-error=parentheses',
                          '-Wno-error=sign-compare',
                          '-Wno-error=sign-conversion',
                          '-Wno-error=strict-aliasing',
                          '-Wno-error=unknown-pragmas',
                          '-Wno-error=missing-declarations',
                          '-Wno-error=maybe-uninitialized',
                          '-Wno-error=unused-but-set-variable',
                          '-Wno-error=unused-variable',
                          '-Wno-error=conversion',
                          '-Wno-sign-conversion',
                          '-Wno-sign-compare',
                          '-Wno-parentheses',
                          '-Wno-maybe-uninitialized',
                          '-Wno-unknown-pragmas',
                          '-Wno-unused-but-set-variable',
                          '-Wno-unused-variable',
                          '-fno-lto']

    all_libm_flags = compile_flags_libm + c_flags_jerry + pebble_cflags
    jerry_libm_env.append_value('CFLAGS', all_libm_flags)

    jerry_libm_dir = 'jerry-libm'

    jerry_libm_env.append_value('INCLUDES', [jerry_libm_dir + '/include'])

    jerry_libm_sources = bld.path.ant_glob(jerry_libm_dir + '/*.c')

    bld.stlib(source=jerry_libm_sources,
              target='libm',
              name='jerry_libm',
              use=['jerry_common_config', 'jerry_runtime_config'],
              env=jerry_libm_env)

    if bld.variant == 'test':
        return

    jerry_build_js_compiler(bld)

# vim:filetype=python