devenv: Set WINEPATH when cross compiling for Windows

pull/10358/head
Xavier Claessens 3 years ago committed by Jussi Pakkanen
parent 1e4d4fce22
commit f9200ca8f7
  1. 6
      docs/markdown/Commands.md
  2. 4
      docs/markdown/snippets/devenv_winepath.md
  3. 18
      mesonbuild/backend/backends.py
  4. 52
      mesonbuild/mdevenv.py

@ -351,4 +351,10 @@ top build directory, that file is loaded by gdb automatically. In the case of
python scripts that needs to load other python modules, `PYTHONPATH` may need
to be modified using `meson.add_devenv()`.
Since *Since 0.63.0* when cross compiling for Windows `WINEPATH` is used instead
of `PATH` which allows running Windows executables using wine. Note that since
`WINEPATH` size is currently limited to 1024 characters, paths relative to the
root of build directory are used. That means current workdir must be the root of
build directory when running wine.
{{ devenv_arguments.inc }}

@ -0,0 +1,4 @@
## Running Windows executables with Wine in `meson devenv`
When cross compiling for Windows, `meson devenv` now sets `WINEPATH` pointing to
all directories containing needed DLLs and executables.

@ -1801,9 +1801,12 @@ class Backend:
env = build.EnvironmentVariables()
extra_paths = set()
library_paths = set()
host_machine = self.environment.machines[MachineChoice.HOST]
need_exe_wrapper = self.environment.need_exe_wrapper()
need_wine = need_exe_wrapper and host_machine.is_windows()
for t in self.build.get_targets().values():
cross_built = not self.environment.machines.matches_build_machine(t.for_machine)
can_run = not cross_built or not self.environment.need_exe_wrapper()
can_run = not cross_built or not need_exe_wrapper or need_wine
in_default_dir = t.should_install() and not t.get_install_dir()[2]
if not can_run or not in_default_dir:
continue
@ -1813,22 +1816,25 @@ class Backend:
# so they get used by default instead of searching on system when
# in developer environment.
extra_paths.add(tdir)
if mesonlib.is_windows() or mesonlib.is_cygwin():
if host_machine.is_windows() or host_machine.is_cygwin():
# On windows we cannot rely on rpath to run executables from build
# directory. We have to add in PATH the location of every DLL needed.
extra_paths.update(self.determine_windows_extra_paths(t, []))
library_paths.update(self.determine_windows_extra_paths(t, []))
elif isinstance(t, build.SharedLibrary):
# Add libraries that are going to be installed in libdir into
# LD_LIBRARY_PATH. This allows running system applications using
# that library.
library_paths.add(tdir)
if library_paths:
if mesonlib.is_windows() or mesonlib.is_cygwin():
if host_machine.is_windows() or host_machine.is_cygwin():
extra_paths.update(library_paths)
elif mesonlib.is_osx():
elif host_machine.is_darwin():
env.prepend('DYLD_LIBRARY_PATH', list(library_paths))
else:
env.prepend('LD_LIBRARY_PATH', list(library_paths))
if extra_paths:
env.prepend('PATH', list(extra_paths))
if need_wine:
env.prepend('WINEPATH', list(extra_paths), separator=';')
else:
env.prepend('PATH', list(extra_paths))
return env

@ -28,6 +28,56 @@ 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 as e:
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:
return
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
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]]:
extra_env = build.EnvironmentVariables()
extra_env.set('MESON_DEVENV', ['1'])
@ -39,6 +89,8 @@ def get_env(b: build.Build, build_dir: str) -> T.Tuple[T.Dict[str, str], T.Set[s
env = i.get_env(env)
varnames |= i.get_names()
reduce_winepath(build_dir, env)
return env, varnames
def bash_completion_files(b: build.Build, install_data: 'InstallData') -> T.List[str]:

Loading…
Cancel
Save