def normalize_path()

in lib/ramble/spack/build_systems/intel.py [0:0]


    def normalize_path(self, component_path, component_suite_dir=None,
                       relative=False):
        '''Returns the absolute or relative path to a component or file under a
        component suite directory.

        Intel's product names, scope, and directory layout changed over the
        years.  This function provides a unified interface to their directory
        names.

        Parameters:

            component_path (str): a component name like 'mkl', or 'mpi', or a
                deeper relative path.

            component_suite_dir (str): _Unversioned_ name of the expected
                parent directory of component_path.  When absent or `None`, an
                appropriate default will be used.  A present but empty string
                `""` requests that `component_path` refer to `self.prefix`
                directly.

                Typical values: `compilers_and_libraries`, `composer_xe`,
                `parallel_studio_xe`.

                Also supported: `advisor`, `inspector`, `vtune`. The actual
                directory name for these suites varies by release year. The
                name will be corrected as needed for use in the return value.

            relative (bool): When True, return path relative to self.prefix,
                otherwise, return an absolute path (the default).
        '''
        # Design note: Choosing the default for `component_suite_dir` was a bit
        # tricky since there better be a sensible means to specify direct
        # parentage under self.prefix (even though you normally shouldn't need
        # a function for that).  I chose "" to allow that case be represented,
        # and 'None' or the absence of the kwarg to represent the most relevant
        # case for the time of writing.
        #
        # In the 2015 releases (the earliest in Spack as of 2018), there were
        # nominally two separate products that provided the compilers:
        # "Composer" as lower tier, and "Parallel Studio" as upper tier. In
        # Spack, we justifiably retcon both as "intel-parallel-studio@composer"
        # and "...@cluster", respectively.  Both of these use the older
        # "composer_xe" dir layout, as do their virtual package personas.
        #
        # All other "intel-foo" packages in Spack as of 2018-04 use the
        # "compilers_and_libraries" layout, including the 2016 releases that
        # are not natively versioned by year.

        cs = component_suite_dir
        if cs is None and component_path.startswith('ism'):
            cs = 'parallel_studio_xe'

        v = self.version_yearlike

        # Glob variants to complete component_suite_dir.
        # Helper var for older MPI versions - those are reparented, with each
        # version in their own version-named dir.
        standalone_glob = '[1-9]*.*.*'

        # Most other components; try most specific glob first.
        # flake8 is far too opinionated about lists - ugh.
        normalize_kwargs = {
            'version_globs': [
                '_%s' % self.version,
                '_%s.*' % v.up_to(2),   # should be: YYYY.Nupdate
                '_*.*.*',               # last resort
            ]
        }
        for rename_rule in [
            # cs given as arg, in years, dir actually used, [version_globs]
            [None,              ':2015', 'composer_xe'],
            [None,              '2016:', 'compilers_and_libraries'],
            ['advisor',         ':2016', 'advisor_xe'],
            ['inspector',       ':2016', 'inspector_xe'],
            ['vtune_profiler',  ':2017', 'vtune_amplifier_xe'],
            ['vtune',           ':2017', 'vtune_amplifier_xe'],  # alt.
            ['vtune_profiler',  ':2019', 'vtune_amplifier'],
            ['itac',            ':',     'itac',  [os.sep + standalone_glob]],
        ]:
            if cs == rename_rule[0] and v.satisfies(ver(rename_rule[1])):
                cs = rename_rule[2]
                if len(rename_rule) > 3:
                    normalize_kwargs = {'version_globs': rename_rule[3]}
                break

        d = self.normalize_suite_dir(cs, **normalize_kwargs)

        # Help find components not located directly under d.
        # NB: ancestor() not well suited if version_globs may contain os.sep .
        parent_dir = re.sub(os.sep + re.escape(cs) + '.*', '', d)

        reparent_as = {}
        if cs == 'compilers_and_libraries':     # must qualify further
            d = os.path.join(d, _expand_fields('{platform}'))
        elif cs == 'composer_xe':
            reparent_as = {'mpi': 'impi'}
            # ignore 'imb' (MPI Benchmarks)

        for nominal_p, actual_p in reparent_as.items():
            if component_path.startswith(nominal_p):
                dirs = glob.glob(
                    os.path.join(parent_dir, actual_p, standalone_glob))
                debug_print('reparent dirs: %s' % dirs)
                # Brazenly assume last match is the most recent version;
                # convert back to relative of parent_dir, and re-assemble.
                rel_dir = dirs[-1].split(parent_dir + os.sep, 1)[-1]
                component_path = component_path.replace(nominal_p, rel_dir, 1)
                d = parent_dir

        d = os.path.join(d, component_path)

        if relative:
            d = os.path.relpath(os.path.realpath(d), parent_dir)

        debug_print(d)
        return d