MSVC Compiler Find and vcvars env integration (#22856)

Made the vcvars batch script location a member variable of the msvc compiler subclass, initialized from the compiler executable path.  Added a setup_custom_environment() method to the msvc subclass that sources the vcvars script, dumps the environment, and copies the relevant environment variables to the Spack environment.  Added class variables to the Windows OS and MSVC compiler subclasses to enable finding the compiler executables and determining their versions.
This commit is contained in:
Betsy McPhail 2021-10-22 16:24:48 -04:00 committed by Peter Scheibel
parent f587a9ce68
commit a7101db39d
3 changed files with 86 additions and 2 deletions

View file

@ -37,8 +37,12 @@ def _get_compiler_version_output(compiler_path, version_arg, ignore_errors=()):
version_arg (str): the argument used to extract version information
"""
compiler = spack.util.executable.Executable(compiler_path)
output = compiler(
version_arg, output=str, error=str, ignore_errors=ignore_errors)
if version_arg:
output = compiler(
version_arg, output=str, error=str, ignore_errors=ignore_errors)
else:
output = compiler(
output=str, error=str, ignore_errors=ignore_errors)
return output

View file

@ -3,7 +3,11 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import subprocess
import sys
from typing import List # novm
from spack.compiler import Compiler
@ -26,6 +30,22 @@ class Msvc(Compiler):
'f77': '',
'fc': ''}
#: Compiler argument that produces version information
version_argument = ''
#: 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):
super(Msvc, self).__init__(*args, **kwargs)
self.vcvarsallfile = os.path.abspath(
os.path.join(self.cc, '../../../../../../..'))
self.vcvarsallfile = os.path.join(
self.vcvarsallfile, 'Auxiliary', 'Build', 'vcvarsall.bat')
@property
def verbose_flag(self):
return ""
@ -33,3 +53,30 @@ def verbose_flag(self):
@property
def pic_flag(self):
return ""
def setup_custom_environment(self, pkg, env):
"""Set environment variables for MSVC using the Microsoft-provided
script."""
if sys.version_info[:2] > (2, 6):
# Capture output from batch script and DOS environment dump
out = subprocess.check_output( # novermin
'cmd /u /c "{0}" {1} && set'.format(self.vcvarsallfile, 'amd64'),
stderr=subprocess.STDOUT)
if sys.version_info[0] >= 3:
out = out.decode('utf-16le', errors='replace')
else:
print("Cannot pull msvc compiler information in Python 2.6 or below")
# Process in to nice Python dictionary
vc_env = { # novermin
key.lower(): value
for key, _, value in
(line.partition('=') for line in out.splitlines())
if key and value
}
# Request setting environment variables
if 'path' in vc_env:
env.set_path('PATH', vc_env['path'].split(';'))
env.set_path('INCLUDE', vc_env.get('include', '').split(';'))
env.set_path('LIB', vc_env.get('lib', '').split(';'))

View file

@ -3,6 +3,10 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import sys
import os
import subprocess
import glob
from spack.architecture import OperatingSystem
from spack.version import Version
@ -22,6 +26,35 @@ class WindowsOs(OperatingSystem):
using the major version operating system number, e.g. 10.
"""
# Find MSVC directories using vswhere
compSearchPaths = []
root = os.environ.get('ProgramFiles(x86)') or os.environ.get('ProgramFiles')
if root:
try:
extra_args = {}
if sys.version_info[:3] >= (3, 6, 0):
extra_args = {'encoding': 'mbcs', 'errors': 'strict'}
paths = subprocess.check_output([
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()
if (3, 0) <= sys.version_info[:2] <= (3, 5):
paths = paths.decode()
msvcPaths = paths.split('\n')
msvcPaths = [os.path.join(path, "VC", "Tools", "MSVC")
for path in msvcPaths]
for p in msvcPaths:
compSearchPaths.extend(
glob.glob(os.path.join(p, '*', 'bin', 'Hostx64', 'x64')))
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
pass
if compSearchPaths:
compiler_search_paths = compSearchPaths
def __init__(self):
super(WindowsOs, self).__init__('Windows10', '10')