diff options
-rw-r--r-- | mesonbuild/mdevenv.py | 44 | ||||
-rw-r--r-- | mesonbuild/mesonlib/universal.py | 91 | ||||
-rw-r--r-- | mesonbuild/mtest.py | 3 | ||||
-rw-r--r-- | mesonbuild/scripts/meson_exe.py | 3 |
4 files changed, 69 insertions, 72 deletions
diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 46b301c..d0e84ff 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -6,7 +6,7 @@ import itertools from pathlib import Path from . import build, minstall, dependencies -from .mesonlib import MesonException, RealPathAction, is_windows, setup_vsenv, OptionKey, quote_arg +from .mesonlib import MesonException, RealPathAction, is_windows, setup_vsenv, OptionKey, quote_arg, get_wine_shortpath from . import mlog import typing as T @@ -28,43 +28,6 @@ def get_windows_shell() -> str: result = subprocess.check_output(command) return result.decode().strip() -def get_wine_shortpath(build_dir: str, winecmd: str, wine_paths: T.List[str]) -> T.List[str]: - ''' - WINEPATH size is limited to 1024 bytes which can easily be exceeded when - adding the path to every dll inside build directory. See - https://bugs.winehq.org/show_bug.cgi?id=45810. - - To shorthen it as much as possible we use path relative to builddir - where possible and convert absolute paths to Windows shortpath (e.g. - "/usr/x86_64-w64-mingw32/lib" to "Z:\\usr\\X86_~FWL\\lib"). - ''' - rel_paths = [] - abs_paths = [] - builddir = Path(build_dir) - for p in wine_paths: - try: - rel = Path(p).relative_to(builddir) - rel_paths.append(str(rel)) - except ValueError: - abs_paths.append(p) - if not abs_paths: - return rel_paths - with tempfile.NamedTemporaryFile('w', suffix='.bat', encoding='utf-8', delete=False) as bat_file: - bat_file.write(''' - @ECHO OFF - for %%x in (%*) do ( - echo|set /p=;%~sx - ) - ''') - try: - stdout = subprocess.check_output([winecmd, 'cmd', '/C', bat_file.name] + abs_paths, - encoding='utf-8', stderr=subprocess.DEVNULL) - return rel_paths + [p for p in set(stdout.split(';')) if p] - except subprocess.CalledProcessError: - return rel_paths + abs_paths - finally: - os.unlink(bat_file.name) - def reduce_winepath(build_dir: str, env: T.Dict[str, str]) -> None: winepath = env.get('WINEPATH') if not winepath: @@ -72,10 +35,7 @@ def reduce_winepath(build_dir: str, env: T.Dict[str, str]) -> None: winecmd = shutil.which('wine64') or shutil.which('wine') if not winecmd: return - winepath = ';'.join(get_wine_shortpath(build_dir, winecmd, winepath.split(';'))) - if len(winepath) > 1024: - mlog.warning(f'WINEPATH exceeds 1024 characters which could cause issues:\n{winepath}') - env['WINEPATH'] = winepath + env['WINEPATH'] = get_wine_shortpath([winecmd], winepath.split(';'), build_dir) mlog.log('Meson detected wine and has set WINEPATH accordingly') def get_env(b: build.Build, build_dir: str) -> T.Tuple[T.Dict[str, str], T.Set[str]]: diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 71f206a..ebcd9f4 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -26,9 +26,8 @@ import platform, subprocess, operator, os, shlex, shutil, re import collections from functools import lru_cache, wraps, total_ordering from itertools import tee, filterfalse -from tempfile import TemporaryDirectory +from tempfile import TemporaryDirectory, NamedTemporaryFile import typing as T -import uuid import textwrap import copy import pickle @@ -1888,35 +1887,71 @@ class RealPathAction(argparse.Action): setattr(namespace, self.dest, os.path.abspath(os.path.realpath(values))) -def get_wine_shortpath(winecmd: T.List[str], wine_paths: T.Sequence[str]) -> str: - """Get A short version of @wine_paths to avoid reaching WINEPATH number - of char limit. - """ +def get_wine_shortpath(winecmd: T.List[str], wine_paths: T.Sequence[str], + workdir: T.Optional[str] = None) -> str: + ''' + WINEPATH size is limited to 1024 bytes which can easily be exceeded when + adding the path to every dll inside build directory. See + https://bugs.winehq.org/show_bug.cgi?id=45810. - wine_paths = list(OrderedSet(wine_paths)) + To shorten it as much as possible we use path relative to `workdir` + where possible and convert absolute paths to Windows shortpath (e.g. + "/usr/x86_64-w64-mingw32/lib" to "Z:\\usr\\X86_~FWL\\lib"). - getShortPathScript = '%s.bat' % str(uuid.uuid4()).lower()[:5] - with open(getShortPathScript, mode='w', encoding='utf-8') as f: - f.write("@ECHO OFF\nfor %%x in (%*) do (\n echo|set /p=;%~sx\n)\n") - f.flush() - try: - with open(os.devnull, 'w', encoding='utf-8') as stderr: - wine_path = subprocess.check_output( - winecmd + - ['cmd', '/C', getShortPathScript] + wine_paths, - stderr=stderr).decode('utf-8') - except subprocess.CalledProcessError as e: - print("Could not get short paths: %s" % e) - wine_path = ';'.join(wine_paths) - finally: - os.remove(getShortPathScript) - if len(wine_path) > 2048: - raise MesonException( - 'WINEPATH size {} > 2048' - ' this will cause random failure.'.format( - len(wine_path))) + This limitation reportedly has been fixed with wine >= 6.4 + ''' - return wine_path.strip(';') + # Remove duplicates + wine_paths = list(OrderedSet(wine_paths)) + + # Check if it's already short enough + wine_path = ';'.join(wine_paths) + if len(wine_path) <= 1024: + return wine_path + + # Check if we have wine >= 6.4 + from ..programs import ExternalProgram + wine = ExternalProgram('wine', winecmd, silent=True) + if version_compare(wine.get_version(), '>=6.4'): + return wine_path + + # Check paths that can be reduced by making them relative to workdir. + rel_paths = [] + if workdir: + abs_paths = [] + for p in wine_paths: + try: + rel = Path(p).relative_to(workdir) + rel_paths.append(str(rel)) + except ValueError: + abs_paths.append(p) + wine_paths = abs_paths + + if wine_paths: + # BAT script that takes a list of paths in argv and prints semi-colon separated shortpaths + with NamedTemporaryFile('w', suffix='.bat', encoding='utf-8', delete=False) as bat_file: + bat_file.write(''' + @ECHO OFF + for %%x in (%*) do ( + echo|set /p=;%~sx + ) + ''') + try: + stdout = subprocess.check_output(winecmd + ['cmd', '/C', bat_file.name] + wine_paths, + encoding='utf-8', stderr=subprocess.DEVNULL) + stdout = stdout.strip(';') + if stdout: + wine_paths = stdout.split(';') + else: + mlog.warning('Could not shorten WINEPATH: empty stdout') + except subprocess.CalledProcessError as e: + mlog.warning(f'Could not shorten WINEPATH: {str(e)}') + finally: + os.unlink(bat_file.name) + wine_path = ';'.join(rel_paths + wine_paths) + if len(wine_path) > 1024: + mlog.warning('WINEPATH exceeds 1024 characters which could cause issues') + return wine_path def run_once(func: T.Callable[..., _T]) -> T.Callable[..., _T]: diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index ea5b2da..d733ce8 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1338,7 +1338,8 @@ class SingleTestRunner: if os.path.basename(c).startswith('wine'): env['WINEPATH'] = get_wine_shortpath( winecmd, - ['Z:' + p for p in self.test.extra_paths] + env.get('WINEPATH', '').split(';') + ['Z:' + p for p in self.test.extra_paths] + env.get('WINEPATH', '').split(';'), + self.test.workdir ) break diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index 486c101..c8f2f3b 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -51,7 +51,8 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[T.Dict[str, str] if exe.exe_wrapper and mesonlib.substring_is_in_list('wine', exe.exe_wrapper.get_command()): child_env['WINEPATH'] = mesonlib.get_wine_shortpath( exe.exe_wrapper.get_command(), - ['Z:' + p for p in exe.extra_paths] + child_env.get('WINEPATH', '').split(';') + ['Z:' + p for p in exe.extra_paths] + child_env.get('WINEPATH', '').split(';'), + exe.workdir ) stdin = None |