mirror of
https://github.com/servo/servo.git
synced 2025-06-15 20:04:28 +00:00
build: Add support for Visual Studio 2022 and VC143 DLLs (#31148)
* build: Add support for Visual Studio 2022 and VC143 DLLs This change adds supports fot Visual Studio 2022 and the VC143 (current) version of the Visual Studio CRT. In addition, it reworks the way that Visual Studio is found, returning all installations in a generator, separately finding it via vswhere.exe, searching paths, and via environment variables. All of these installations are searched for the DLLs with highest priority given to the highest version of MS Visual Studio installed. The hope is that this makes the process more robust and properly handles having multiple versions installed, but only one with the correct runtime DLLs. * Update based on review comments Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com> --------- Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
45af1198aa
commit
dc2df7b027
5 changed files with 165 additions and 135 deletions
|
@ -12,80 +12,144 @@ import json
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List
|
||||
from typing import Generator, List, Optional
|
||||
|
||||
import servo.platform
|
||||
COMPATIBLE_MSVC_VERSIONS = {
|
||||
"2019": "16.0",
|
||||
"2022": "17.0",
|
||||
}
|
||||
MSVC_REDIST_VERSIONS = ["VC141", "VC142", "VC143", "VC150", "VC160"]
|
||||
|
||||
PROGRAM_FILES = os.environ.get("PROGRAMFILES", "C:\\Program Files")
|
||||
PROGRAM_FILES_X86 = os.environ.get("ProgramFiles(x86)", "C:\\Program Files (x86)")
|
||||
|
||||
|
||||
@dataclasses.dataclass(kw_only=True)
|
||||
@dataclasses.dataclass(frozen=True, kw_only=True)
|
||||
class VisualStudioInstallation:
|
||||
version_number: str
|
||||
installation_path: str
|
||||
vc_install_path: str
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.version_number < other.version_number
|
||||
|
||||
def find_highest_msvc_version_ext():
|
||||
|
||||
def find_vswhere():
|
||||
for path in [PROGRAM_FILES, PROGRAM_FILES_X86]:
|
||||
if not path:
|
||||
continue
|
||||
vswhere = os.path.join(path, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe')
|
||||
if os.path.exists(vswhere):
|
||||
return vswhere
|
||||
return None
|
||||
|
||||
|
||||
def find_compatible_msvc_with_vswhere() -> Generator[VisualStudioInstallation, None, None]:
|
||||
"""Try to find the MSVC installation with the `vswhere.exe` tool. The results
|
||||
are sorted with newer versions first."""
|
||||
def vswhere(args):
|
||||
program_files = (os.environ.get('PROGRAMFILES(X86)')
|
||||
or os.environ.get('PROGRAMFILES'))
|
||||
|
||||
vswhere = find_vswhere()
|
||||
if not vswhere:
|
||||
return
|
||||
|
||||
output = subprocess.check_output([
|
||||
vswhere,
|
||||
'-format', 'json',
|
||||
'-products', '*',
|
||||
'-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
|
||||
'-requires', 'Microsoft.VisualStudio.Component.Windows10SDK'
|
||||
]).decode(errors='ignore')
|
||||
|
||||
for install in json.loads(output):
|
||||
installed_version = f"{install['installationVersion'].split('.')[0]}.0"
|
||||
if installed_version not in COMPATIBLE_MSVC_VERSIONS.values():
|
||||
continue
|
||||
installation_path = install['installationPath']
|
||||
yield VisualStudioInstallation(
|
||||
version_number=installed_version,
|
||||
installation_path=installation_path,
|
||||
vc_install_path=os.path.join(installation_path, "VC")
|
||||
)
|
||||
|
||||
|
||||
def find_compatible_msvc_with_path() -> Generator[VisualStudioInstallation, None, None]:
|
||||
for program_files in [PROGRAM_FILES, PROGRAM_FILES_X86]:
|
||||
if not program_files:
|
||||
return []
|
||||
vswhere = os.path.join(program_files, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe')
|
||||
if not os.path.exists(vswhere):
|
||||
return []
|
||||
output = subprocess.check_output([vswhere, '-format', 'json'] + args).decode(errors='ignore')
|
||||
return json.loads(output)
|
||||
|
||||
for install in vswhere(['-products', '*',
|
||||
'-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
|
||||
'-requires', 'Microsoft.VisualStudio.Component.Windows10SDK']):
|
||||
version = install['installationVersion'].split('.')[0] + '.0'
|
||||
yield (install['installationPath'], version)
|
||||
continue
|
||||
for (version, version_number) in COMPATIBLE_MSVC_VERSIONS.items():
|
||||
for edition in ["Enterprise", "Professional", "Community", "BuildTools"]:
|
||||
installation_path = os.path.join(program_files, "Microsoft Visual Studio", version, edition)
|
||||
if os.path.exists(installation_path):
|
||||
yield VisualStudioInstallation(
|
||||
version_number=version_number,
|
||||
installation_path=installation_path,
|
||||
vc_install_path=os.path.join(installation_path, "VC")
|
||||
)
|
||||
|
||||
|
||||
def find_highest_msvc_version():
|
||||
prog_files = os.environ.get("ProgramFiles(x86)")
|
||||
|
||||
# TODO(mrobinson): Add support for Visual Studio 2022.
|
||||
vs_versions = {
|
||||
"2019": "16.0",
|
||||
}
|
||||
|
||||
for (version, version_number) in vs_versions.items():
|
||||
for edition in ["Enterprise", "Professional", "Community", "BuildTools"]:
|
||||
vsinstalldir = os.path.join(prog_files, "Microsoft Visual Studio", version, edition)
|
||||
if os.path.exists(vsinstalldir):
|
||||
return (vsinstalldir, version_number)
|
||||
|
||||
versions = sorted(find_highest_msvc_version_ext(), key=lambda tup: float(tup[1]))
|
||||
if not versions:
|
||||
print("Can't find a Visual Studio installation. "
|
||||
"Please set the VSINSTALLDIR and VisualStudioVersion environment variables")
|
||||
sys.exit(1)
|
||||
return versions[0]
|
||||
|
||||
|
||||
def find_msvc() -> VisualStudioInstallation:
|
||||
vsinstalldir = os.environ.get('VSINSTALLDIR')
|
||||
def find_compatible_msvc_with_environment_variables() -> Optional[VisualStudioInstallation]:
|
||||
installation_path = os.environ.get('VSINSTALLDIR')
|
||||
version_number = os.environ.get('VisualStudioVersion')
|
||||
if not vsinstalldir or not version_number:
|
||||
(vsinstalldir, version_number) = find_highest_msvc_version()
|
||||
|
||||
vc_install_path = os.environ.get("VCINSTALLDIR", os.path.join(vsinstalldir, "VC"))
|
||||
if not os.path.exists(vc_install_path):
|
||||
print(f"Can't find Visual C++ {version_number} installation at {vc_install_path}")
|
||||
sys.exit(1)
|
||||
|
||||
if not installation_path or not version_number:
|
||||
return None
|
||||
vc_install_path = os.environ.get("VCINSTALLDIR", os.path.join(installation_path, "VC"))
|
||||
if not os.path.exists(installation_path) or not os.path.exists(vc_install_path):
|
||||
return None
|
||||
return VisualStudioInstallation(
|
||||
version_number=version_number,
|
||||
installation_path=vsinstalldir,
|
||||
installation_path=installation_path,
|
||||
vc_install_path=vc_install_path,
|
||||
)
|
||||
|
||||
|
||||
def find_windows_sdk_installation_path(vs_platform: str) -> str:
|
||||
def find_msvc_installations() -> List[VisualStudioInstallation]:
|
||||
# First try to find Visual Studio via `vswhere.exe` and in well-known paths.
|
||||
installations = list(find_compatible_msvc_with_vswhere())
|
||||
installations.extend(find_compatible_msvc_with_path())
|
||||
if installations:
|
||||
return sorted(set(installations), reverse=True)
|
||||
|
||||
# Fall back to using the environment variables, which could theoretically
|
||||
# point to a version of Visual Studio that is unsupported.
|
||||
installation = find_compatible_msvc_with_environment_variables()
|
||||
if installation:
|
||||
return [installation]
|
||||
|
||||
raise Exception("Can't find a Visual Studio installation. "
|
||||
"Please set the VSINSTALLDIR and VisualStudioVersion environment variables")
|
||||
|
||||
|
||||
def find_msvc_redist_dirs(vs_platform: str) -> Generator[str, None, None]:
|
||||
installations = sorted(set(list(find_msvc_installations())), reverse=True)
|
||||
|
||||
tried = []
|
||||
for installation in installations:
|
||||
redist_dir = os.path.join(installation.vc_install_path, "Redist", "MSVC")
|
||||
if not os.path.isdir(redist_dir):
|
||||
tried.append(redist_dir)
|
||||
continue
|
||||
|
||||
for subdirectory in os.listdir(redist_dir)[::-1]:
|
||||
redist_path = os.path.join(redist_dir, subdirectory)
|
||||
for redist_version in MSVC_REDIST_VERSIONS:
|
||||
# there are two possible paths
|
||||
# `x64\Microsoft.VC*.CRT` or `onecore\x64\Microsoft.VC*.CRT`
|
||||
path1 = os.path.join(vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
||||
path2 = os.path.join("onecore", vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
||||
for path in [path1, path2]:
|
||||
path = os.path.join(redist_path, path)
|
||||
if os.path.isdir(path):
|
||||
yield path
|
||||
else:
|
||||
tried.append(path)
|
||||
|
||||
print("Couldn't locate MSVC redistributable directory. Tried:", file=sys.stderr)
|
||||
for path in tried:
|
||||
print(f" * {path}", file=sys.stderr)
|
||||
raise Exception("Can't find a MSVC redistributatable directory.")
|
||||
|
||||
|
||||
def find_windows_sdk_installation_path() -> str:
|
||||
"""Try to find the Windows SDK installation path using the Windows registry.
|
||||
Raises an Exception if the path cannot be found in the registry."""
|
||||
|
||||
|
@ -98,52 +162,6 @@ def find_windows_sdk_installation_path(vs_platform: str) -> str:
|
|||
key_path = r'SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0'
|
||||
try:
|
||||
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
|
||||
path = str(winreg.QueryValueEx(key, "InstallationFolder")[0])
|
||||
return os.path.join(path, "Redist", "ucrt", "DLLs", vs_platform)
|
||||
return str(winreg.QueryValueEx(key, "InstallationFolder")[0])
|
||||
except FileNotFoundError:
|
||||
raise Exception(f"Couldn't find Windows SDK installation path in registry at path ({key_path})")
|
||||
|
||||
|
||||
def find_msvc_redist_dirs(target: str) -> List[str]:
|
||||
assert 'windows' in servo.platform.host_triple()
|
||||
|
||||
installation = find_msvc()
|
||||
msvc_redist_dir = None
|
||||
vs_platforms = {
|
||||
"x86_64": "x64",
|
||||
"i686": "x86",
|
||||
"aarch64": "arm64",
|
||||
}
|
||||
target_arch = target.split('-')[0]
|
||||
vs_platform = vs_platforms[target_arch]
|
||||
|
||||
redist_dir = os.path.join(installation.vc_install_path, "Redist", "MSVC")
|
||||
if not os.path.isdir(redist_dir):
|
||||
raise Exception(f"Couldn't locate MSVC redistributable directory {redist_dir}")
|
||||
|
||||
for p in os.listdir(redist_dir)[::-1]:
|
||||
redist_path = os.path.join(redist_dir, p)
|
||||
for v in ["VC141", "VC142", "VC150", "VC160"]:
|
||||
# there are two possible paths
|
||||
# `x64\Microsoft.VC*.CRT` or `onecore\x64\Microsoft.VC*.CRT`
|
||||
redist1 = os.path.join(redist_path, vs_platform, "Microsoft.{}.CRT".format(v))
|
||||
redist2 = os.path.join(redist_path, "onecore", vs_platform, "Microsoft.{}.CRT".format(v))
|
||||
if os.path.isdir(redist1):
|
||||
msvc_redist_dir = redist1
|
||||
break
|
||||
elif os.path.isdir(redist2):
|
||||
msvc_redist_dir = redist2
|
||||
break
|
||||
if msvc_redist_dir:
|
||||
break
|
||||
|
||||
if not msvc_redist_dir:
|
||||
print("Couldn't locate MSVC redistributable directory")
|
||||
sys.exit(1)
|
||||
|
||||
redist_dirs = [
|
||||
msvc_redist_dir,
|
||||
find_windows_sdk_installation_path(vs_platform)
|
||||
]
|
||||
|
||||
return redist_dirs
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue