Source code for spack.operating_systems.windows_os

# Copyright 2013-2024 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 glob
import os
import pathlib
import platform
import subprocess

from llnl.util import tty

from spack.error import SpackError
from spack.util import windows_registry as winreg
from spack.version import Version

from ._operating_system import OperatingSystem


[docs] def windows_version(): """Windows version as a Version object""" # include the build number as this provides important information # for low lever packages and components like the SDK and WDK # The build number is the version component that would otherwise # be the patch version in sematic versioning, i.e. z of x.y.z return Version(platform.version())
[docs] class WindowsOs(OperatingSystem): """This class represents the Windows operating system. This will be auto detected using the python platform.win32_ver() once we have a python setup that runs natively. The Windows platform will be represented using the major version operating system number, e.g. 10. """ def __init__(self): plat_ver = windows_version() if plat_ver < Version("10"): raise SpackError("Spack is not supported on Windows versions older than 10") super().__init__("windows{}".format(plat_ver), plat_ver) def __str__(self): return self.name @property def vs_install_paths(self): vs_install_paths = [] root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") if root: try: extra_args = {"encoding": "mbcs", "errors": "strict"} paths = subprocess.check_output( # type: ignore[call-overload] # novermin [ os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-prerelease", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath", "-products", "*", ], **extra_args, ).strip() vs_install_paths = paths.split("\n") except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): pass return vs_install_paths @property def msvc_paths(self): 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"))) oneapi_root = self.oneapi_root if oneapi_root: _compiler_search_paths.extend( glob.glob(os.path.join(oneapi_root, "compiler", "**", "bin"), recursive=True) ) # Second strategy: Find MSVC via the registry def try_query_registry(retry=False): winreg_report_error = lambda e: tty.debug( 'Windows registry query on "SOFTWARE\\WOW6432Node\\Microsoft"' f"under HKEY_LOCAL_MACHINE: {str(e)}" ) try: # Registry interactions are subject to race conditions, etc and can generally # be flakey, do this in a catch block to prevent reg issues from interfering # with compiler detection msft = winreg.WindowsRegistryView( "SOFTWARE\\WOW6432Node\\Microsoft", winreg.HKEY.HKEY_LOCAL_MACHINE ) return msft.find_subkeys(r"VisualStudio_.*", recursive=False) except OSError as e: # OSErrors propagated into caller by Spack's registry module are expected # and indicate a known issue with the registry query # i.e. user does not have permissions or the key/value # doesn't exist winreg_report_error(e) return [] except winreg.InvalidRegistryOperation as e: # Other errors raised by the Spack's reg module indicate # an unexpected error type, and are handled specifically # as the underlying cause is difficult/impossible to determine # without manually exploring the registry # These errors can also be spurious (race conditions) # and may resolve on re-execution of the query # or are permanent (specific types of permission issues) # but the registry raises the same exception for all types of # atypical errors if retry: winreg_report_error(e) return [] vs_entries = try_query_registry() if not vs_entries: # Occasional spurious race conditions can arise when reading the MS reg # typically these race conditions resolve immediately and we can safely # retry the reg query without waiting # Note: Winreg does not support locking vs_entries = try_query_registry(retry=True) vs_paths = [] def clean_vs_path(path): path = path.split(",")[0].lstrip("@") return str((pathlib.Path(path).parent / "..\\..").resolve()) for entry in vs_entries: try: val = entry.get_subkey("Capabilities").get_value("ApplicationDescription").value vs_paths.append(clean_vs_path(val)) except FileNotFoundError as e: if hasattr(e, "winerror") and e.winerror == 2: pass else: raise _compiler_search_paths.extend(vs_paths) return _compiler_search_paths