From 2a55d2913d402e3f7e97fe5f859ab31fa47cab9a Mon Sep 17 00:00:00 2001 From: Andoni Morales Alastruey Date: Wed, 18 Sep 2024 14:28:40 +0200 Subject: [PATCH] vsenv: improve how VS vars are parsed Use a command instead of creating a temporary file for the .bat script and process only newly created variables --- mesonbuild/utils/vsenv.py | 78 +++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/mesonbuild/utils/vsenv.py b/mesonbuild/utils/vsenv.py index 2764a78b5c8d..61fd15793dec 100644 --- a/mesonbuild/utils/vsenv.py +++ b/mesonbuild/utils/vsenv.py @@ -5,8 +5,6 @@ import json import pathlib import shutil -import tempfile -import locale from .. import mlog from .core import MesonException @@ -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. @@ -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)