# Copyright 2013-2022 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 os
import re
import subprocess
import sys
from distutils.version import StrictVersion
from typing import Dict, List, Set # novm
import spack.operating_systems.windows_os
import spack.util.executable
from spack.compiler import Compiler
from spack.error import SpackError
avail_fc_version = set() # type: Set[str]
fc_path = dict() # type: Dict[str, str]
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',
}
[docs]def get_valid_fortran_pth(comp_ver):
cl_ver = str(comp_ver).split('@')[1]
sort_fn = lambda fc_ver: StrictVersion(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 StrictVersion(cl_ver) <= StrictVersion(fortran_mapping[ver]):
return fc_path[ver]
return None
[docs]class Msvc(Compiler):
# Subclasses use possible names of C compiler
cc_names = ['cl.exe']
# Subclasses use possible names of C++ compiler
cxx_names = ['cl.exe']
# Subclasses use possible names of Fortran 77 compiler
f77_names = ['ifx.exe'] # type: List[str]
# Subclasses use possible names of Fortran 90 compiler
fc_names = ['ifx.exe'] # type: List[str]
# Named wrapper links within build_env_path
# Due to the challenges of supporting compiler wrappers
# in Windows, we leave these blank, and dynamically compute
# based on proper versions of MSVC from there
# pending acceptance of #28117 for full support using
# compiler wrappers
link_paths = {'cc': '',
'cxx': '',
'f77': '',
'fc': ''}
#: Compiler argument that produces version information
version_argument = ''
# For getting ifx's version, call it with version_argument
# and ignore the error code
ignore_version_errors = [1]
#: Regex used to extract version from compiler's output
version_regex = r'([1-9][0-9]*\.[0-9]*\.[0-9]*)'
# Initialize, deferring to base class but then adding the vcvarsallfile
# file based on compiler executable path.
def __init__(self, *args, **kwargs):
new_pth = [pth if pth else get_valid_fortran_pth(args[0]) for pth in args[3]]
args[3][:] = new_pth
super(Msvc, self).__init__(*args, **kwargs)
if os.getenv("ONEAPI_ROOT"):
# If this found, it sets all the vars
self.setvarsfile = os.path.join(
os.getenv("ONEAPI_ROOT"), "setvars.bat")
else:
# To use the MSVC compilers, VCVARS must be invoked
# VCVARS is located at a fixed location, referencable
# idiomatically by the following relative path from the
# compiler.
# Spack first finds the compilers via VSWHERE
# and stores their path, but their respective VCVARS
# file must be invoked before useage.
self.setvarsfile = os.path.abspath(
os.path.join(self.cc, '../../../../../../..'))
self.setvarsfile = os.path.join(
self.setvarsfile, 'Auxiliary', 'Build', 'vcvars64.bat')
@property
def msvc_version(self):
ver = re.search(Msvc.version_regex, self.cc).group(1)
ver = "".join(ver.split('.')[:2])[:-1]
return "MSVC" + ver
[docs] def setup_custom_environment(self, pkg, env):
"""Set environment variables for MSVC using the
Microsoft-provided script."""
if sys.version_info[:2] > (2, 6):
# Set the build environment variables for spack. Just using
# subprocess.call() doesn't work since that operates in its own
# environment which is destroyed (along with the adjusted variables)
# once the process terminates. So go the long way around: examine
# output, sort into dictionary, use that to make the build
# environment.
out = subprocess.check_output( # novermin
'cmd /u /c "{}" {} && set'.format(self.setvarsfile, 'amd64'),
stderr=subprocess.STDOUT)
if sys.version_info[0] >= 3:
out = out.decode('utf-16le', errors='replace') # novermin
int_env = dict((key.lower(), value) for key, _, value in
(line.partition('=') for line in out.splitlines())
if key and value)
if 'path' in int_env:
env.set_path('PATH', int_env['path'].split(';'))
env.set_path('INCLUDE', int_env.get('include', '').split(';'))
env.set_path('LIB', int_env.get('lib', '').split(';'))
env.set('CC', self.cc)
env.set('CXX', self.cxx)
env.set('FC', self.fc)
env.set('F77', self.f77)
else:
# Should not this be an exception?
print("Cannot pull msvc compiler information in Python 2.6 or below")
[docs] @classmethod
def fc_version(cls, fc):
# We're using intel for the Fortran compilers, which exist if
# ONEAPI_ROOT is a meaningful variable
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
[docs] @classmethod
def f77_version(cls, f77):
return cls.fc_version(f77)