Ejemplo n.º 1
0
def GenerateOutput(target_list, target_dicts, data, params):
    """
  Generates all the output structure for the specified targets.
  """
    options = params['options']

    if options.generator_output:

        def output_path(filename):
            return filename.replace(params['cwd'], options.generator_output)
    else:

        def output_path(filename):
            return filename

    default_configuration = None

    for qualified_target in target_list:
        spec = target_dicts[qualified_target]
        if spec['toolset'] != 'target':
            raise Exception(
                'Multiple toolsets not supported in scons build (target %s)' %
                qualified_target)
        scons_target = SCons.Target(spec)
        if scons_target.is_ignored:
            continue

        # TODO:  assumes the default_configuration of the first target
        # non-Default target is the correct default for all targets.
        # Need a better model for handle variation between targets.
        if (not default_configuration
                and spec['default_configuration'] != 'Default'):
            default_configuration = spec['default_configuration']

        build_file, target = gyp.common.ParseQualifiedTarget(
            qualified_target)[:2]
        output_file = TargetFilename(target, build_file, options.suffix)
        if options.generator_output:
            output_file = output_path(output_file)

        if not spec.has_key('libraries'):
            spec['libraries'] = []

        # Add dependent static library targets to the 'libraries' value.
        deps = spec.get('dependencies', [])
        spec['scons_dependencies'] = []
        for d in deps:
            td = target_dicts[d]
            target_name = td['target_name']
            spec['scons_dependencies'].append("Alias('%s')" % target_name)
            if td['type'] in ('static_library', 'shared_library'):
                libname = td.get('product_name', target_name)
                spec['libraries'].append('lib' + libname)
            if td['type'] == 'loadable_module':
                prereqs = spec.get('scons_prerequisites', [])
                # TODO:  parameterize with <(SHARED_LIBRARY_*) variables?
                td_target = SCons.Target(td)
                td_target.target_prefix = '${SHLIBPREFIX}'
                td_target.target_suffix = '${SHLIBSUFFIX}'

        GenerateSConscript(output_file, spec, build_file, data[build_file])

    if not default_configuration:
        default_configuration = 'Default'

    for build_file in sorted(data.keys()):
        path, ext = os.path.splitext(build_file)
        if ext != '.gyp':
            continue
        output_dir, basename = os.path.split(path)
        output_filename = path + '_main' + options.suffix + '.scons'

        all_targets = gyp.common.AllTargets(target_list, target_dicts,
                                            build_file)
        sconscript_files = {}
        for t in all_targets:
            scons_target = SCons.Target(target_dicts[t])
            if scons_target.is_ignored:
                continue
            bf, target = gyp.common.ParseQualifiedTarget(t)[:2]
            target_filename = TargetFilename(target, bf, options.suffix)
            tpath = gyp.common.RelativePath(target_filename, output_dir)
            sconscript_files[target] = tpath

        output_filename = output_path(output_filename)
        if sconscript_files:
            GenerateSConscriptWrapper(build_file, data[build_file], basename,
                                      output_filename, sconscript_files,
                                      default_configuration)
Ejemplo n.º 2
0
def GenerateSConscript(output_filename, spec, build_file, build_file_data):
    """
  Generates a SConscript file for a specific target.

  This generates a SConscript file suitable for building any or all of
  the target's configurations.

  A SConscript file may be called multiple times to generate targets for
  multiple configurations.  Consequently, it needs to be ready to build
  the target for any requested configuration, and therefore contains
  information about the settings for all configurations (generated into
  the SConscript file at gyp configuration time) as well as logic for
  selecting (at SCons build time) the specific configuration being built.

  The general outline of a generated SConscript file is:

    --  Header

    --  Import 'env'.  This contains a $CONFIG_NAME construction
        variable that specifies what configuration to build
        (e.g. Debug, Release).

    --  Configurations.  This is a dictionary with settings for
        the different configurations (Debug, Release) under which this
        target can be built.  The values in the dictionary are themselves
        dictionaries specifying what construction variables should added
        to the local copy of the imported construction environment
        (Append), should be removed (FilterOut), and should outright
        replace the imported values (Replace).

    --  Clone the imported construction environment and update
        with the proper configuration settings.

    --  Initialize the lists of the targets' input structure and prerequisites.

    --  Target-specific actions and rules.  These come after the
        input file and prerequisite initializations because the
        outputs of the actions and rules may affect the input file
        list (process_outputs_as_sources) and get added to the list of
        prerequisites (so that they're guaranteed to be executed before
        building the target).

    --  Call the Builder for the target itself.

    --  Arrange for any copies to be made into installation directories.

    --  Set up the {name} Alias (phony Node) for the target as the
        primary handle for building all of the target's pieces.

    --  Use env.Require() to make sure the prerequisites (explicitly
        specified, but also including the actions and rules) are built
        before the target itself.

    --  Return the {name} Alias to the calling SConstruct file
        so it can be added to the list of default targets.
  """
    scons_target = SCons.Target(spec)

    gyp_dir = os.path.dirname(output_filename)
    if not gyp_dir:
        gyp_dir = '.'
    gyp_dir = os.path.abspath(gyp_dir)

    output_dir = os.path.dirname(output_filename)
    src_dir = build_file_data['_DEPTH']
    src_dir_rel = gyp.common.RelativePath(src_dir, output_dir)
    subdir = gyp.common.RelativePath(os.path.dirname(build_file), src_dir)
    src_subdir = '$SRC_DIR/' + subdir
    src_subdir_ = src_subdir + '/'

    component_name = os.path.splitext(os.path.basename(build_file))[0]
    target_name = spec['target_name']

    if not os.path.exists(gyp_dir):
        os.makedirs(gyp_dir)
    fp = open(output_filename, 'w')
    fp.write(header)

    fp.write('\nimport os\n')
    fp.write('\nImport("env")\n')

    #
    fp.write('\n')
    fp.write('env = env.Clone(COMPONENT_NAME=%s,\n' % repr(component_name))
    fp.write('                TARGET_NAME=%s)\n' % repr(target_name))

    #
    for config in spec['configurations'].itervalues():
        if config.get('scons_line_length'):
            fp.write(_spawn_hack)
            break

    #
    indent = ' ' * 12
    fp.write('\n')
    fp.write('configurations = {\n')
    for config_name, config in spec['configurations'].iteritems():
        fp.write('    \'%s\' : {\n' % config_name)

        fp.write('        \'Append\' : dict(\n')
        GenerateConfig(fp, config, indent, src_subdir)
        libraries = spec.get('libraries')
        if libraries:
            WriteList(fp,
                      map(repr, libraries),
                      prefix=indent,
                      preamble='%sLIBS = [\n    ' % indent,
                      postamble='\n%s],\n' % indent)
        fp.write('        ),\n')

        fp.write('        \'FilterOut\' : dict(\n')
        for key, var in config.get('scons_remove', {}).iteritems():
            fp.write('             %s = %s,\n' % (key, repr(var)))
        fp.write('        ),\n')

        fp.write('        \'Replace\' : dict(\n')
        scons_settings = config.get('scons_variable_settings', {})
        for key in sorted(scons_settings.keys()):
            val = pprint.pformat(scons_settings[key])
            fp.write('             %s = %s,\n' % (key, val))
        if 'c++' in spec.get('link_languages', []):
            fp.write('             %s = %s,\n' % ('LINK', repr('$CXX')))
        if config.get('scons_line_length'):
            fp.write('             SPAWN = gyp_spawn,\n')
        fp.write('        ),\n')

        fp.write('        \'ImportExternal\' : [\n')
        for var in config.get('scons_import_variables', []):
            fp.write('             %s,\n' % repr(var))
        fp.write('        ],\n')

        fp.write('        \'PropagateExternal\' : [\n')
        for var in config.get('scons_propagate_variables', []):
            fp.write('             %s,\n' % repr(var))
        fp.write('        ],\n')

        fp.write('    },\n')
    fp.write('}\n')

    fp.write('\n'
             'config = configurations[env[\'CONFIG_NAME\']]\n'
             'env.Append(**config[\'Append\'])\n'
             'env.FilterOut(**config[\'FilterOut\'])\n'
             'env.Replace(**config[\'Replace\'])\n')

    fp.write('\n'
             '# Scons forces -fPIC for SHCCFLAGS on some platforms.\n'
             '# Disable that so we can control it from cflags in gyp.\n'
             '# Note that Scons itself is inconsistent with its -fPIC\n'
             '# setting. SHCCFLAGS forces -fPIC, and SHCFLAGS does not.\n'
             '# This will make SHCCFLAGS consistent with SHCFLAGS.\n'
             'env[\'SHCCFLAGS\'] = [\'$CCFLAGS\']\n')

    fp.write('\n'
             'for _var in config[\'ImportExternal\']:\n'
             '  if _var in ARGUMENTS:\n'
             '    env[_var] = ARGUMENTS[_var]\n'
             '  elif _var in os.environ:\n'
             '    env[_var] = os.environ[_var]\n'
             'for _var in config[\'PropagateExternal\']:\n'
             '  if _var in ARGUMENTS:\n'
             '    env[_var] = ARGUMENTS[_var]\n'
             '  elif _var in os.environ:\n'
             '    env[\'ENV\'][_var] = os.environ[_var]\n')

    fp.write('\n' "env['ENV']['LD_LIBRARY_PATH'] = env.subst('$LIB_DIR')\n")

    #
    #fp.write("\nif env.has_key('CPPPATH'):\n")
    #fp.write("  env['CPPPATH'] = map(env.Dir, env['CPPPATH'])\n")

    variants = spec.get('variants', {})
    for setting in sorted(variants.keys()):
        if_fmt = 'if ARGUMENTS.get(%s) not in (None, \'0\'):\n'
        fp.write('\n')
        fp.write(if_fmt % repr(setting.upper()))
        fp.write('  env.AppendUnique(\n')
        GenerateConfig(fp, variants[setting], indent, src_subdir)
        fp.write('  )\n')

    #
    scons_target.write_input_files(fp)

    fp.write('\n')
    fp.write('target_files = []\n')
    prerequisites = spec.get('scons_prerequisites', [])
    fp.write('prerequisites = %s\n' % pprint.pformat(prerequisites))

    actions = spec.get('actions', [])
    for action in actions:
        a = ['cd', src_subdir, '&&'] + action['action']
        message = action.get('message')
        if message:
            message = repr(message)
        inputs = [FixPath(f, src_subdir_) for f in action.get('inputs', [])]
        outputs = [FixPath(f, src_subdir_) for f in action.get('outputs', [])]
        if outputs:
            template = _command_template
        else:
            template = _alias_template
        fp.write(
            template % {
                'inputs': pprint.pformat(inputs),
                'outputs': pprint.pformat(outputs),
                'action': pprint.pformat(a),
                'message': message,
                'target_name': target_name,
            })
        if int(action.get('process_outputs_as_sources', 0)):
            fp.write('input_files.extend(_outputs)\n')
        fp.write('prerequisites.extend(_outputs)\n')
        fp.write('target_files.extend(_outputs)\n')

    rules = spec.get('rules', [])
    for rule in rules:
        name = re.sub('[^a-zA-Z0-9_]', '_', rule['rule_name'])
        message = rule.get('message')
        if message:
            message = repr(message)
        if int(rule.get('process_outputs_as_sources', 0)):
            poas_line = '_processed_input_files.extend(_generated)'
        else:
            poas_line = '_processed_input_files.append(infile)'
        inputs = [FixPath(f, src_subdir_) for f in rule.get('inputs', [])]
        outputs = [FixPath(f, src_subdir_) for f in rule.get('outputs', [])]
        # Skip a rule with no action and no inputs.
        if 'action' not in rule and not rule.get('rule_sources', []):
            continue
        a = ['cd', src_subdir, '&&'] + rule['action']
        fp.write(
            _rule_template % {
                'inputs': pprint.pformat(inputs),
                'outputs': pprint.pformat(outputs),
                'action': pprint.pformat(a),
                'extension': rule['extension'],
                'name': name,
                'message': message,
                'process_outputs_as_sources_line': poas_line,
                'src_dir': src_subdir_,
            })

    scons_target.write_target(fp, src_subdir)

    copies = spec.get('copies', [])
    if copies:
        fp.write(_copy_action_template)
    for copy in copies:
        destdir = None
        files = None
        try:
            destdir = copy['destination']
        except KeyError, e:
            gyp.common.ExceptionAppend(
                e, "Required 'destination' key missing for 'copies' in %s." %
                build_file)
            raise
        try:
            files = copy['structure']
        except KeyError, e:
            gyp.common.ExceptionAppend(
                e, "Required 'structure' key missing for 'copies' in %s." %
                build_file)
            raise