The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
4.0 KiB
126 lines
4.0 KiB
from __future__ import annotations |
|
|
|
import os |
|
import subprocess |
|
import json |
|
import pathlib |
|
import shutil |
|
import tempfile |
|
import locale |
|
|
|
from .. import mlog |
|
from .core import MesonException |
|
from .universal import is_windows, windows_detect_native_arch |
|
|
|
|
|
__all__ = [ |
|
'setup_vsenv', |
|
] |
|
|
|
|
|
bat_template = '''@ECHO OFF |
|
|
|
call "{}" |
|
|
|
ECHO {} |
|
SET |
|
''' |
|
|
|
# If on Windows and VS is installed but not set up in the environment, |
|
# set it to be runnable. In this way Meson can be directly invoked |
|
# from any shell, VS Code etc. |
|
def _setup_vsenv(force: bool) -> bool: |
|
if not is_windows(): |
|
return False |
|
if os.environ.get('OSTYPE') == 'cygwin': |
|
return False |
|
if 'MESON_FORCE_VSENV_FOR_UNITTEST' not in os.environ: |
|
# VSINSTALL is set when running setvars from a Visual Studio installation |
|
# Tested with Visual Studio 2012 and 2017 |
|
if 'VSINSTALLDIR' in os.environ: |
|
return False |
|
# Check explicitly for cl when on Windows |
|
if shutil.which('cl.exe'): |
|
return False |
|
if not force: |
|
if shutil.which('cc'): |
|
return False |
|
if shutil.which('gcc'): |
|
return False |
|
if shutil.which('clang'): |
|
return False |
|
if shutil.which('clang-cl'): |
|
return False |
|
|
|
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") |
|
bat_locator_bin = pathlib.Path(root, 'Microsoft Visual Studio/Installer/vswhere.exe') |
|
if not bat_locator_bin.exists(): |
|
raise MesonException(f'Could not find {bat_locator_bin}') |
|
bat_json = subprocess.check_output( |
|
[ |
|
str(bat_locator_bin), |
|
'-latest', |
|
'-prerelease', |
|
'-requiresAny', |
|
'-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', |
|
'-requires', 'Microsoft.VisualStudio.Workload.WDExpress', |
|
'-products', '*', |
|
'-utf8', |
|
'-format', |
|
'json' |
|
] |
|
) |
|
bat_info = json.loads(bat_json) |
|
if not bat_info: |
|
# VS installer installed but not VS itself maybe? |
|
raise MesonException('Could not parse vswhere.exe output') |
|
bat_root = pathlib.Path(bat_info[0]['installationPath']) |
|
if windows_detect_native_arch() == 'arm64': |
|
bat_path = bat_root / 'VC/Auxiliary/Build/vcvarsarm64.bat' |
|
if not bat_path.exists(): |
|
bat_path = bat_root / 'VC/Auxiliary/Build/vcvarsx86_arm64.bat' |
|
else: |
|
bat_path = bat_root / 'VC/Auxiliary/Build/vcvars64.bat' |
|
# if VS is not found try VS Express |
|
if not bat_path.exists(): |
|
bat_path = bat_root / 'VC/Auxiliary/Build/vcvarsx86_amd64.bat' |
|
if not bat_path.exists(): |
|
raise MesonException(f'Could not find {bat_path}') |
|
|
|
mlog.log('Activating VS', bat_info[0]['catalog']['productDisplayVersion']) |
|
bat_separator = '---SPLIT---' |
|
bat_contents = bat_template.format(bat_path, bat_separator) |
|
bat_file = tempfile.NamedTemporaryFile('w', suffix='.bat', encoding='utf-8', delete=False) |
|
bat_file.write(bat_contents) |
|
bat_file.flush() |
|
bat_file.close() |
|
bat_output = subprocess.check_output(bat_file.name, universal_newlines=True, |
|
encoding=locale.getpreferredencoding(False)) |
|
os.unlink(bat_file.name) |
|
bat_lines = bat_output.split('\n') |
|
bat_separator_seen = False |
|
for bat_line in bat_lines: |
|
if bat_line == bat_separator: |
|
bat_separator_seen = True |
|
continue |
|
if not bat_separator_seen: |
|
continue |
|
if not bat_line: |
|
continue |
|
try: |
|
k, v = bat_line.split('=', 1) |
|
except ValueError: |
|
# there is no "=", ignore junk data |
|
pass |
|
else: |
|
os.environ[k] = v |
|
return True |
|
|
|
def setup_vsenv(force: bool = False) -> bool: |
|
try: |
|
return _setup_vsenv(force) |
|
except MesonException as e: |
|
if force: |
|
raise |
|
mlog.warning('Failed to activate VS environment:', str(e)) |
|
return False
|
|
|