Improve WINEPATH reduction

- Remove duplicated code in mdevenv.py
- Change the limit to 1024 instead of 2048 which is what has been
  tested.
- Skip shortening if it is already short enough.
- Skip shortening with wine >= 6.4 which does not seems to have that
  limitation any more.
- Downgrade exception to warning in the case WINEPATH cannot be
  shortened under 1024 chars, it is possible that it will still work.
pull/10508/head
Xavier Claessens 3 years ago committed by Xavier Claessens
parent b1649899a2
commit 57909b53d6
  1. 44
      mesonbuild/mdevenv.py
  2. 91
      mesonbuild/mesonlib/universal.py
  3. 3
      mesonbuild/mtest.py
  4. 3
      mesonbuild/scripts/meson_exe.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]]:

@ -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]:

@ -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

@ -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

Loading…
Cancel
Save