# Copyright 2012 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#     * Neither the name of Google Inc. nor the names of its
#       contributors may be used to endorse or promote products derived
#       from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# set up path for js2c import
import sys
from os.path import join, dirname, abspath
root_dir = dirname(File('SConscript').rfile().abspath)
sys.path.append(join(root_dir, 'tools'))
import js2c

Import("env windows linux darwin solaris freebsd debugBuild")

# pared-down copies of the equivalent structures in v8's SConstruct/SConscript:
LIBRARY_FLAGS = {
  'all': {
    'all': {
      'CPPDEFINES':   ['ENABLE_DEBUGGER_SUPPORT'],
    },
    'mode:debug': {
      'CPPDEFINES':   ['V8_ENABLE_CHECKS', 'OBJECT_PRINT']
    },
  },
  'gcc': {
    'all': {
      'CCFLAGS':      ['-Wno-unused-parameter',
                       '-Woverloaded-virtual',
                       '-Wnon-virtual-dtor']
    },
    'mode:debug': {
      'CPPDEFINES':   ['ENABLE_DISASSEMBLER', 'DEBUG'],
    },
    'os:linux': {
      'CCFLAGS':      ['-ansi', '-pedantic'],
    },
    'os:macos': {
      'CCFLAGS':      ['-ansi', '-pedantic'],
    },
    'os:freebsd': {
      'CCFLAGS':      ['-ansi'],
    },
    'os:solaris': {
      # On Solaris, to get isinf, INFINITY, fpclassify and other macros one
      # needs to define __C99FEATURES__.
      'CPPDEFINES':   ['__C99FEATURES__'],
      'CCFLAGS':      ['-ansi'],
    },
    'arch:ia32': {
      'CPPDEFINES':   ['V8_TARGET_ARCH_IA32'],
    },
    'arch:x64': {
      'CPPDEFINES':   ['V8_TARGET_ARCH_X64'],
    },
  },
  'msvc': {
    'all': {
      'CPPDEFINES':   ['WIN32'],
      'CCFLAGS':      ['/W3', '/WX', '/wd4351', '/wd4355', '/wd4800'],
    },
    'arch:ia32': {
      'CPPDEFINES':   ['V8_TARGET_ARCH_IA32', '_USE_32BIT_TIME_T', 'V8_NO_FAST_TLS'],
    },
    'arch:x64': {
      'CPPDEFINES':   ['V8_TARGET_ARCH_X64'],
    },
    'mode:debug': {
      'CPPDEFINES':   ['_DEBUG', 'ENABLE_DISASSEMBLER', 'DEBUG'],
    },
  }
}

SOURCES = {
  'all': Split("""
    accessors.cc
    allocation.cc
    api.cc
    assembler.cc
    ast.cc
    atomicops_internals_x86_gcc.cc
    bignum-dtoa.cc
    bignum.cc
    bootstrapper.cc
    builtins.cc
    cached-powers.cc
    checks.cc
    circular-queue.cc
    code-stubs.cc
    codegen.cc
    compilation-cache.cc
    compiler.cc
    contexts.cc
    conversions.cc
    counters.cc
    cpu-profiler.cc
    data-flow.cc
    date.cc
    dateparser.cc
    debug-agent.cc
    debug.cc
    deoptimizer.cc
    disassembler.cc
    diy-fp.cc
    dtoa.cc
    elements-kind.cc
    elements.cc
    execution.cc
    extensions/externalize-string-extension.cc
    extensions/gc-extension.cc
    extensions/statistics-extension.cc
    factory.cc
    fast-dtoa.cc
    fixed-dtoa.cc
    flags.cc
    frames.cc
    full-codegen.cc
    func-name-inferrer.cc
    gdb-jit.cc
    global-handles.cc
    handles.cc
    heap-profiler.cc
    heap.cc
    hydrogen-instructions.cc
    hydrogen.cc
    ic.cc
    incremental-marking.cc
    inspector.cc
    interface.cc
    interpreter-irregexp.cc
    isolate.cc
    jsregexp.cc
    lithium-allocator.cc
    lithium.cc
    liveedit.cc
    liveobjectlist.cc
    log-utils.cc
    log.cc
    mark-compact.cc
    messages.cc
    objects-printer.cc
    objects-visiting.cc
    objects.cc
    once.cc
    optimizing-compiler-thread.cc
    parser.cc
    preparse-data.cc
    preparser.cc
    profile-generator.cc
    property.cc
    regexp-macro-assembler-irregexp.cc
    regexp-macro-assembler.cc
    regexp-stack.cc
    rewriter.cc
    runtime-profiler.cc
    runtime.cc
    safepoint-table.cc
    scanner-character-streams.cc
    scanner.cc
    scopeinfo.cc
    scopes.cc
    serialize.cc
    snapshot-common.cc
    spaces.cc
    store-buffer.cc
    string-search.cc
    string-stream.cc
    strtod.cc
    stub-cache.cc
    token.cc
    transitions.cc
    type-info.cc
    unicode.cc
    utils.cc
    v8-counters.cc
    v8.cc
    v8conversions.cc
    v8threads.cc
    v8utils.cc
    variables.cc
    version.cc
    zone.cc
    """),
  'arch:ia32': Split("""
    ia32/assembler-ia32.cc
    ia32/builtins-ia32.cc
    ia32/code-stubs-ia32.cc
    ia32/codegen-ia32.cc
    ia32/cpu-ia32.cc
    ia32/debug-ia32.cc
    ia32/deoptimizer-ia32.cc
    ia32/disasm-ia32.cc
    ia32/frames-ia32.cc
    ia32/full-codegen-ia32.cc
    ia32/ic-ia32.cc
    ia32/lithium-codegen-ia32.cc
    ia32/lithium-gap-resolver-ia32.cc
    ia32/lithium-ia32.cc
    ia32/macro-assembler-ia32.cc
    ia32/regexp-macro-assembler-ia32.cc
    ia32/stub-cache-ia32.cc
    """),
  'arch:x64': Split("""
    x64/assembler-x64.cc
    x64/builtins-x64.cc
    x64/code-stubs-x64.cc
    x64/codegen-x64.cc
    x64/cpu-x64.cc
    x64/debug-x64.cc
    x64/deoptimizer-x64.cc
    x64/disasm-x64.cc
    x64/frames-x64.cc
    x64/full-codegen-x64.cc
    x64/ic-x64.cc
    x64/lithium-codegen-x64.cc
    x64/lithium-gap-resolver-x64.cc
    x64/lithium-x64.cc
    x64/macro-assembler-x64.cc
    x64/regexp-macro-assembler-x64.cc
    x64/stub-cache-x64.cc
    """),
  'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'],
  'os:linux':   ['platform-linux.cc', 'platform-posix.cc'],
  'os:macos':   ['platform-macos.cc', 'platform-posix.cc'],
  'os:solaris': ['platform-solaris.cc', 'platform-posix.cc'],
  'os:nullos':  ['platform-nullos.cc'],
  'os:win32':   ['platform-win32.cc', 'win32-math.cc'],
  'mode:release': [],
  'mode:debug': [
    'objects-debug.cc', 'prettyprinter.cc', 'regexp-macro-assembler-tracer.cc'
  ]
}

EXPERIMENTAL_LIBRARY_FILES = '''
proxy.js
collection.js
'''.split()

LIBRARY_FILES = '''
runtime.js
v8natives.js
array.js
string.js
uri.js
math.js
messages.js
apinatives.js
date.js
regexp.js
json.js
liveedit-debugger.js
mirror-debugger.js
debug-debugger.js
'''.split()

def get_flags(flag, toolchain, options):
    ret = []
    for t in (toolchain, 'all'):
        for o in (options.values() + ['all']):
            ret.extend(LIBRARY_FLAGS[t].get(o,{}).get(flag,[]))
    return ret

def get_options():
    processor = env['PROCESSOR_ARCHITECTURE']
    if processor == 'i386':
        arch_string = 'arch:ia32'
    elif processor == 'i686':
        arch_string = 'arch:ia32'
    elif processor == 'x86_64':
        arch_string = 'arch:x64'
    elif processor == 'amd64':
        arch_string = 'arch:x64'
    else:
        assert False, "Unsupported architecture: " + processor

    if linux:
        os_string = 'os:linux'
    elif darwin:
        os_string = 'os:macos'
    elif windows:
        os_string = 'os:win32'
    elif freebsd:
        os_string = 'os:freebsd'
    elif solaris:
        os_string = 'os:solaris'
    else:
        os_string = 'os:nullos'

    if debugBuild:
        mode_string = 'mode:debug'
    else:
        mode_string = 'mode:release'

    return {'mode': mode_string, 'os': os_string, 'arch': arch_string}

def get_sources(options):
    keys = options.values() + ['all']

    sources = []
    for i in keys:
        sources.extend(('src/'+s) for s in SOURCES[i])

    # sources generated from .js files:
    sources.append('src/libraries.cc')
    sources.append('src/experimental-libraries.cc')

    # we're building with v8 "snapshot=off", which requires this file:
    sources.append('src/snapshot-empty.cc')

    return sources

def get_toolchain():
    if windows:
        return 'msvc'
    else:
        return 'gcc'

# convert our SConstruct variables to their v8 equivalents:
toolchain = get_toolchain()
options = get_options()
sources = get_sources(options)

env = env.Clone()

# remove -Iinclude and prepend -Isrc, to resolve namespace conflicts:
#
# mongo source needs to compile with include/v8.h, but v8 source
# needs to compile with src/v8.h
#
# in addition, v8 source needs to compile with src/parser.h, which
# is being placed here earlier in the search path than windows sdk's
# Include/parser.h (v8 doesn't even use any of those header files)
env['CPPPATH'].remove('$BUILD_DIR/third_party/v8/include')
env.Prepend(CPPPATH='$BUILD_DIR/third_party/v8/src')

# add v8 ccflags and cppdefines to environment if they're not already
# present
ccflags = get_flags('CCFLAGS', toolchain, options)
ccflags = filter(lambda f :
                     f not in env['CCFLAGS'] + env['CXXFLAGS'] + env['CFLAGS'],
                 ccflags)
env.Append(CCFLAGS=ccflags)
cppdefines = get_flags('CPPDEFINES', toolchain, options)
cppdefines = filter(lambda f : f not in env['CPPDEFINES'], cppdefines)
env.Append(CPPDEFINES=cppdefines)

# NOTE: Suppress attempts to enable warnings in v8. Trying to individually suppress with -Wno-
# results in a game of whack-a-mole between various versions of clang and gcc as they add new
# warnings. We won't be changing the v8 sources, so the warnings aren't helpful.
def removeIfPresent(lst, item):
    try:
        lst.remove(item)
    except ValueError:
        pass

for to_remove in ['-Werror', '-Wall', '-W']:
    removeIfPresent(env['CCFLAGS'], to_remove)

# specify rules for building libraries.cc and experimental-libraries.cc
env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C)
experimental_library_files = [('src/'+s) for s in EXPERIMENTAL_LIBRARY_FILES]
experimental_library_files.append('src/macros.py')
env.JS2C(['src/experimental-libraries.cc'],
         experimental_library_files,
         TYPE='EXPERIMENTAL',
         COMPRESSION='off')
library_files = [('src/'+s) for s in LIBRARY_FILES]
library_files.append('src/macros.py')
env.JS2C(['src/libraries.cc'], library_files, TYPE='CORE', COMPRESSION='off')

env.Library("v8", sources)
