summaryrefslogtreecommitdiff
path: root/lib/spack/spack/operating_systems/cray_frontend.py
blob: 090e99e4744657c5826f18db9b82c3af998db735 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import contextlib
import os
import re

import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty

from spack.util.environment import get_path
from spack.util.module_cmd import module

from .linux_distro import LinuxDistro


@contextlib.contextmanager
def unload_programming_environment():
    """Context manager that unloads Cray Programming Environments."""
    env_bu = None

    # We rely on the fact that the PrgEnv-* modules set the PE_ENV
    # environment variable.
    if "PE_ENV" in os.environ:
        # Copy environment variables to restore them after the compiler
        # detection. We expect that the only thing PrgEnv-* modules do is
        # the environment variables modifications.
        env_bu = os.environ.copy()

        # Get the name of the module from the environment variable.
        prg_env = "PrgEnv-" + os.environ["PE_ENV"].lower()

        # Unload the PrgEnv-* module. By doing this we intentionally
        # provoke errors when the Cray's compiler wrappers are executed
        # (Error: A PrgEnv-* modulefile must be loaded.) so they will not
        # be detected as valid compilers by the overridden method. We also
        # expect that the modules that add the actual compilers' binaries
        # into the PATH environment variable (i.e. the following modules:
        # 'intel', 'cce', 'gcc', etc.) will also be unloaded since they are
        # specified as prerequisites in the PrgEnv-* modulefiles.
        module("unload", prg_env)

    yield

    # Restore the environment.
    if env_bu is not None:
        os.environ.clear()
        os.environ.update(env_bu)


class CrayFrontend(LinuxDistro):
    """Represents OS that runs on login and service nodes of the Cray platform.
    It acts as a regular Linux without Cray-specific modules and compiler
    wrappers."""

    @property
    def compiler_search_paths(self):
        """Calls the default function but unloads Cray's programming
        environments first.

        This prevents from detecting Cray compiler wrappers and avoids
        possible false detections.
        """
        import spack.compilers

        with unload_programming_environment():
            search_paths = get_path("PATH")

        extract_path_re = re.compile(r"prepend-path[\s]*PATH[\s]*([/\w\.:-]*)")

        for compiler_cls in spack.compilers.all_compiler_types():
            # Check if the compiler class is supported on Cray
            prg_env = getattr(compiler_cls, "PrgEnv", None)
            compiler_module = getattr(compiler_cls, "PrgEnv_compiler", None)
            if not (prg_env and compiler_module):
                continue

            # It is supported, check which versions are available
            output = module("avail", compiler_cls.PrgEnv_compiler)
            version_regex = r"({0})/([\d\.]+[\d]-?[\w]*)".format(compiler_cls.PrgEnv_compiler)
            matches = re.findall(version_regex, output)
            versions = tuple(version for _, version in matches if "classic" not in version)

            # Now inspect the modules and add to paths
            msg = "[CRAY FE] Detected FE compiler [name={0}, versions={1}]"
            tty.debug(msg.format(compiler_module, versions))
            for v in versions:
                try:
                    current_module = compiler_module + "/" + v
                    out = module("show", current_module)
                    match = extract_path_re.search(out)
                    search_paths += match.group(1).split(":")
                except Exception as e:
                    msg = (
                        "[CRAY FE] An unexpected error occurred while "
                        "detecting FE compiler [compiler={0}, "
                        " version={1}, error={2}]"
                    )
                    tty.debug(msg.format(compiler_cls.name, v, str(e)))

        search_paths = list(llnl.util.lang.dedupe(search_paths))
        return fs.search_paths_for_executables(*search_paths)