text: # Copyright (c) 2011 Google Inc. All rights reserved.
  # Use of this source code is governed by a BSD-style license that can be
  # found in the LICENSE file.
  
  """
  SCons generator.
  
  This contains class definitions and supporting functions for generating
  pieces of SCons files for the different types of GYP targets.
  """
  
  import os
  
  
  def WriteList(fp, list, prefix='',
                          separator=',\n    ',
                          preamble=None,
                          postamble=None):
    fp.write(preamble or '')
    fp.write((separator or ' ').join([prefix + l for l in list]))
    fp.write(postamble or '')
  
  
  class TargetBase(object):
    """
    Base class for a SCons representation of a GYP target.
    """
    is_ignored = False
    target_prefix = ''
    target_suffix = ''
    def __init__(self, spec):
      self.spec = spec
    def full_product_name(self):
      """
      Returns the full name of the product being built:
  
        * Uses 'product_name' if it's set, else prefix + 'target_name'.
        * Prepends 'product_dir' if set.
        * Appends SCons suffix variables for the target type (or
          product_extension).
      """
      suffix = self.target_suffix
      product_extension = self.spec.get('product_extension')
      if product_extension:
        suffix = '.' + product_extension
      prefix = self.spec.get('product_prefix', self.target_prefix)
      name = self.spec['target_name']
      name = prefix + self.spec.get('product_name', name) + suffix
      product_dir = self.spec.get('product_dir')
      if product_dir:
        name = os.path.join(product_dir, name)
      else:
        name = os.path.join(self.out_dir, name)
      return name
  
    def write_input_files(self, fp):
      """
      Writes the definition of the input files (sources).
      """
      sources = self.spec.get('sources')
      if not sources:
        fp.write('\ninput_files = []\n')
        return
      preamble = '\ninput_files = [\n    '
      postamble = ',\n]\n'
      WriteList(fp, map(repr, sources), preamble=preamble, postamble=postamble)
  
    def builder_call(self):
      """
      Returns the actual SCons builder call to build this target.
      """
      name = self.full_product_name()
      return 'env.%s(env.File(%r), input_files)' % (self.builder_name, name)
    def write_target(self, fp, src_dir='', pre=''):
      """
      Writes the lines necessary to build this target.
      """
      fp.write('\n' + pre)
      fp.write('_outputs = %s\n' % self.builder_call())
      fp.write('target_files.extend(_outputs)\n')
  
  
  class NoneTarget(TargetBase):
    """
    A GYP target type of 'none', implicitly or explicitly.
    """
    def write_target(self, fp, pre=''):
      fp.write('\ntarget_files.extend(input_files)\n')
  
  
  class SettingsTarget(TargetBase):
    """
    A GYP target type of 'settings'.
    """
    is_ignored = True
  
  
  compilable_sources_template = """
  _result = []
  for infile in input_files:
    if env.compilable(infile):
      if (type(infile) == type('')
          and (infile.startswith(%(src_dir)r)
               or not os.path.isabs(env.subst(infile)))):
        # Force files below the build directory by replacing all '..'
        # elements in the path with '__':
        base, ext = os.path.splitext(os.path.normpath(infile))
        base = [d == '..' and '__' or d for d in base.split('/')]
        base = os.path.join(*base)
        object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base
        if not infile.startswith(%(src_dir)r):
          infile = %(src_dir)r + infile
        infile = env.%(name)s(object, infile)[0]
      else:
        infile = env.%(name)s(infile)[0]
    _result.append(infile)
  input_files = _result
  """
  
  class CompilableSourcesTargetBase(TargetBase):
    """
    An abstract base class for targets that compile their source files.
  
    We explicitly transform compilable files into object files,
    even though SCons could infer that for us, because we want
    to control where the object file ends up.  (The implicit rules
    in SCons always put the object file next to the source file.)
    """
    intermediate_builder_name = None
    def write_target(self, fp, src_dir='', pre=''):
      if self.intermediate_builder_name is None:
        raise NotImplementedError
      if src_dir and not src_dir.endswith('/'):
        src_dir += '/'
      variables = {
          'src_dir': src_dir,
          'name': self.intermediate_builder_name,
      }
      fp.write(compilable_sources_template % variables)
      super(CompilableSourcesTargetBase, self).write_target(fp)
  
  
  class ProgramTarget(CompilableSourcesTargetBase):
    """
    A GYP target type of 'executable'.
    """
    builder_name = 'GypProgram'
    intermediate_builder_name = 'StaticObject'
    target_prefix = '${PROGPREFIX}'
    target_suffix = '${PROGSUFFIX}'
    out_dir = '${TOP_BUILDDIR}'
  
  
  class StaticLibraryTarget(CompilableSourcesTargetBase):
    """
    A GYP target type of 'static_library'.
    """
    builder_name = 'GypStaticLibrary'
    intermediate_builder_name = 'StaticObject'
    target_prefix = '${LIBPREFIX}'
    target_suffix = '${LIBSUFFIX}'
    out_dir = '${LIB_DIR}'
  
  
  class SharedLibraryTarget(CompilableSourcesTargetBase):
    """
    A GYP target type of 'shared_library'.
    """
    builder_name = 'GypSharedLibrary'
    intermediate_builder_name = 'SharedObject'
    target_prefix = '${SHLIBPREFIX}'
    target_suffix = '${SHLIBSUFFIX}'
    out_dir = '${LIB_DIR}'
  
  
  class LoadableModuleTarget(CompilableSourcesTargetBase):
    """
    A GYP target type of 'loadable_module'.
    """
    builder_name = 'GypLoadableModule'
    intermediate_builder_name = 'SharedObject'
    target_prefix = '${SHLIBPREFIX}'
    target_suffix = '${SHLIBSUFFIX}'
    out_dir = '${TOP_BUILDDIR}'
  
  
  TargetMap = {
    None : NoneTarget,
    'none' : NoneTarget,
    'settings' : SettingsTarget,
    'executable' : ProgramTarget,
    'static_library' : StaticLibraryTarget,
    'shared_library' : SharedLibraryTarget,
    'loadable_module' : LoadableModuleTarget,
  }
  
  
  def Target(spec):
    return TargetMap[spec.get('type')](spec)
  