# Copyright (c) 2008 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import utils

Import('env')

env = env.Clone()

# TODO: move all these builders out to site_scons or somesuch.

# TODO: all the other ports, including third-party libs
# - SAFARI, android, symbian
# - breakpad[_osx]
# - glint
# - growl
# - spidermonkey
# - libspeex
# - libtremor
# TODO: other targets
# - installer (safari, android, ...)

# Initialize our BROWSER_* input files.
env.InitBrowserInputs()


# Building .stab files, using M4FLAGS.

env.Replace(
    STAB = 'python $OPEN_DIR/tools/parse_stab.py',
    I18N_INPUTS_BASEDIR = '$OPEN_DIR/ui/generated',
    STABCOM = \
        '$STAB $M4FLAGS ${_M4INCFLAGS} $TARGET $SOURCE $I18N_INPUTS_BASEDIR',
)
stab_builder = Builder(action = '$STABCOM', src_suffix = '.stab')
env.Append(BUILDERS = {'Stab': stab_builder})


# Building .idl files.
# This is a total mess.  MIDL needs to be run from $OPEN_DIR because it's too
# stupid to apply its include paths to a relative path like "ui/ie/bla.idl"
# (it only looks in the current dir).  So we have to jump through hoops to fix
# up our relative include paths and output files.
env.Tool('midl')

if env['BROWSER'] == 'IE':
  env.Replace(
      SCONS_DIR = '..',  # the scons dir relative to OPEN_DIR
      IDLINCPREFIX = '/I$SCONS_DIR/',
      IDLINCSUFFIX = '',
      _IDLINCFLAGS = ('${_concat(IDLINCPREFIX, CPPPATH, IDLINCSUFFIX, '
                     '__env__, RDirs, TARGET, SOURCE)}'),
      IDLDEFPREFIX = '/D',
      IDLDEFSUFFIX = '',
      _IDLDEFFLAGS = ('${_defines(IDLDEFPREFIX, CPPDEFINES, '
                     'IDLDEFSUFFIX, __env__)}'),
      MIDLCOM = (
          'cd $OPEN_DIR && '
          '$MIDL ${_IDLDEFFLAGS} ${_IDLINCFLAGS} -env win32 -Oicf '
          '/tlb $SCONS_DIR/${TARGET.base}.tlb '
          '/h $SCONS_DIR/${TARGET.base}.h '
          '/iid $SCONS_DIR/${TARGET.base}_i.c '
          '/proxy $SCONS_DIR/${TARGET.base}_p.c '
          '/dlldata $SCONS_DIR/${TARGET.base}_data.c '
          '$SCONS_DIR/$SOURCE'
      ),
  )
elif env['BROWSER'] in ['FF2', 'FF3']:
  env.Replace(
      GECKO_SDK = '$GECKO_BASE/$OS',
      GECKO_BIN = '$GECKO_SDK/gecko_sdk/bin',
      GECKO_LIB = '$GECKO_SDK/gecko_sdk/lib',
      MIDLCOM = (
          'xpidl -I $GECKO_SDK/gecko_sdk/idl -I $GECKO_BASE '
          '-m header -o ${TARGET.base} $SOURCE && '
          'xpidl -I $GECKO_SDK/gecko_sdk/idl -I $GECKO_BASE '
          '-m typelib -o ${TARGET.base} $SOURCE'
      ),
  )
  if env['BROWSER'] == 'FF2':
    env['GECKO_BASE'] = '$THIRD_PARTY_DIR/gecko_1.8'
  else:
    env['GECKO_BASE'] = '$THIRD_PARTY_DIR/gecko_1.9'

  env.PrependENVPath('PATH', env.Dir('#/$GECKO_BIN').abspath)

def MyIdlEmitter(target, source, env):
  """Produce the list of outputs generated by our IDL compiler.  Outputs
  differ depending on whether we're using xpidl (Firefox) or midl (IE)."""
  base = os.path.splitext(str(target[0]))[0]
  if 'xpidl' in env['MIDLCOM']:
    # Firefox's IDL compiler only generates .h and .xpt files. 
    target = [base + '.h', base + '.xpt']
  else:
    # MIDL generates the following.  Depending on the IDL, a .tlb is
    # generated, but since we can't know, we'll just ignore that.
    target = [base + '.h', base + '_i.c', base + '_p.c', base + '_data.c']
  return (target, source)
# Copy the builder object so we don't overwrite everyone's
# emitter with ours.
import copy
env['BUILDERS']['TypeLibrary'] = copy.copy(env['BUILDERS']['TypeLibrary'])
env['BUILDERS']['TypeLibrary'].emitter = MyIdlEmitter


# Building .xpt files.

if env['BROWSER'] in ['FF2', 'FF3']:
  env.Replace(
    XPTLINK = 'xpt_link',
    XPTLINKCOM = '$XPTLINK $TARGET $SOURCES',
  )
  xpt_builder = Builder(action = '$XPTLINKCOM', src_suffix = '.xpt')
  env.Append(BUILDERS = {'XptLink': xpt_builder})


# C++ defines

env.Prepend(
    CPPDEFINES = [
        'BROWSER_${BROWSER}=1',
    ],
)

if env['BROWSER'] in ['FF2', 'FF3']:

  if env['OS'] == 'osx':
    env.Prepend(
        CPPDEFINES = [
            'LINUX'
        ]
    )

  env.Prepend(
      CPPDEFINES = [
# TODO(cprince): Update source files so we don't need this compatibility define?
        'BROWSER_FF=1',
        'MOZILLA_STRICT_API',
# SpiderMonkey (the Firefox JS engine)'s JS_GET_CLASS macro in jsapi.h needs
# this defined to work with the gecko SDK that we've built.
# The definition of JS_THREADSAFE must be kept in sync with MOZJS_CPPFLAGS.
        'JS_THREADSAFE',
      ],
      CPPPATH = [
        '$GECKO_BASE',
        '$GECKO_SDK',
        '$GECKO_SDK/gecko_sdk/include',
      ],
  )
elif env['BROWSER'] == 'NPAPI' and env['OS'] == 'win32':
  env.Prepend(
      CPPDEFINES = [
        'BROWSER_CHROME=1',
      ],
  )
elif env['BROWSER'] == 'SF':
  env.Replace(
# Enable breakpad for Safari on MacOSX.
      USING_BREAKPAD_OSX = '1')
      
  env.Append(
      CPPDEFINES = [
# Remove these - During development, it was convenient to have these defined in
# the Safari port.  Before release we want to clean this up, and replace these
# with a single BROWSER_SF symbol.
# We also want to consolidate the include paths, so we don't have to add these
# paths here.
          'BROWSER_NPAPI',
          'BROWSER_WEBKIT',
          'BROWSER_SAFARI'
      ],
      CCFLAGS = [
# These flags are needed so that instead of exporting all symbols defined in
# the code, we just export those specifically marked, this reduces the output
# size.
          '-fvisibility=hidden'
      ],
      CPPPATH = ['$THIRD_PARTY/spidermonkey/nspr/pr/include']
  )

env.Append(
    LIBS = [
        'googleurl-gears',
        'png-gears',
        'sqlite-gears',
        'zlib-gears',
    ],
)

if env['OS'] == 'osx':
  env.Append(
      LIBS = [
          'mozjs-gears',
          'curl',
          'crypto',
          'leopard_support',
          'breakpad_osx-gears'
      ],
      LIBPATH = [
          '$OPEN_DIR/tools/osx'
      ],
  )

# Add in libraries for in-development APIs for non-official builds.
if not env['OFFICIAL_BUILD']:
  env.Append(LIBS = [
      'jpeg-gears',
      'gd',
  ])

  # Except on these platforms.
  if env['OS'] == 'wince':
    env.FilterOut(LIBS = [
      'gd',
      'jpeg-gears',
    ])

if env['OS'] == 'android':
  env.Append(LIBS = [
    'mozjs-gears',
  ])
  env.FilterOut(LIBS = [
    'googleurl-gears',
    'sqlite-gears',
    'png-gears',
    'zlib-gears',
    'jpeg-gears',
    'gd',
  ])

if env['BROWSER'] == 'IE':
  if env['OS'] == 'win32':
    env.Append(
        LIBS = [
            'kernel32.lib',
            'user32.lib',
            'gdi32.lib',
            'uuid.lib',
            'sensapi.lib',
            'shlwapi.lib',
            'shell32.lib',
            'advapi32.lib',
            'wininet.lib',
            'comdlg32.lib',
            'user32.lib',
        ],
    )
  else:  # OS=wince
    env.Append(
        LIBS = [
            'wininet.lib',
            'ceshell.lib',
            'coredll.lib',
            'corelibc.lib',
            'ole32.lib',
            'oleaut32.lib',
            'uuid.lib',
            'commctrl.lib',
            'atlosapis.lib',
            'piedocvw.lib',
            'cellcore.lib',
            'htmlview.lib',
            'imaging.lib',
            'toolhelp.lib',
            'aygshell.lib',
            'iphlpapi.lib',
            'gpsapi.lib',
        ],
    )
elif env['BROWSER'] in ['FF2', 'FF3']:
  env.Append(LIBPATH = '$GECKO_LIB')

  if env['BROWSER'] == 'FF2':
    env.Append(LIBS = '$FF2_LIBS')
  else:
    env.Append(LIBS = '$FF3_LIBS')

  if env['OS'] == 'win32':
    env.Append(
        LIBS = [
            'xpcom',
            'xpcomglue_s',
            'nspr4.lib',
            'js3250.lib',
            'ole32.lib',
            'shell32.lib',
            'shlwapi.lib',
            'advapi32.lib',
            'wininet.lib',
            'comdlg32.lib',
            'user32.lib',
        ],
    )
  elif env['OS'] == 'linux':
    env.Append(
        LIBS = [
            'xpcom',
            'xpcomglue_s',
        ],
# Although the 1.9 SDK contains libnspr4, it is better to link against
# libxul, which in turn depends on libnspr4. In Ubuntu 8.04, libnspr4 was
# not listed in /usr/lib, only libxul was.
        FF2_LIBS = ['nspr4'],
        FF3_LIBS = ['xul'],
    )
  elif env['OS'] == 'osx':
    env.Append(
        LIBS = [
            'xpcom',
            'mozjs',
            'nspr4',
            'plds4',
            'plc4',
        ],
        FF2_LIBS = ['xpcom_core'],
# TODO(mpcomplete): the Makefiles specify many more libs, but we link fine with
# just this.  Why?
        FF3_LIBS = ['xpcomglue_s'],
    )
elif env['BROWSER'] == 'NPAPI':
  if env['OS'] == 'win32':
    env.Append(
        LIBS = [
            'delayimp.lib',
            'comdlg32.lib',
        ],
        SHLINKFLAGS = [
            '/DELAYLOAD:"comdlg32.dll"',
        ],
    )


# Building resources.
if env['OS'] in ['win32', 'wince']:
  env_res = env.Clone()
  env_res.Replace(
      CPPDEFINES = [
          '_UNICODE',
          'UNICODE',
          '$EXTRA_DEFINES',
      ],
      EXTRA_DEFINES = [
          'BROWSER_${BROWSER}=1',
      ],
      CPPPATH = [
          '$COMMON_GENFILES_DIR/..',
          '$GENFILES_DIR/..',
          '$OPEN_DIR',
          '$VC80_CPPPATH',
      ],
  )

  if env['MODE'] == 'dbg':
    env_res.Append(CPPDEFINES = 'DEBUG=1')
  else:
    env_res.Append(CPPDEFINES = 'NDEBUG=1')

  env_res.Append(RCFLAGS = [('/l', '0x409')])
  if env['OS'] == 'win32':
    env_res.Append(
        CPPPATH = [
            '$PRIVATE_THIRD_PARTY_DIR/atlmfc_vc80/files/include',
        ],
    )
  elif env['OS'] == 'wince':
    env_res.Append(
        CPPDEFINES = [
            'OS_WINCE',
            '_WIN32',
            '_WIN32_WCE',
            'UNDER_CE',
        ],
        CPPPATH = [
            '$OPEN_DIR/..',
            '$PRIVATE_THIRD_PARTY_DIR/atlmfc_vc80ce/files/include',
        ],
        RCFLAGS = [
            '-N',
        ]
    )
else:
  env_res = None

if env['OS'] == 'win32':
  # TODO: move to breakpad
  env.Append(CPPFLAGS = ['/wd4018'])

# TODO(playmobil): Create builder to generate required header files.
#  browser_specific_objects += \
#      env.SharedObject('$OPEN_DIR/base/safari/resource_archive.cc')

#-----------------------------------------------------------------------------
# Generate the dependency tree.

def PatternRule(t, s): return utils.PatternRule(t, s, env)
def GetInputs(var): return utils.GetInputs(var, env)

outputs = {}

# genfiles/%: %.m4
m4s = [env.M4(*PatternRule('$GENFILES_DIR/${SOURCE.filebase}', src))
       for src in GetInputs('$BROWSER_M4SRCS')]
outputs['M4S'] = m4s

# genfiles/%.html: %.html_m4
html_m4s = [env.M4(
                *PatternRule('$GENFILES_DIR/${SOURCE.filebase}.html', src))
            for src in GetInputs('$BROWSER_HTML_M4SRCS')]

# genfiles/i18n/%: %.m4
# This magic grabs the last *2* components from the path ("en-US/foo.m4")
def PathEnd(path, n):
  """Returns the last n components of the given path.
  Example: PathEnd('/foo/bar/baz', 2) == 'bar/baz'"""
  split_path = os.path.normpath(path).split(os.sep)
  return os.sep.join(split_path[-n:])
env['PathEnd'] = PathEnd
tmp_pattern = '$GENFILES_DIR/i18n/${PathEnd(str(SOURCE.base), 2)}'
i18n_m4s = [env.M4(*PatternRule(tmp_pattern, src))
            for src in GetInputs('$BROWSER_I18N_M4SRCS')]

# genfiles/%.js: %.js.stab
stabs = [env.Stab(
             *PatternRule('$GENFILES_DIR/${SOURCE.filebase}', src))
         for src in GetInputs('$BROWSER_STABSRCS')]

# genfiles/%.html: %.js
for stab in stabs:
  env.Depends(*PatternRule('${SOURCE.base}.html', stab))

# genfiles/%.tlb: %.idl
xptsrcs = []
for src in GetInputs('$BROWSER_IDLSRCS'):
  idl = env.TypeLibrary(
            *PatternRule('$GENFILES_DIR/${SOURCE.filebase}.tlb', src))
  env.Append(BROWSER_CPPSRCS = [x for x in idl if str(x).endswith('_i.c')])
  if env['BROWSER'] in ['FF2', 'FF3']:
    xptsrcs += [x for x in idl if str(x).endswith('.xpt')]

if env['BROWSER'] in ['FF2', 'FF3']:
  outputs['MODULE_TYPELIB'] = env.XptLink('gears.xpt', xptsrcs)

if env_res:
  # genfiles/%.res: %.rc
  env.Append(BROWSER_SHLINKSRCS = [
      [env_res.RES(
           *PatternRule('$GENFILES_DIR/${SOURCE.filebase}.res', src))
       for src in GetInputs('$BROWSER_RESSRCS')]
  ])

# TODO: figure out why the .rc scanner doesn't notice these dependencies.
if env['OS'] in ['win32', 'wince'] and env['BROWSER'] in ['NPAPI', 'IE']:
  env.Depends('$GENFILES_DIR/ui_resources.rc', html_m4s)
  env.Depends('$OPEN_DIR/base/npapi/module.rc', m4s)

# Safari resources
if env['BROWSER'] == 'SF':
  env.Replace(
      WEBARCHIVER = '$OPEN_DIR/tools/osx/webarchiver/webarchiver',
      GEN_RESOURCE_LIST = 'python $OPEN_DIR/tools/osx/gen_resource_list.py',
      GEN_RESOURCES = ['$GENFILES_DIR/resource_list.h'],
  )
  def WebArchiver(src):
    src_basename = os.path.basename(os.path.splitext(src)[0])
    return ('$WEBARCHIVER "$GENFILES_DIR/%s.webarchive" "%s" '
            ' $COMMON_RESOURCES' % (src_basename, src))
  def XXD(src):
    src_basename = os.path.basename(os.path.splitext(src)[0])
    env.Append(GEN_RESOURCES = ['$GENFILES_DIR/%s.h' % src_basename])
    return 'xxd -i "%s" > "$GENFILES_DIR/%s.h"' % (src, src_basename)
  resource_list_h = env.Command(
      '$GENFILES_DIR/resource_list.h',
      GetInputs('$COMMON_RESOURCES') + html_m4s,
      [WebArchiver('$GENFILES_DIR/permissions_dialog.html'),
       WebArchiver('$GENFILES_DIR/shortcuts_dialog.html'),
       WebArchiver('$GENFILES_DIR/settings_dialog.html'),
       XXD('$GENFILES_DIR/permissions_dialog.webarchive'),
       XXD('$GENFILES_DIR/shortcuts_dialog.webarchive'),
       XXD('$GENFILES_DIR/settings_dialog.webarchive'),
       XXD('$OPEN_DIR/ui/common/location_data.png'),
       XXD('$OPEN_DIR/ui/common/local_data.png'),
      '$GEN_RESOURCE_LIST $GEN_RESOURCES'],
  )
  resource_obj = env.SharedObject(
      '$GENFILES_DIR/resources',
      '$OPEN_DIR/base/safari/resource_archive.cc')
  env.Depends(resource_obj, resource_list_h)
  env.Append(BROWSER_LINKSRCS = resource_obj)


# HACK: gears, vista_broker, and wince_setup share some inputs, but the
# Chrome* helpers muck with the environment, so SCons complains if we try to
# compile those inputs differently.  So we try to precompile them before
# passing them to those methods first.  So far, only *_CPPSRCS inputs
# are shared.

module = env.ChromeSharedLibrary('gears-$OS-$ARCH-$MODE-${BROWSER}',
    env.SharedObject(GetInputs('$BROWSER_CPPSRCS')) +
    GetInputs('$BROWSER_LINKSRCS $BROWSER_SHLINKSRCS'))
outputs['MODULE'] = env.InstallAs('${SHLIBPREFIX}gears${SHLIBSUFFIX}', module)
if env['OS'] in ['win32', 'wince'] and env['MODE'] == 'dbg':
  outputs['MODULE_PDB'] = env.InstallAs('gears.pdb',
      'gears-$OS-$ARCH-$MODE-${BROWSER}.pdb')

if env['GEARS_STATIC_LIB']:
  if env['OS'] == 'win32' and env['BROWSER'] == 'NPAPI':
    lib = env.ChromeLibrary('gears-static',
        env.SharedObject(GetInputs('$BROWSER_CPPSRCS')) +
        GetInputs('$BROWSER_LINKSRCS'))
    env.Alias('gears', lib)
else:
  env.Alias('gears', outputs['MODULE'])
  if 'MODULE_PDB' in outputs:
    env.Alias('gears', outputs['MODULE_PDB'])

if env['OS'] == 'wince':
  env.Append(WINCE_SETUP_LINKSRCS = [
      [env_res.RES(*PatternRule('$GENFILES_DIR/${SOURCE.filebase}.res', src))
       for src in GetInputs('$WINCE_SETUP_RESSRCS')]
  ])
  outputs['WINCE_SETUP_DLL'] = env.ChromeSharedLibrary('setup',
      env.SharedObject(GetInputs('$WINCE_SETUP_CPPSRCS')) +
      GetInputs('$WINCE_SETUP_LINKSRCS'))

if env['OS'] == 'win32' and env['BROWSER'] == 'IE':
# Note: We use IE_OUTDIR so that relative path from gears.dll is same in
# development environment as deployment environment.
# Note: vista_broker.exe needs to stay in sync with name used in
# desktop_win32.cc.
# TODO(aa): This can move to common_outdir like crash_sender.exe
  env.Append(VISTA_BROKER_LINKSRCS = [
       [env_res.RES('$GENFILES_DIR/vista_broker_${SOURCE.filebase}.res',
           env_res.File(src), EXTRA_DEFINES = 'VISTA_BROKER=1')
       for src in GetInputs('$VISTA_BROKER_RESSRCS')]
  ])
  outputs['VISTA_BROKER_EXE'] = env.ChromeProgram('vista_broker',
      env.SharedObject(GetInputs('$VISTA_BROKER_CPPSRCS')) +
      GetInputs('$VISTA_BROKER_LINKSRCS'))
  env.Alias('gears', outputs['VISTA_BROKER_EXE'])

if env['BROWSER'] == 'SF':
  outputs['PROXY_DLL'] = env.ChromeSharedLibrary('gears_proxy',
      GetInputs('$SF_PROXY_DLL_CPPSRCS'))
  outputs['INPUTMANAGER_EXE'] = env.ChromeProgram('GearsEnabler',
      GetInputs('$SF_INPUTMANAGER_CPPSRCS'),
      FRAMEWORKS = env['FRAMEWORKS'] + Split('Foundation AppKit'))

# See main SConscript for how 'outputs' is used.
Return('outputs')