diff options
author | John W. Parent <45471568+johnwparent@users.noreply.github.com> | 2024-04-18 17:53:56 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-18 21:53:56 +0000 |
commit | 6fba31ce3427a6e3985c37543c0f946d1c7c01e4 (patch) | |
tree | 7f2a45aabecd517f605446080a314fd6dc9434de | |
parent | 112cead00b5cadbf20dec3dbc2cb20651427883b (diff) | |
download | spack-6fba31ce3427a6e3985c37543c0f946d1c7c01e4.tar.gz spack-6fba31ce3427a6e3985c37543c0f946d1c7c01e4.tar.bz2 spack-6fba31ce3427a6e3985c37543c0f946d1c7c01e4.tar.xz spack-6fba31ce3427a6e3985c37543c0f946d1c7c01e4.zip |
Windows: Update MSVC + oneAPI detection and integration (#43646)
* Later versions of oneAPI have moved, so update detection to find it
in both old and new location
* Remove reliance on ONEAPI_ROOT env variable when determining Fortran
compiler version for %msvc
* When finding a Fortran compiler for MSVC, there was logic enforcing
a maximum MSVC version for a given oneAPI Fortran version. This
mapping was out of date and excluding valid combinations, so has
been removed (the logic now just picks the latest available
oneAPI Fortran compiler for any given MSVC version).
-rw-r--r-- | lib/spack/spack/compilers/msvc.py | 87 | ||||
-rwxr-xr-x | lib/spack/spack/operating_systems/windows_os.py | 15 |
2 files changed, 58 insertions, 44 deletions
diff --git a/lib/spack/spack/compilers/msvc.py b/lib/spack/spack/compilers/msvc.py index d34ef83c3e..9914e5242a 100644 --- a/lib/spack/spack/compilers/msvc.py +++ b/lib/spack/spack/compilers/msvc.py @@ -8,7 +8,7 @@ import re import subprocess import sys import tempfile -from typing import Dict, List, Set +from typing import Dict, List import archspec.cpu @@ -20,15 +20,7 @@ from spack.compiler import Compiler from spack.error import SpackError from spack.version import Version, VersionRange -avail_fc_version: Set[str] = set() -fc_path: Dict[str, str] = dict() - -fortran_mapping = { - "2021.3.0": "19.29.30133", - "2021.2.1": "19.28.29913", - "2021.2.0": "19.28.29334", - "2021.1.0": "19.28.29333", -} +FC_PATH: Dict[str, str] = dict() class CmdCall: @@ -115,15 +107,13 @@ class VCVarsInvocation(VarsInvocation): return f"{script} {self.arch} {self.sdk_ver} {self.vcvars_ver}" -def get_valid_fortran_pth(comp_ver): - cl_ver = str(comp_ver) +def get_valid_fortran_pth(): + """Assign maximum available fortran compiler version""" + # TODO (johnwparent): validate compatibility w/ try compiler + # functionality when added sort_fn = lambda fc_ver: Version(fc_ver) - sort_fc_ver = sorted(list(avail_fc_version), key=sort_fn) - for ver in sort_fc_ver: - if ver in fortran_mapping: - if Version(cl_ver) <= Version(fortran_mapping[ver]): - return fc_path[ver] - return None + sort_fc_ver = sorted(list(FC_PATH.keys()), key=sort_fn) + return FC_PATH[sort_fc_ver[-1]] if sort_fc_ver else None class Msvc(Compiler): @@ -167,11 +157,9 @@ class Msvc(Compiler): # This positional argument "paths" is later parsed and process by the base class # via the call to `super` later in this method paths = args[3] - # This positional argument "cspec" is also parsed and handled by the base class - # constructor - cspec = args[0] - new_pth = [pth if pth else get_valid_fortran_pth(cspec.version) for pth in paths] - paths[:] = new_pth + latest_fc = get_valid_fortran_pth() + new_pth = [pth if pth else latest_fc for pth in paths[2:]] + paths[2:] = new_pth # Initialize, deferring to base class but then adding the vcvarsallfile # file based on compiler executable path. super().__init__(*args, **kwargs) @@ -183,7 +171,7 @@ class Msvc(Compiler): # and stores their path, but their respective VCVARS # file must be invoked before useage. env_cmds = [] - compiler_root = os.path.join(self.cc, "../../../../../../..") + compiler_root = os.path.join(os.path.dirname(self.cc), "../../../../../..") vcvars_script_path = os.path.join(compiler_root, "Auxiliary", "Build", "vcvars64.bat") # get current platform architecture and format for vcvars argument arch = spack.platforms.real_host().default.lower() @@ -198,11 +186,34 @@ class Msvc(Compiler): # paths[2] refers to the fc path and is a generic check # for a fortran compiler if paths[2]: + + def get_oneapi_root(pth: str): + """From within a prefix known to be a oneAPI path + determine the oneAPI root path from arbitrary point + under root + + Args: + pth: path prefixed within oneAPI root + """ + if not pth: + return "" + while os.path.basename(pth) and os.path.basename(pth) != "oneAPI": + pth = os.path.dirname(pth) + return pth + # If this found, it sets all the vars - oneapi_root = os.path.join(self.cc, "../../..") + oneapi_root = get_oneapi_root(self.fc) + if not oneapi_root: + raise RuntimeError(f"Non-oneAPI Fortran compiler {self.fc} assigned to MSVC") oneapi_root_setvars = os.path.join(oneapi_root, "setvars.bat") + # some oneAPI exes return a version more precise than their + # install paths specify, so we determine path from + # the install path rather than the fc executable itself + numver = r"\d+\.\d+(?:\.\d+)?" + pattern = f"((?:{numver})|(?:latest))" + version_from_path = re.search(pattern, self.fc).group(1) oneapi_version_setvars = os.path.join( - oneapi_root, "compiler", str(self.ifx_version), "env", "vars.bat" + oneapi_root, "compiler", version_from_path, "env", "vars.bat" ) # order matters here, the specific version env must be invoked first, # otherwise it will be ignored if the root setvars sets up the oneapi @@ -314,23 +325,19 @@ class Msvc(Compiler): @classmethod def fc_version(cls, fc): - # We're using intel for the Fortran compilers, which exist if - # ONEAPI_ROOT is a meaningful variable if not sys.platform == "win32": return "unknown" fc_ver = cls.default_version(fc) - avail_fc_version.add(fc_ver) - fc_path[fc_ver] = fc - if os.getenv("ONEAPI_ROOT"): - try: - sps = spack.operating_systems.windows_os.WindowsOs().compiler_search_paths - except AttributeError: - raise SpackError("Windows compiler search paths not established") - clp = spack.util.executable.which_string("cl", path=sps) - ver = cls.default_version(clp) - else: - ver = fc_ver - return ver + FC_PATH[fc_ver] = fc + try: + sps = spack.operating_systems.windows_os.WindowsOs().compiler_search_paths + except AttributeError: + raise SpackError( + "Windows compiler search paths not established, " + "please report this behavior to github.com/spack/spack" + ) + clp = spack.util.executable.which_string("cl", path=sps) + return cls.default_version(clp) if clp else fc_ver @classmethod def f77_version(cls, f77): diff --git a/lib/spack/spack/operating_systems/windows_os.py b/lib/spack/spack/operating_systems/windows_os.py index 6ce25bb8de..3d3d44175f 100755 --- a/lib/spack/spack/operating_systems/windows_os.py +++ b/lib/spack/spack/operating_systems/windows_os.py @@ -74,16 +74,23 @@ class WindowsOs(OperatingSystem): return [os.path.join(path, "VC", "Tools", "MSVC") for path in self.vs_install_paths] @property + def oneapi_root(self): + root = os.environ.get("ONEAPI_ROOT", "") or os.path.join( + os.environ.get("ProgramFiles(x86)", ""), "Intel", "oneAPI" + ) + if os.path.exists(root): + return root + + @property def compiler_search_paths(self): # First Strategy: Find MSVC directories using vswhere _compiler_search_paths = [] for p in self.msvc_paths: _compiler_search_paths.extend(glob.glob(os.path.join(p, "*", "bin", "Hostx64", "x64"))) - if os.getenv("ONEAPI_ROOT"): + oneapi_root = self.oneapi_root + if oneapi_root: _compiler_search_paths.extend( - glob.glob( - os.path.join(str(os.getenv("ONEAPI_ROOT")), "compiler", "*", "windows", "bin") - ) + glob.glob(os.path.join(oneapi_root, "compiler", "**", "bin"), recursive=True) ) # Second strategy: Find MSVC via the registry |