#!/usr/bin/env python
# Copyright (c) 2012 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 argparse
import StringIO
import sys
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(os.path.dirname(SCRIPT_DIR), 'tools'))

import getos

valid_tools = ['newlib', 'glibc', getos.GetPlatform()]


def Error(msg):
  print(msg)
  sys.exit(1)


PREAMBLE = """\
{
  'includes': ['%s/build_tools/nacl.gypi'],
"""

NEXE_TARGET = """\
    {
      'target_name': '%(NAME)s_x86_32%(EXT)s',
      'product_name': '%(NAME)s_x86_32%(EXT)s',
      'type': '%(GYP_TYPE)s',
      'sources': %(SOURCES)s,
      'libraries': %(LIBS)s,
      'include_dirs': %(INCLUDES)s,
      'cflags': ['-m32', '-pedantic'] + %(CFLAGS)s,
      'make_valid_configurations': ['newlib-debug', 'newlib-release',
                                    'glibc-debug', 'glibc-release'],
      'ldflags': ['-m32', '-L../../lib/x86_32/<(CONFIGURATION_NAME)'],
      'toolset': 'target',
      %(CONFIGS)s
    },
    {
      'target_name': '%(NAME)s_x86_64%(EXT)s',
      'product_name': '%(NAME)s_x86_64%(EXT)s',
      'type': '%(GYP_TYPE)s',
      'sources': %(SOURCES)s,
      'libraries': %(LIBS)s,
      'include_dirs': %(INCLUDES)s,
      'make_valid_configurations': ['newlib-debug', 'newlib-release',
                                    'glibc-debug', 'glibc-release'],
      'cflags': ['-m64', '-pedantic'] + %(CFLAGS)s,
      'ldflags': ['-m64', '-L../../lib/x86_64/<(CONFIGURATION_NAME)'],
      'toolset': 'target',
      %(CONFIGS)s
    },
"""

NLIB_TARGET = """\
    {
      'target_name': '%(NAME)s_x86_32%(EXT)s',
      'product_name': 'lib%(NAME)s%(EXT)s',
      'product_dir': '../../lib/x86_32/<(CONFIGURATION_NAME)',
      'type': '%(GYP_TYPE)s',
      'sources': %(SOURCES)s,
      'libraries': %(LIBS)s,
      'include_dirs': %(INCLUDES)s,
      'cflags': ['-m32', '-pedantic'] + %(CFLAGS)s,
      'make_valid_configurations': ['newlib-debug', 'newlib-release',
                                    'glibc-debug', 'glibc-release'],
      'ldflags': ['-m32'],
      'toolset': 'target',
      %(CONFIGS)s
    },
    {
      'target_name': '%(NAME)s_x86_64%(EXT)s',
      'product_name': 'lib%(NAME)s%(EXT)s',
      'product_dir': '../../lib/x86_64/<(CONFIGURATION_NAME)',
      'type': '%(GYP_TYPE)s',
      'sources': %(SOURCES)s,
      'libraries': %(LIBS)s,
      'include_dirs': %(INCLUDES)s,
      'make_valid_configurations': ['newlib-debug', 'newlib-release',
                                    'glibc-debug', 'glibc-release'],
      'cflags': ['-m64', '-pedantic'] + %(CFLAGS)s,
      'ldflags': ['-m64'],
      'toolset': 'target',
      %(CONFIGS)s
    },
"""

HOST_LIB_TARGET = """\
    {
      'target_name': '%(NAME)s%(EXT)s',
      'type': '%(GYP_TYPE)s',
      'toolset': 'host',
      'sources': %(SOURCES)s,
      'cflags': %(CFLAGS)s,
      'cflags_c': ['-std=gnu99'],
      'include_dirs': %(INCLUDES)s,
      'make_valid_configurations': ['host-debug', 'host-release'],
      'product_dir': '../../lib/%(ARCH)s/<(CONFIGURATION_NAME)',
      'product_name': '%(NAME)s%(EXT)s',
      %(CONFIGS)s
    },
"""

HOST_EXE_TARGET = """\
    {
      'target_name': '%(NAME)s%(EXT)s',
      'type': '%(GYP_TYPE)s',
      'toolset': 'host',
      'sources': %(SOURCES)s,
      'cflags': %(CFLAGS)s,
      'cflags_c': ['-std=gnu99'],
      'ldflags': ['-L../../lib/%(ARCH)s/<(CONFIGURATION_NAME)'],
      'libraries': %(LIBS)s,
      'include_dirs': %(INCLUDES)s,
      'make_valid_configurations': ['host-debug', 'host-release'],
      'msvs_settings': {
        'VCLinkerTool': {
          'AdditionalLibraryDirectories':
            ['../../lib/%(ARCH)s/<(CONFIGURATION_NAME)'],
         }
       },
       %(CONFIGS)s
    },
"""

NMF_TARGET = """\
    {
      'target_name': '%(NAME)s_%(TOOLCHAIN)s.nmf',
      'product_name': '%(NAME)s.nmf',
      'product_dir': '<(PRODUCT_DIR)/%(TOOLCHAIN)s',
      'type': 'none',
      'make_valid_configurations': ['%(TOOLCHAIN)s-debug', '%(TOOLCHAIN)s-release'],
      'actions': [
        {
          'action_name': 'nmf',
          'inputs': ['<(PRODUCT_DIR)/%(NAME)s_x86_32.nexe',
                     '<(PRODUCT_DIR)/%(NAME)s_x86_64.nexe'] + %(SODEPS)s,
          'outputs': ['<(PRODUCT_DIR)/%(NAME)s.nmf'],
          'action': ['../../tools/create_nmf.py', '-t', '%(TOOLCHAIN)s', '-s',
                     '<(PRODUCT_DIR)'] + %(NMFACTION)s,
        },
      ]
    },
"""

TOOLCHAIN_CONFIG = """\
        '%(toolchain)s-release' : {
          'cflags' : ['-O2'],
        },
        '%(toolchain)s-debug' : {
          'cflags' : ['-g', '-O0'],
        },
"""

NEXE_CONFIG = """\
          '%(toolchain)s-release' : {
            'cflags' : ['--%(toolchain)s', '-O2',
                        '-idirafter', '../../include'],
            'ldflags' : ['--%(toolchain)s'],
            'arflags' : ['--%(toolchain)s'],
          },
          '%(toolchain)s-debug' : {
            'cflags' : ['--%(toolchain)s', '-g', '-O0',
                        '-idirafter', '../../include'],
            'ldflags' : ['--%(toolchain)s'],
            'arflags' : ['--%(toolchain)s'],
          },
"""

WIN32_CONFIGS = """\
  'target_defaults': {
    'default_configuration': 'Debug_PPAPI',
    'configurations': {
      'Debug_PPAPI': {
        'msvs_configuration_platform': 'PPAPI',
        'msbuild_configuration_attributes': {
          'ConfigurationType': 'DynamicLibrary'
        },
        'include_dirs': ['../../include/win'],
        'defines': ['_WINDOWS', '_DEBUG', 'WIN32'],
      },
      'Release_PPAPI': {
        'msvs_configuration_platform': 'PPAPI',
        'msbuild_configuration_attributes': {
          'ConfigurationType': 'DynamicLibrary'
        },
        'include_dirs': ['../../include/win'],
        'defines': ['_WINDOWS', 'NDEBUG', 'WIN32'],
      },
      'Debug_NaCl': {
        'msvs_configuration_platform': 'NaCl',
        'msbuild_configuration_attributes': {
          'ConfigurationType': 'Application'
        },
      },
      'Release_NaCl': {
        'msvs_configuration_platform': 'NaCl',
        'msbuild_configuration_attributes': {
          'ConfigurationType': 'Application'
        },
      },
    },
  },
"""


def WriteNaClTargets(output, target, tools):
  configs = "'configurations' : {\n"
  for tc in tools:
    if tc not in valid_tools:
      continue
    if tc in ['newlib', 'glibc']:
      configs += NEXE_CONFIG % {'toolchain': tc}
  configs += "      }"
  target['CONFIGS'] = configs
  if target['TYPE'] == 'lib':
    output.write(NLIB_TARGET % target)
  else:
    output.write(NEXE_TARGET % target)


def ConfigName(toolchain):
  if toolchain == getos.GetPlatform():
    return 'host'
  else:
    return toolchain


def ProcessDSC(filename, outfile=None):
  if not os.path.exists(filename):
    Error("file not found: %s" % filename)

  desc = open(filename).read()
  desc = eval(desc, {}, {})
  if not desc.get('TARGETS'):
    Error("no TARGETS found in dsc")

  if not outfile:
    outfile = desc['NAME'] + '.gyp'
    outfile = os.path.join(os.path.dirname(filename), outfile)

  output = StringIO.StringIO()

  srcdir = os.path.dirname(SCRIPT_DIR)
  output.write(PREAMBLE % srcdir.replace("\\", '/'))

  win32 = sys.platform in ('win32', 'cygwin')
  if win32:
    output.write(WIN32_CONFIGS)
  else:
    for tc in desc['TOOLS']:
      if tc in valid_tools:
        default = '%s-debug' % ConfigName(tc)
        break

    output.write("""\
  'target_defaults': {
    'default_configuration': '%s',
    'configurations' : {\n""" % default)

    for tc in desc['TOOLS']:
      if tc not in valid_tools:
        continue
      output.write(TOOLCHAIN_CONFIG % {'toolchain': ConfigName(tc)})

    output.write("    }\n  },\n")

  output.write("\n  'targets': [\n")

  # make a list of all the so target names so that the nmf rules
  # can depend on them all
  sofiles = []
  soremap = []
  for target in desc['TARGETS']:
    if target['TYPE'] == 'so':
      name = target['NAME']
      sofiles.append('<(PRODUCT_DIR)/%s_x86_64.so' % name)
      sofiles.append('<(PRODUCT_DIR)/%s_x86_32.so' % name)
      soremap += ['-n', '%s_x86_64.so,%s.so' % (name, name)]
      soremap += ['-n', '%s_x86_32.so,%s.so' % (name, name)]


  # iterate through dsc targets generating gyp targets
  for target in desc['TARGETS']:
    target.setdefault('INCLUDES', [])
    target['INCLUDES'] = [x.replace("$(NACL_SDK_ROOT)", "../..")
                          for x in target['INCLUDES']]

    libs = target.get('LIBS', [])
    if win32:
      libs = [l for l in libs if l not in ('ppapi', 'ppapi_cpp')]
      target['LIBS'] = ['-l' + l + '.lib' for l in libs]
    else:
      target['LIBS'] = ['-l' + l for l in libs]
    if target['TYPE'] == 'so':
      if win32:
        target['EXT'] = ''
      else:
        target['EXT'] = '.so'
      target['GYP_TYPE'] = 'shared_library'
    elif target['TYPE'] == 'lib':
      if win32:
        target['EXT'] = ''
      else:
        target['EXT'] = '.a'
      target['GYP_TYPE'] = 'static_library'
    elif target['TYPE'] == 'main':
      target['EXT'] = '.nexe'
      target['GYP_TYPE'] = 'executable'
    else:
      Error("unknown type: %s" % target['TYPE'])

    target['CFLAGS'] = target.get('CXXFLAGS', [])

    if not win32 and ('newlib' in desc['TOOLS'] or 'glibc' in desc['TOOLS']):
      WriteNaClTargets(output, target, desc['TOOLS'])
      if target['TYPE'] == 'main':
        target['SODEPS'] = sofiles
        target['NMFACTION'] = ['-o', '<@(_outputs)', '-L<(NMF_PATH1)',
                               '-L<(NMF_PATH2)', '-D', '<(OBJDUMP)',
                               '<@(_inputs)']
        target['NMFACTION'] += soremap
        if 'newlib' in desc['TOOLS']:
          target['TOOLCHAIN'] = 'newlib'
          output.write(NMF_TARGET % target)
        if 'glibc' in desc['TOOLS']:
          target['TOOLCHAIN'] = 'glibc'
          output.write(NMF_TARGET % target)

    if win32 or getos.GetPlatform() in desc['TOOLS']:
      target['ARCH'] = 'x86_32'
      target['INCLUDES'].append('../../include')
      if win32:
        target['HOST'] = 'win'
        target['CONFIGS'] = ''
        target['CFLAGS'] = []
      else:
        target['CONFIGS'] = ''
        target['HOST'] = 'linux'
        target['CFLAGS'].append('-fPIC')
      if target['TYPE'] == 'main':
        target['GYP_TYPE'] = 'shared_library'
        if win32:
          target['EXT'] = ''
        else:
          target['EXT'] = '.so'
        output.write(HOST_EXE_TARGET % target)
      else:
        output.write(HOST_LIB_TARGET % target)

  output.write('  ],\n}\n')

  print('Writing: ' + outfile)
  open(outfile, 'w').write(output.getvalue())


def main(args):
  parser = argparse.ArgumentParser()
  parser.add_argument('-o', help='Set output filename.', dest='output')
  parser.add_argument('dsc', help='dsc to convert')
  options = parser.parse_args(args)

  if options.output:
    outdir = os.path.dirname(options.output)
    if not os.path.exists(outdir):
      os.makedirs(outdir)

  ProcessDSC(options.dsc, options.output)
  return 0


if __name__ == '__main__':
  sys.exit(main(sys.argv[1:]))
