def produce_config()

in builder/core/project.py [0:0]


def produce_config(build_spec, project, overrides=None, variant_config=None, **additional_variables):
    """ Traverse the configurations to produce one for the given spec """
    host_os = current_os()

    defaults = {
        'hosts': HOSTS,
        'targets': TARGETS,
        'compilers': COMPILERS,
        'architectures': ARCHS,
    }

    # Build the list of config options to poll
    configs = UniqueList()

    # Processes a config object (could come from a file), searching for keys hosts, targets, and compilers
    def process_config(config, depth=0):

        def process_element(map, element_name, instance):
            if not map or not isinstance(map, dict):
                return

            element = map.get(element_name)
            # Some keys will just contain lists or scalars (e.g. hosts)
            if not element or not isinstance(element, dict):
                return

            new_config = element.get(instance)
            if not new_config:
                return

            configs.append(new_config)

            # recursively process config as long as sub-sections are found
            process_config(new_config, depth+1)

            return new_config

        # Pull out any top level defaults
        if depth == 0:
            defaults = {}
            for key, value in config.items():
                if key not in ('hosts', 'targets', 'compilers', 'architectures', 'variants'):
                    defaults[key] = value
            if len(defaults) > 0:
                configs.append(defaults)

        # pull out arch + any aliases
        archs = _arch_aliases(build_spec)
        for arch in archs:
            process_element(config, 'architectures', arch)

        # Get defaults from os (linux) then override with host (al2, manylinux, etc)
        if host_os != build_spec.host:
            process_element(config, 'hosts', host_os)
        process_element(config, 'hosts', build_spec.host)

        # pull out spec target to override
        process_element(config, 'targets', build_spec.target)

        # pull out spec compiler and version info
        compiler = process_element(config, 'compilers', build_spec.compiler)

        # Allow most specific resolves to come last
        process_element(compiler, 'versions', build_spec.compiler_version)

    # Process defaults first
    process_config(defaults)

    # process platform
    # target, arch -> platform
    target_platform = '{}-{}'.format(build_spec.target, build_spec.arch)
    configs.append(PLATFORMS[target_platform])

    # then override with config file
    project_config = project.config
    process_config(project_config)

    # then add variant
    if variant_config:
        process_config(variant_config)

    new_version = {
        'spec': build_spec,
    }
    # Iterate all keys and apply them
    for key, default in KEYS.items():
        new_version[key] = default

        for config in configs:
            override_key = '!' + key
            apply_key = '+' + key
            if override_key in config:  # Force Override
                new_version[key] = config[override_key]
            elif apply_key in config:  # Force Apply
                _apply_value(new_version, key, config[apply_key])
            elif key in config:
                # Project configs override defaults unless force applied
                if key in project_config and config[key] == project_config[key]:
                    new_version[key] = config[key]
                else:  # By default, merge all values (except strings)
                    _apply_value(new_version, key, config[key])

    new_version = _coalesce_pkg_options(build_spec, new_version)

    def apply_overrides(config, overrides):
        if not overrides:
            return
        for key, val in overrides.items():
            if key.startswith('!'):
                # re-init and replace current value, obeying type coercion rules
                key = key[1:]
                if key in config:
                    config[key] = config[key].__class__()
                _apply_value(config, key, val)
            else:
                _apply_value(config, key, val)

    apply_overrides(new_version, overrides)

    # Default variables
    replacements = {
        'host': build_spec.host,
        'compiler': build_spec.compiler,
        'version': build_spec.compiler_version,
        'target': build_spec.target,
        'arch': build_spec.arch,
        'cwd': os.getcwd(),
        **additional_variables,
    }

    # Pull variables from the configs
    for config in configs:
        if 'variables' in config:
            variables = config['variables']
            assert type(variables) == dict
            replacements.update(variables)

    # Post process
    new_version = replace_variables(new_version, replacements)
    new_version['variables'] = replacements

    # resolve build variants for the top level config
    if not variant_config:
        variants = project_config.get('variants', {})
        resolved_variants = {}
        for name, variant_config in variants.items():
            variant = produce_config(build_spec, project, None, variant_config, **additional_variables)
            resolved_variants[name] = variant
        new_version['variants'] = resolved_variants

    new_version['__processed'] = True

    return new_version