示例#1
0
    def test_check_capability_mapping(self):
        """Test comparing the functionality of two toolchains"""
        test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
        init_config(build_options={
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })
        get_toolchain_hierarchy.clear()
        foss_hierarchy = get_toolchain_hierarchy({'name': 'foss', 'version': '2018a'}, incl_capabilities=True)
        iimpi_hierarchy = get_toolchain_hierarchy({'name': 'iimpi', 'version': '2016.01'},
                                                  incl_capabilities=True)

        # Hierarchies are returned with top-level toolchain last, foss has 4 elements here, intel has 2
        self.assertEqual(foss_hierarchy[0]['name'], 'GCC')
        self.assertEqual(foss_hierarchy[1]['name'], 'golf')
        self.assertEqual(foss_hierarchy[2]['name'], 'gompi')
        self.assertEqual(foss_hierarchy[3]['name'], 'foss')
        self.assertEqual(iimpi_hierarchy[0]['name'], 'GCCcore')
        self.assertEqual(iimpi_hierarchy[1]['name'], 'iccifort')
        self.assertEqual(iimpi_hierarchy[2]['name'], 'iimpi')

        # golf <-> iimpi (should return False)
        self.assertFalse(check_capability_mapping(foss_hierarchy[1], iimpi_hierarchy[1]), "golf requires math libs")
        # gompi <-> iimpi
        self.assertTrue(check_capability_mapping(foss_hierarchy[2], iimpi_hierarchy[2]))
        # GCC <-> iimpi
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[2]))
        # GCC <-> iccifort
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[1]))
        # GCC <-> GCCcore
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[0]))
    def test_check_capability_mapping(self):
        """Test comparing the functionality of two toolchains"""
        test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
        init_config(build_options={
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })
        get_toolchain_hierarchy.clear()
        foss_hierarchy = get_toolchain_hierarchy({'name': 'foss', 'version': '2018a'}, incl_capabilities=True)
        iimpi_hierarchy = get_toolchain_hierarchy({'name': 'iimpi', 'version': '2016.01'},
                                                  incl_capabilities=True)

        # Hierarchies are returned with top-level toolchain last, foss has 4 elements here, intel has 2
        self.assertEqual(foss_hierarchy[0]['name'], 'GCC')
        self.assertEqual(foss_hierarchy[1]['name'], 'golf')
        self.assertEqual(foss_hierarchy[2]['name'], 'gompi')
        self.assertEqual(foss_hierarchy[3]['name'], 'foss')
        self.assertEqual(iimpi_hierarchy[0]['name'], 'GCCcore')
        self.assertEqual(iimpi_hierarchy[1]['name'], 'iccifort')
        self.assertEqual(iimpi_hierarchy[2]['name'], 'iimpi')

        # golf <-> iimpi (should return False)
        self.assertFalse(check_capability_mapping(foss_hierarchy[1], iimpi_hierarchy[1]), "golf requires math libs")
        # gompi <-> iimpi
        self.assertTrue(check_capability_mapping(foss_hierarchy[2], iimpi_hierarchy[2]))
        # GCC <-> iimpi
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[2]))
        # GCC <-> iccifort
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[1]))
        # GCC <-> GCCcore
        self.assertTrue(check_capability_mapping(foss_hierarchy[0], iimpi_hierarchy[0]))
示例#3
0
    def test_match_minimum_tc_specs(self):
        """Test matching a toolchain to lowest possible in a hierarchy"""
        test_easyconfigs = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'easyconfigs',
            'test_ecs')
        init_config(
            build_options={
                'robot_path': test_easyconfigs,
                'silent': True,
                'valid_module_classes': module_classes(),
            })
        get_toolchain_hierarchy.clear()
        foss_hierarchy = get_toolchain_hierarchy(
            {
                'name': 'foss',
                'version': '2018a'
            }, incl_capabilities=True)
        iimpi_hierarchy = get_toolchain_hierarchy(
            {
                'name': 'iimpi',
                'version': '2016.01'
            }, incl_capabilities=True)
        # Hierarchies are returned with top-level toolchain last, foss has 4 elements here, intel has 2
        self.assertEqual(foss_hierarchy[0]['name'], 'GCC')
        self.assertEqual(foss_hierarchy[1]['name'], 'golf')
        self.assertEqual(foss_hierarchy[2]['name'], 'gompi')
        self.assertEqual(foss_hierarchy[3]['name'], 'foss')
        self.assertEqual(iimpi_hierarchy[0]['name'], 'GCCcore')
        self.assertEqual(iimpi_hierarchy[1]['name'], 'iccifort')
        self.assertEqual(iimpi_hierarchy[2]['name'], 'iimpi')

        # base compiler first (GCCcore which maps to GCC/6.4.0-2.28)
        self.assertEqual(
            match_minimum_tc_specs(iimpi_hierarchy[0], foss_hierarchy), {
                'name': 'GCC',
                'version': '6.4.0-2.28'
            })
        # then iccifort (which also maps to GCC/6.4.0-2.28)
        self.assertEqual(
            match_minimum_tc_specs(iimpi_hierarchy[1], foss_hierarchy), {
                'name': 'GCC',
                'version': '6.4.0-2.28'
            })
        # Then MPI
        self.assertEqual(
            match_minimum_tc_specs(iimpi_hierarchy[2], foss_hierarchy), {
                'name': 'gompi',
                'version': '2018a'
            })
        # Check against own math only subtoolchain for math
        self.assertEqual(
            match_minimum_tc_specs(foss_hierarchy[1], foss_hierarchy), {
                'name': 'golf',
                'version': '2018a'
            })
        # Make sure there's an error when we can't do the mapping
        error_msg = "No possible mapping from source toolchain spec .*"
        self.assertErrorRegex(EasyBuildError, error_msg,
                              match_minimum_tc_specs, foss_hierarchy[3],
                              iimpi_hierarchy)
示例#4
0
def map_toolchain_hierarchies(source_toolchain, target_toolchain, modtool):
    """
    Create a map between toolchain hierarchy of the initial toolchain and that of the target toolchain

    :param source_toolchain: initial toolchain of the easyconfig(s)
    :param target_toolchain: target toolchain for tweaked easyconfig(s)
    :param modtool: module tool used

    :return: mapping from source hierarchy to target hierarchy
    """
    tc_mapping = {}
    source_tc_hierarchy = get_toolchain_hierarchy(source_toolchain,
                                                  incl_capabilities=True)
    target_tc_hierarchy = get_toolchain_hierarchy(target_toolchain,
                                                  incl_capabilities=True)

    for toolchain_spec in source_tc_hierarchy:
        tc_mapping[toolchain_spec['name']] = match_minimum_tc_specs(
            toolchain_spec, target_tc_hierarchy)

    # Check for presence of binutils in source and target toolchain dependency trees
    # (only do this when GCCcore is present in both and GCCcore is not the top of the tree)
    gcccore = GCCcore.NAME
    source_tc_names = [tc_spec['name'] for tc_spec in source_tc_hierarchy]
    target_tc_names = [tc_spec['name'] for tc_spec in target_tc_hierarchy]
    if gcccore in source_tc_names and gcccore in target_tc_names and source_tc_hierarchy[
            -1]['name'] != gcccore:
        binutils = 'binutils'
        # Determine the dependency trees
        source_dep_tree = get_dep_tree_of_toolchain(source_tc_hierarchy[-1],
                                                    modtool)
        target_dep_tree = get_dep_tree_of_toolchain(target_tc_hierarchy[-1],
                                                    modtool)
        # Find the binutils mapping
        if binutils in [dep['name'] for dep in source_dep_tree]:
            # We need the binutils that was built using GCCcore (we assume that everything is using standard behaviour:
            # build binutils with GCCcore and then use that for anything built with GCCcore)
            binutils_deps = [
                dep for dep in target_dep_tree if dep['name'] == binutils
            ]
            binutils_gcccore_deps = [
                dep for dep in binutils_deps
                if dep['toolchain']['name'] == gcccore
            ]
            if len(binutils_gcccore_deps) == 1:
                tc_mapping[binutils] = {
                    'version': binutils_gcccore_deps[0]['version'],
                    'versionsuffix': binutils_gcccore_deps[0]['versionsuffix']
                }
            else:
                raise EasyBuildError(
                    "Target hierarchy %s should have binutils using GCCcore, can't determine mapping!",
                    target_tc_hierarchy[-1])

    return tc_mapping
示例#5
0
    def test_match_minimum_tc_specs(self):
        """Test matching a toolchain to lowest possible in a hierarchy"""
        test_easyconfigs = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'easyconfigs',
            'test_ecs')
        init_config(
            build_options={
                'valid_module_classes': module_classes(),
                'robot_path': test_easyconfigs,
            })
        get_toolchain_hierarchy.clear()
        goolf_hierarchy = get_toolchain_hierarchy(
            {
                'name': 'goolf',
                'version': '1.4.10'
            }, incl_capabilities=True)
        iimpi_hierarchy = get_toolchain_hierarchy(
            {
                'name': 'iimpi',
                'version': '5.5.3-GCC-4.8.3'
            },
            incl_capabilities=True)
        # Hierarchies are returned with top-level toolchain last, goolf has 4 elements here, intel has 2
        self.assertEqual(goolf_hierarchy[0]['name'], 'GCC')
        self.assertEqual(goolf_hierarchy[1]['name'], 'golf')
        self.assertEqual(goolf_hierarchy[2]['name'], 'gompi')
        self.assertEqual(goolf_hierarchy[3]['name'], 'goolf')
        self.assertEqual(iimpi_hierarchy[0]['name'], 'iccifort')
        self.assertEqual(iimpi_hierarchy[1]['name'], 'iimpi')

        # Compiler first
        self.assertEqual(
            match_minimum_tc_specs(iimpi_hierarchy[0], goolf_hierarchy), {
                'name': 'GCC',
                'version': '4.7.2'
            })
        # Then MPI
        self.assertEqual(
            match_minimum_tc_specs(iimpi_hierarchy[1], goolf_hierarchy), {
                'name': 'gompi',
                'version': '1.4.10'
            })
        # Check against own math only subtoolchain for math
        self.assertEqual(
            match_minimum_tc_specs(goolf_hierarchy[1], goolf_hierarchy), {
                'name': 'golf',
                'version': '1.4.10'
            })
        # Make sure there's an error when we can't do the mapping
        error_msg = "No possible mapping from source toolchain spec .*"
        self.assertErrorRegex(EasyBuildError, error_msg,
                              match_minimum_tc_specs, goolf_hierarchy[3],
                              iimpi_hierarchy)
示例#6
0
    def test_get_toolchain_hierarchy(self):
        """Test get_toolchain_hierarchy function."""
        test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs')
        init_config(build_options={
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })

        goolf_hierarchy = get_toolchain_hierarchy({'name': 'goolf', 'version': '1.4.10'})
        self.assertEqual(goolf_hierarchy, [
            {'name': 'GCC', 'version': '4.7.2'},
            {'name': 'gompi', 'version': '1.4.10'},
            {'name': 'goolf', 'version': '1.4.10'},
        ])

        iimpi_hierarchy = get_toolchain_hierarchy({'name': 'iimpi', 'version': '5.5.3-GCC-4.8.3'})
        self.assertEqual(iimpi_hierarchy, [
            {'name': 'iccifort', 'version': '2013.5.192-GCC-4.8.3'},
            {'name': 'iimpi', 'version': '5.5.3-GCC-4.8.3'},
        ])

        # test also including dummy
        init_config(build_options={
            'add_dummy_to_minimal_toolchains': True,
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })

        get_toolchain_hierarchy.clear()
        gompi_hierarchy = get_toolchain_hierarchy({'name': 'gompi', 'version': '1.4.10'})
        self.assertEqual(gompi_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCC', 'version': '4.7.2'},
            {'name': 'gompi', 'version': '1.4.10'},
        ])

        get_toolchain_hierarchy.clear()
        # check whether GCCcore is considered as subtoolchain, even if it's only listed as a dep
        gcc_hierarchy = get_toolchain_hierarchy({'name': 'GCC', 'version': '4.9.3-2.25'})
        self.assertEqual(gcc_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCCcore', 'version': '4.9.3'},
            {'name': 'GCC', 'version': '4.9.3-2.25'},
        ])

        get_toolchain_hierarchy.clear()
        iccifort_hierarchy = get_toolchain_hierarchy({'name': 'iccifort', 'version': '2016.1.150-GCC-4.9.3-2.25'})
        self.assertEqual(iccifort_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCCcore', 'version': '4.9.3'},
            {'name': 'iccifort', 'version': '2016.1.150-GCC-4.9.3-2.25'},
        ])
示例#7
0
def map_toolchain_hierarchies(source_toolchain, target_toolchain, modtool):
    """
    Create a map between toolchain hierarchy of the initial toolchain and that of the target toolchain

    :param source_toolchain: initial toolchain of the easyconfig(s)
    :param target_toolchain: target toolchain for tweaked easyconfig(s)
    :param modtool: module tool used

    :return: mapping from source hierarchy to target hierarchy
    """
    tc_mapping = {}
    source_tc_hierarchy = get_toolchain_hierarchy(source_toolchain, incl_capabilities=True)
    target_tc_hierarchy = get_toolchain_hierarchy(target_toolchain, incl_capabilities=True)

    for toolchain_spec in source_tc_hierarchy:
        tc_mapping[toolchain_spec['name']] = match_minimum_tc_specs(toolchain_spec, target_tc_hierarchy)

    # Check for presence of binutils in source and target toolchain dependency trees
    # (only do this when GCCcore is present in both and GCCcore is not the top of the tree)
    gcccore = GCCcore.NAME
    source_tc_names = [tc_spec['name'] for tc_spec in source_tc_hierarchy]
    target_tc_names = [tc_spec['name'] for tc_spec in target_tc_hierarchy]
    if gcccore in source_tc_names and gcccore in target_tc_names and source_tc_hierarchy[-1]['name'] != gcccore:
        binutils = 'binutils'
        # Determine the dependency trees
        source_dep_tree = get_dep_tree_of_toolchain(source_tc_hierarchy[-1], modtool)
        target_dep_tree = get_dep_tree_of_toolchain(target_tc_hierarchy[-1], modtool)
        # Find the binutils mapping
        if binutils in [dep['name'] for dep in source_dep_tree]:
            # We need the binutils that was built using GCCcore (we assume that everything is using standard behaviour:
            # build binutils with GCCcore and then use that for anything built with GCCcore)
            binutils_deps = [dep for dep in target_dep_tree if dep['name'] == binutils]
            binutils_gcccore_deps = [dep for dep in binutils_deps if dep['toolchain']['name'] == gcccore]
            if len(binutils_gcccore_deps) == 1:
                tc_mapping[binutils] = {'version': binutils_gcccore_deps[0]['version'],
                                        'versionsuffix': binutils_gcccore_deps[0]['versionsuffix']}
            else:
                raise EasyBuildError("Target hierarchy %s should have binutils using GCCcore, can't determine mapping!",
                                     target_tc_hierarchy[-1])

    return tc_mapping
示例#8
0
    def test_match_minimum_tc_specs(self):
        """Test matching a toolchain to lowest possible in a hierarchy"""
        test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
        init_config(build_options={
            'robot_path': test_easyconfigs,
            'silent': True,
            'valid_module_classes': module_classes(),
        })
        get_toolchain_hierarchy.clear()
        foss_hierarchy = get_toolchain_hierarchy({'name': 'foss', 'version': '2018a'}, incl_capabilities=True)
        iimpi_hierarchy = get_toolchain_hierarchy({'name': 'iimpi', 'version': '2016.01'},
                                                  incl_capabilities=True)
        # Hierarchies are returned with top-level toolchain last, foss has 4 elements here, intel has 2
        self.assertEqual(foss_hierarchy[0]['name'], 'GCC')
        self.assertEqual(foss_hierarchy[1]['name'], 'golf')
        self.assertEqual(foss_hierarchy[2]['name'], 'gompi')
        self.assertEqual(foss_hierarchy[3]['name'], 'foss')
        self.assertEqual(iimpi_hierarchy[0]['name'], 'GCCcore')
        self.assertEqual(iimpi_hierarchy[1]['name'], 'iccifort')
        self.assertEqual(iimpi_hierarchy[2]['name'], 'iimpi')

        # base compiler first (GCCcore which maps to GCC/6.4.0-2.28)
        self.assertEqual(match_minimum_tc_specs(iimpi_hierarchy[0], foss_hierarchy),
                         {'name': 'GCC', 'version': '6.4.0-2.28'})
        # then iccifort (which also maps to GCC/6.4.0-2.28)
        self.assertEqual(match_minimum_tc_specs(iimpi_hierarchy[1], foss_hierarchy),
                         {'name': 'GCC', 'version': '6.4.0-2.28'})
        # Then MPI
        self.assertEqual(match_minimum_tc_specs(iimpi_hierarchy[2], foss_hierarchy),
                         {'name': 'gompi', 'version': '2018a'})
        # Check against own math only subtoolchain for math
        self.assertEqual(match_minimum_tc_specs(foss_hierarchy[1], foss_hierarchy),
                         {'name': 'golf', 'version': '2018a'})
        # Make sure there's an error when we can't do the mapping
        error_msg = "No possible mapping from source toolchain spec .*"
        self.assertErrorRegex(EasyBuildError, error_msg, match_minimum_tc_specs,
                              foss_hierarchy[3], iimpi_hierarchy)
示例#9
0
    def test_get_toolchain_hierarchy(self):
        """Test get_toolchain_hierarchy function."""
        test_easyconfigs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
        init_config(build_options={
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })

        goolf_hierarchy = get_toolchain_hierarchy({'name': 'goolf', 'version': '1.4.10'})
        self.assertEqual(goolf_hierarchy, [
            {'name': 'GCC', 'version': '4.7.2'},
            {'name': 'gompi', 'version': '1.4.10'},
            {'name': 'goolf', 'version': '1.4.10'},
        ])

        iimpi_hierarchy = get_toolchain_hierarchy({'name': 'iimpi', 'version': '5.5.3-GCC-4.8.3'})
        self.assertEqual(iimpi_hierarchy, [
            {'name': 'iccifort', 'version': '2013.5.192-GCC-4.8.3'},
            {'name': 'iimpi', 'version': '5.5.3-GCC-4.8.3'},
        ])

        # test also including dummy
        init_config(build_options={
            'add_dummy_to_minimal_toolchains': True,
            'valid_module_classes': module_classes(),
            'robot_path': test_easyconfigs,
        })

        get_toolchain_hierarchy.clear()
        gompi_hierarchy = get_toolchain_hierarchy({'name': 'gompi', 'version': '1.4.10'})
        self.assertEqual(gompi_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCC', 'version': '4.7.2'},
            {'name': 'gompi', 'version': '1.4.10'},
        ])

        get_toolchain_hierarchy.clear()
        # check whether GCCcore is considered as subtoolchain, even if it's only listed as a dep
        gcc_hierarchy = get_toolchain_hierarchy({'name': 'GCC', 'version': '4.9.3-2.25'})
        self.assertEqual(gcc_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCCcore', 'version': '4.9.3'},
            {'name': 'GCC', 'version': '4.9.3-2.25'},
        ])

        get_toolchain_hierarchy.clear()
        iccifort_hierarchy = get_toolchain_hierarchy({'name': 'iccifort', 'version': '2016.1.150-GCC-4.9.3-2.25'})
        self.assertEqual(iccifort_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'GCCcore', 'version': '4.9.3'},
            {'name': 'iccifort', 'version': '2016.1.150-GCC-4.9.3-2.25'},
        ])

        get_toolchain_hierarchy.clear()
        build_options = {
            'add_dummy_to_minimal_toolchains': True,
            'external_modules_metadata': ConfigObj(),
            'robot_path': test_easyconfigs,
        }
        init_config(build_options=build_options)
        craycce_hierarchy = get_toolchain_hierarchy({'name': 'CrayCCE', 'version': '5.1.29'})
        self.assertEqual(craycce_hierarchy, [
            {'name': 'dummy', 'version': ''},
            {'name': 'CrayCCE', 'version': '5.1.29'},
        ])
示例#10
0
def tweak(easyconfigs, build_specs, modtool, targetdirs=None):
    """Tweak list of easyconfigs according to provided build specifications."""
    tweaked_ecs_path, tweaked_ecs_deps_path = None, None
    if targetdirs is not None:
        tweaked_ecs_path, tweaked_ecs_deps_path = targetdirs
    # make sure easyconfigs all feature the same toolchain (otherwise we *will* run into trouble)
    toolchains = nub(
        ['%(name)s/%(version)s' % ec['ec']['toolchain'] for ec in easyconfigs])
    if len(toolchains) > 1:
        raise EasyBuildError(
            "Multiple toolchains featured in easyconfigs, --try-X not supported in that case: %s",
            toolchains)
    # Toolchain is unique, let's store it
    source_toolchain = easyconfigs[-1]['ec']['toolchain']
    modifying_toolchains = False
    target_toolchain = {}
    src_to_dst_tc_mapping = {}
    revert_to_regex = False

    if 'toolchain_name' in build_specs or 'toolchain_version' in build_specs:
        keys = build_specs.keys()

        # Make sure there are no more build_specs, as combining --try-toolchain* with other options is currently not
        # supported
        if any(key not in ['toolchain_name', 'toolchain_version', 'toolchain']
               for key in keys):
            print_warning(
                "Combining --try-toolchain* with other build options is not fully supported: using regex"
            )
            revert_to_regex = True

        if not revert_to_regex:
            # we're doing something with the toolchain,
            # so build specifications should be applied to whole dependency graph;
            # obtain full dependency graph for specified easyconfigs;
            # easyconfigs will be ordered 'top-to-bottom' (toolchains and dependencies appearing first)
            modifying_toolchains = True

            if 'toolchain_name' in keys:
                target_toolchain['name'] = build_specs['toolchain_name']
            else:
                target_toolchain['name'] = source_toolchain['name']

            if 'toolchain_version' in keys:
                target_toolchain['version'] = build_specs['toolchain_version']
            else:
                target_toolchain['version'] = source_toolchain['version']

            if build_option('map_toolchains'):
                try:
                    src_to_dst_tc_mapping = map_toolchain_hierarchies(
                        source_toolchain, target_toolchain, modtool)
                except EasyBuildError as err:
                    # make sure exception was raised by match_minimum_tc_specs because toolchain mapping didn't work
                    if "No possible mapping from source toolchain" in err.msg:
                        error_msg = err.msg + '\n'
                        error_msg += "Toolchain %s is not equivalent to toolchain %s in terms of capabilities. "
                        error_msg += "(If you know what you are doing, "
                        error_msg += "you can use --disable-map-toolchains to proceed anyway.)"
                        raise EasyBuildError(error_msg,
                                             target_toolchain['name'],
                                             source_toolchain['name'])
                    else:
                        # simply re-raise the exception if something else went wrong
                        raise err
            else:
                msg = "Mapping of (sub)toolchains disabled, so falling back to regex mode, "
                msg += "disabling recursion and not changing (sub)toolchains for dependencies"
                _log.info(msg)
                revert_to_regex = True
                modifying_toolchains = False

        if not revert_to_regex:
            _log.debug(
                "Applying build specifications recursively (no software name/version found): %s",
                build_specs)
            orig_ecs = resolve_dependencies(easyconfigs,
                                            modtool,
                                            retain_all_deps=True)

            # Filter out the toolchain hierarchy (which would only appear if we are applying build_specs recursively)
            # We can leave any dependencies they may have as they will only be used if required (or originally listed)
            _log.debug("Filtering out toolchain hierarchy for %s",
                       source_toolchain)

            i = 0
            while i < len(orig_ecs):
                tc_names = [
                    tc['name']
                    for tc in get_toolchain_hierarchy(source_toolchain)
                ]
                if orig_ecs[i]['ec']['name'] in tc_names:
                    # drop elements in toolchain hierarchy
                    del orig_ecs[i]
                else:
                    i += 1
    else:
        revert_to_regex = True

    if revert_to_regex:
        # no recursion if software name/version build specification are included or we are amending something
        # in that case, do not construct full dependency graph
        orig_ecs = easyconfigs
        _log.debug(
            "Software name/version found, so not applying build specifications recursively: %s"
            % build_specs)

    # keep track of originally listed easyconfigs (via their path)
    listed_ec_paths = [ec['spec'] for ec in easyconfigs]

    # generate tweaked easyconfigs, and continue with those instead
    tweaked_easyconfigs = []
    for orig_ec in orig_ecs:
        # Only return tweaked easyconfigs for easyconfigs which were listed originally on the command line
        # (and use the prepended path so that they are found first).
        # easyconfig files for dependencies are also generated but not included, they will be resolved via --robot
        # either from existing easyconfigs or, if that fails, from easyconfigs in the appended path

        tc_name = orig_ec['ec']['toolchain']['name']

        new_ec_file = None
        verification_build_specs = copy.copy(build_specs)
        if orig_ec['spec'] in listed_ec_paths:
            if modifying_toolchains:
                if tc_name in src_to_dst_tc_mapping:
                    new_ec_file = map_easyconfig_to_target_tc_hierarchy(
                        orig_ec['spec'], src_to_dst_tc_mapping,
                        tweaked_ecs_path)
                    # Need to update the toolchain in the build_specs to match the toolchain mapping
                    keys = verification_build_specs.keys()
                    if 'toolchain_name' in keys:
                        verification_build_specs[
                            'toolchain_name'] = src_to_dst_tc_mapping[tc_name][
                                'name']
                    if 'toolchain_version' in keys:
                        verification_build_specs[
                            'toolchain_version'] = src_to_dst_tc_mapping[
                                tc_name]['version']
                    if 'toolchain' in keys:
                        verification_build_specs[
                            'toolchain'] = src_to_dst_tc_mapping[tc_name]
            else:
                new_ec_file = tweak_one(orig_ec['spec'],
                                        None,
                                        build_specs,
                                        targetdir=tweaked_ecs_path)

            if new_ec_file:
                new_ecs = process_easyconfig(
                    new_ec_file, build_specs=verification_build_specs)
                tweaked_easyconfigs.extend(new_ecs)
        else:
            # Place all tweaked dependency easyconfigs in the directory appended to the robot path
            if modifying_toolchains:
                if tc_name in src_to_dst_tc_mapping:
                    new_ec_file = map_easyconfig_to_target_tc_hierarchy(
                        orig_ec['spec'],
                        src_to_dst_tc_mapping,
                        targetdir=tweaked_ecs_deps_path)
            else:
                new_ec_file = tweak_one(orig_ec['spec'],
                                        None,
                                        build_specs,
                                        targetdir=tweaked_ecs_deps_path)

    return tweaked_easyconfigs
示例#11
0
def tweak(easyconfigs, build_specs, modtool, targetdirs=None):
    """Tweak list of easyconfigs according to provided build specifications."""
    tweaked_ecs_path, tweaked_ecs_deps_path = None, None
    if targetdirs is not None:
        tweaked_ecs_path, tweaked_ecs_deps_path = targetdirs
    # make sure easyconfigs all feature the same toolchain (otherwise we *will* run into trouble)
    toolchains = nub(['%(name)s/%(version)s' % ec['ec']['toolchain'] for ec in easyconfigs])
    if len(toolchains) > 1:
        raise EasyBuildError("Multiple toolchains featured in easyconfigs, --try-X not supported in that case: %s",
                             toolchains)
    # Toolchain is unique, let's store it
    source_toolchain = easyconfigs[-1]['ec']['toolchain']
    modifying_toolchains = False
    target_toolchain = {}
    src_to_dst_tc_mapping = {}
    revert_to_regex = False

    if 'toolchain_name' in build_specs or 'toolchain_version' in build_specs:
        keys = build_specs.keys()

        # Make sure there are no more build_specs, as combining --try-toolchain* with other options is currently not
        # supported
        if any(key not in ['toolchain_name', 'toolchain_version', 'toolchain'] for key in keys):
            warning_msg = "Combining --try-toolchain* with other build options is not fully supported: using regex"
            print_warning(warning_msg, silent=build_option('silent'))
            revert_to_regex = True

        if not revert_to_regex:
            # we're doing something with the toolchain,
            # so build specifications should be applied to whole dependency graph;
            # obtain full dependency graph for specified easyconfigs;
            # easyconfigs will be ordered 'top-to-bottom' (toolchains and dependencies appearing first)
            modifying_toolchains = True

            if 'toolchain_name' in keys:
                target_toolchain['name'] = build_specs['toolchain_name']
            else:
                target_toolchain['name'] = source_toolchain['name']

            if 'toolchain_version' in keys:
                target_toolchain['version'] = build_specs['toolchain_version']
            else:
                target_toolchain['version'] = source_toolchain['version']

            if build_option('map_toolchains'):
                try:
                    src_to_dst_tc_mapping = map_toolchain_hierarchies(source_toolchain, target_toolchain, modtool)
                except EasyBuildError as err:
                    # make sure exception was raised by match_minimum_tc_specs because toolchain mapping didn't work
                    if "No possible mapping from source toolchain" in err.msg:
                        error_msg = err.msg + '\n'
                        error_msg += "Toolchain %s is not equivalent to toolchain %s in terms of capabilities. "
                        error_msg += "(If you know what you are doing, "
                        error_msg += "you can use --disable-map-toolchains to proceed anyway.)"
                        raise EasyBuildError(error_msg, target_toolchain['name'], source_toolchain['name'])
                    else:
                        # simply re-raise the exception if something else went wrong
                        raise err
            else:
                msg = "Mapping of (sub)toolchains disabled, so falling back to regex mode, "
                msg += "disabling recursion and not changing (sub)toolchains for dependencies"
                _log.info(msg)
                revert_to_regex = True
                modifying_toolchains = False

        if not revert_to_regex:
            _log.debug("Applying build specifications recursively (no software name/version found): %s", build_specs)
            orig_ecs = resolve_dependencies(easyconfigs, modtool, retain_all_deps=True)

            # Filter out the toolchain hierarchy (which would only appear if we are applying build_specs recursively)
            # We can leave any dependencies they may have as they will only be used if required (or originally listed)
            _log.debug("Filtering out toolchain hierarchy for %s", source_toolchain)

            i = 0
            while i < len(orig_ecs):
                tc_names = [tc['name'] for tc in get_toolchain_hierarchy(source_toolchain)]
                if orig_ecs[i]['ec']['name'] in tc_names:
                    # drop elements in toolchain hierarchy
                    del orig_ecs[i]
                else:
                    i += 1
    else:
        revert_to_regex = True

    if revert_to_regex:
        # no recursion if software name/version build specification are included or we are amending something
        # in that case, do not construct full dependency graph
        orig_ecs = easyconfigs
        _log.debug("Software name/version found, so not applying build specifications recursively: %s" % build_specs)

    # keep track of originally listed easyconfigs (via their path)
    listed_ec_paths = [ec['spec'] for ec in easyconfigs]

    # generate tweaked easyconfigs, and continue with those instead
    tweaked_easyconfigs = []
    for orig_ec in orig_ecs:
        # Only return tweaked easyconfigs for easyconfigs which were listed originally on the command line
        # (and use the prepended path so that they are found first).
        # easyconfig files for dependencies are also generated but not included, they will be resolved via --robot
        # either from existing easyconfigs or, if that fails, from easyconfigs in the appended path

        tc_name = orig_ec['ec']['toolchain']['name']

        new_ec_file = None
        verification_build_specs = copy.copy(build_specs)
        if orig_ec['spec'] in listed_ec_paths:
            if modifying_toolchains:
                if tc_name in src_to_dst_tc_mapping:
                    new_ec_file = map_easyconfig_to_target_tc_hierarchy(orig_ec['spec'], src_to_dst_tc_mapping,
                                                                        tweaked_ecs_path)
                    # Need to update the toolchain in the build_specs to match the toolchain mapping
                    keys = verification_build_specs.keys()
                    if 'toolchain_name' in keys:
                        verification_build_specs['toolchain_name'] = src_to_dst_tc_mapping[tc_name]['name']
                    if 'toolchain_version' in keys:
                        verification_build_specs['toolchain_version'] = src_to_dst_tc_mapping[tc_name]['version']
                    if 'toolchain' in keys:
                        verification_build_specs['toolchain'] = src_to_dst_tc_mapping[tc_name]
            else:
                new_ec_file = tweak_one(orig_ec['spec'], None, build_specs, targetdir=tweaked_ecs_path)

            if new_ec_file:
                new_ecs = process_easyconfig(new_ec_file, build_specs=verification_build_specs)
                tweaked_easyconfigs.extend(new_ecs)
        else:
            # Place all tweaked dependency easyconfigs in the directory appended to the robot path
            if modifying_toolchains:
                if tc_name in src_to_dst_tc_mapping:
                    new_ec_file = map_easyconfig_to_target_tc_hierarchy(orig_ec['spec'], src_to_dst_tc_mapping,
                                                                        targetdir=tweaked_ecs_deps_path)
            else:
                new_ec_file = tweak_one(orig_ec['spec'], None, build_specs, targetdir=tweaked_ecs_deps_path)

    return tweaked_easyconfigs