Skip to content

Commit

Permalink
vsenv: improve how VS vars are parsed
Browse files Browse the repository at this point in the history
Use a command instead of creating a temporary file
for the .bat script and process only newly created variables
  • Loading branch information
ylatuya committed Sep 18, 2024
1 parent e96cecd commit 2a55d29
Showing 1 changed file with 33 additions and 45 deletions.
78 changes: 33 additions & 45 deletions mesonbuild/utils/vsenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import json
import pathlib
import shutil
import tempfile
import locale

from .. import mlog
from .core import MesonException
Expand All @@ -18,14 +16,6 @@
]


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.
Expand Down Expand Up @@ -88,43 +78,41 @@ def _setup_vsenv(force: bool) -> bool:
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
parts = bat_line.split('=', 1)
if len(parts) != 2:
continue
k, v = parts
# Some CI's include variables containing the issue description, like
# GitLab's CI_MERGE_REQUEST_DESCRIPTION. The description itself can
# have multiple lines and any kind of content.
# If one of these lines is '=========', it will leave k empty
# when it's split by '='
if k is None or v is None:
continue
try:
os.environ[k] = v
except ValueError:
# Ignore errors from junk data returning invalid environment variable names
pass

before_separator = '---BEFORE---'
after_separator = '---AFTER---'
# This will print to stdout the env variables set before the VS
# activation and after VS activation so that we can process only
# newly created environment variables. This is required to correctly parse
# environment variables taking into account that some variables
# can have multiple lines. (https://github.com/mesonbuild/meson/pull/13682)
cmd = f'set&& echo {before_separator}&&"{bat_path.absolute()}" && echo {after_separator}&& set'
process = subprocess.Popen(
f'cmd.exe /c "{cmd}"',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
)
stdout, stderr = process.communicate()
if process.returncode != 0:
raise RuntimeError(f"Script failed with error: {stderr.decode()}")
lines = stdout.decode().splitlines()

# Remove the output from the vcvars script
try:
lines_before = lines[:lines.index(before_separator)]
lines_after = lines[lines.index(after_separator) + 1:]
except ValueError:
raise MesonException('Could not find separators in environment variables output')

# Filter out duplicated lines to remove env variables that haven't changed
new_lines = set(lines_before) - set(lines_after)
for line in new_lines:
k, v = line.split('=', 1)
os.environ[k] = v
return True


def setup_vsenv(force: bool = False) -> bool:
try:
return _setup_vsenv(force)
Expand Down

0 comments on commit 2a55d29

Please sign in to comment.