From 3e7433086c19fbaa5029f2926515f3ca5087299c Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Thu, 3 Nov 2022 09:59:27 -0400 Subject: devenv: Add --workdir option Most of the time it is preferable to remain the the top source dir instead of going into the builddir. Add --workdir argument to be able to have a different workdir than builddir, but keep it default to builddir for backward compatibility, and also because it makes gdb integration better. --- mesonbuild/mdevenv.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index b75d18b83..6223b14e1 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, get_wine_shortpath +from .mesonlib import MesonException, is_windows, setup_vsenv, OptionKey, quote_arg, get_wine_shortpath from . import mlog import typing as T @@ -16,8 +16,10 @@ if T.TYPE_CHECKING: POWERSHELL_EXES = {'pwsh.exe', 'powershell.exe'} def add_arguments(parser: argparse.ArgumentParser) -> None: - parser.add_argument('-C', dest='wd', action=RealPathAction, - help='Directory to cd into before running') + parser.add_argument('-C', dest='builddir', type=Path, default='.', + help='Path to build directory') + parser.add_argument('--workdir', '-w', type=Path, default=None, + help='Directory to cd into before running (default: builddir, Since 1.0.0)') parser.add_argument('--dump', action='store_true', help='Only print required environment (Since 0.62.0)') parser.add_argument('devcmd', nargs=argparse.REMAINDER, metavar='command', @@ -123,11 +125,12 @@ def write_gdb_script(privatedir: Path, install_data: 'InstallData') -> None: mlog.log('Meson detected GDB helpers and added config in', mlog.bold(str(gdbinit_path))) def run(options: argparse.Namespace) -> int: - privatedir = Path(options.wd) / 'meson-private' + privatedir = Path(options.builddir) / 'meson-private' buildfile = privatedir / 'build.dat' if not buildfile.is_file(): - raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') - b = build.load(options.wd) + raise MesonException(f'Directory {options.builddir!r} does not seem to be a Meson build directory.') + b = build.load(options.builddir) + workdir = options.workdir or options.builddir devenv, varnames = get_env(b) if options.dump: @@ -182,7 +185,7 @@ def run(options: argparse.Namespace) -> int: try: return subprocess.call(args, close_fds=False, env=devenv, - cwd=options.wd) + cwd=workdir) except subprocess.CalledProcessError as e: return e.returncode except FileNotFoundError: -- cgit v1.2.3 From bc2f1fb2f3e4e3601a262facc16313e1852926f0 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Thu, 3 Nov 2022 10:02:33 -0400 Subject: devenv: Add more info how to get gdb scripts working Now that top builddir is not the default workdir any more, the .gdbinit file we write there won't be loaded automatically unless user cd there, or use --init-command. There is also a global setting that user has to set to allow automatically loading .gdbinit file. --- mesonbuild/mdevenv.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 6223b14e1..7f9b04ede 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -92,7 +92,7 @@ def add_gdb_auto_load(autoload_path: Path, gdb_helper: str, fname: Path) -> None except (FileExistsError, shutil.SameFileError): pass -def write_gdb_script(privatedir: Path, install_data: 'InstallData') -> None: +def write_gdb_script(privatedir: Path, install_data: 'InstallData', workdir: Path) -> None: if not shutil.which('gdb'): return bdir = privatedir.parent @@ -122,7 +122,16 @@ def write_gdb_script(privatedir: Path, install_data: 'InstallData') -> None: gdbinit_path.write_text(gdbinit_line, encoding='utf-8') first_time = True if first_time: - mlog.log('Meson detected GDB helpers and added config in', mlog.bold(str(gdbinit_path))) + gdbinit_path = gdbinit_path.resolve() + workdir_path = workdir.resolve() + rel_path = gdbinit_path.relative_to(workdir_path) + mlog.log('Meson detected GDB helpers and added config in', mlog.bold(str(rel_path))) + mlog.log('To load it automatically you might need to:') + mlog.log(' - Add', mlog.bold(f'add-auto-load-safe-path {gdbinit_path.parent}'), + 'in', mlog.bold('~/.gdbinit')) + if gdbinit_path.parent != workdir_path: + mlog.log(' - Change current workdir to', mlog.bold(str(rel_path.parent)), + 'or use', mlog.bold(f'--init-command {rel_path}')) def run(options: argparse.Namespace) -> int: privatedir = Path(options.builddir) / 'meson-private' @@ -142,7 +151,7 @@ def run(options: argparse.Namespace) -> int: return 0 install_data = minstall.load_install_data(str(privatedir / 'install.dat')) - write_gdb_script(privatedir, install_data) + write_gdb_script(privatedir, install_data, workdir) setup_vsenv(b.need_vsenv) -- cgit v1.2.3 From b926374205bd761085031755c87152d08bc10e9d Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 5 Nov 2022 10:30:10 -0400 Subject: devenv: Do not include system values in --dump This makes --dump print variables like `FOO=/path:$FOO:/another/path`. --- mesonbuild/mdevenv.py | 12 ++++++------ mesonbuild/utils/core.py | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 7f9b04ede..57972ce5f 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, is_windows, setup_vsenv, OptionKey, quote_arg, get_wine_shortpath +from .mesonlib import MesonException, is_windows, setup_vsenv, OptionKey, get_wine_shortpath from . import mlog import typing as T @@ -47,15 +47,15 @@ def reduce_winepath(env: T.Dict[str, str]) -> None: env['WINEPATH'] = get_wine_shortpath([winecmd], winepath.split(';')) mlog.log('Meson detected wine and has set WINEPATH accordingly') -def get_env(b: build.Build) -> T.Tuple[T.Dict[str, str], T.Set[str]]: +def get_env(b: build.Build, dump: bool) -> T.Tuple[T.Dict[str, str], T.Set[str]]: extra_env = build.EnvironmentVariables() extra_env.set('MESON_DEVENV', ['1']) extra_env.set('MESON_PROJECT_NAME', [b.project_name]) - env = os.environ.copy() + env = {} if dump else os.environ.copy() varnames = set() for i in itertools.chain(b.devenv, {extra_env}): - env = i.get_env(env) + env = i.get_env(env, dump) varnames |= i.get_names() reduce_winepath(env) @@ -141,12 +141,12 @@ def run(options: argparse.Namespace) -> int: b = build.load(options.builddir) workdir = options.workdir or options.builddir - devenv, varnames = get_env(b) + devenv, varnames = get_env(b, options.dump) if options.dump: if options.devcmd: raise MesonException('--dump option does not allow running other command.') for name in varnames: - print(f'{name}={quote_arg(devenv[name])}') + print(f'{name}="{devenv[name]}"') print(f'export {name}') return 0 diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py index ed413cace..5450cdc29 100644 --- a/mesonbuild/utils/core.py +++ b/mesonbuild/utils/core.py @@ -119,23 +119,24 @@ class EnvironmentVariables(HoldableObject): self.envvars.append((self._prepend, name, values, separator)) @staticmethod - def _set(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str: + def _set(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str: return separator.join(values) @staticmethod - def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str: - curr = env.get(name) + def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str: + curr = env.get(name, default_value) return separator.join(values if curr is None else [curr] + values) @staticmethod - def _prepend(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str: - curr = env.get(name) + def _prepend(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str: + curr = env.get(name, default_value) return separator.join(values if curr is None else values + [curr]) - def get_env(self, full_env: T.MutableMapping[str, str]) -> T.Dict[str, str]: + def get_env(self, full_env: T.MutableMapping[str, str], dump: bool = False) -> T.Dict[str, str]: env = full_env.copy() for method, name, values, separator in self.envvars: - env[name] = method(env, name, values, separator) + default_value = f'${name}' if dump else None + env[name] = method(env, name, values, separator, default_value) return env -- cgit v1.2.3 From f73970cc8f2599e41bac4e99de3a88ee7983a7ce Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 7 Nov 2022 13:04:22 -0500 Subject: devenv: Add executables locations to both PATH and WINEPATH Handles the case when wine-binfmt is installed, which makes .exe files executable without without specifying an exe wrapper. --- mesonbuild/backend/backends.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 747d80edd..eda6fb6d2 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1850,9 +1850,9 @@ class Backend: env = build.EnvironmentVariables() extra_paths = set() library_paths = set() + build_machine = self.environment.machines[MachineChoice.BUILD] 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() + need_wine = not build_machine.is_windows() 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 need_exe_wrapper or need_wine @@ -1874,18 +1874,23 @@ class Backend: # LD_LIBRARY_PATH. This allows running system applications using # that library. library_paths.add(tdir) + if need_wine: + # Executable paths should be in both PATH and WINEPATH. + # - Having them in PATH makes bash completion find it, + # and make running "foo.exe" find it when wine-binfmt is installed. + # - Having them in WINEPATH makes "wine foo.exe" find it. + library_paths.update(extra_paths) if library_paths: - if host_machine.is_windows() or host_machine.is_cygwin(): + if need_wine: + env.prepend('WINEPATH', list(library_paths), separator=';') + elif host_machine.is_windows() or host_machine.is_cygwin(): extra_paths.update(library_paths) 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: - if need_wine: - env.prepend('WINEPATH', list(extra_paths), separator=';') - else: - env.prepend('PATH', list(extra_paths)) + env.prepend('PATH', list(extra_paths)) return env def compiler_to_generator(self, target: build.BuildTarget, -- cgit v1.2.3 From 302a29593a49c875adb5638375c1e5018efb7fb9 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Sat, 5 Nov 2022 18:20:44 -0400 Subject: devenv: Always include env for HOST machine Cross compiled executables could still be run with an exe wrapper, or with proper binfmt installed. Fixes: #10999 --- mesonbuild/backend/backends.py | 4 +--- mesonbuild/mdevenv.py | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index eda6fb6d2..f5b09f356 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1854,10 +1854,8 @@ class Backend: host_machine = self.environment.machines[MachineChoice.HOST] need_wine = not build_machine.is_windows() 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 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: + if t.for_machine != MachineChoice.HOST or not in_default_dir: continue tdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t)) if isinstance(t, build.Executable): diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 57972ce5f..4b3d783a5 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -150,6 +150,14 @@ def run(options: argparse.Namespace) -> int: print(f'export {name}') return 0 + if b.environment.need_exe_wrapper(): + m = 'An executable wrapper could be required' + exe_wrapper = b.environment.get_exe_wrapper() + if exe_wrapper: + cmd = ' '.join(exe_wrapper.get_command()) + m += f': {cmd}' + mlog.log(m) + install_data = minstall.load_install_data(str(privatedir / 'install.dat')) write_gdb_script(privatedir, install_data, workdir) -- cgit v1.2.3 From 548c9adad44ce88788356180fc2ec8e3665b3952 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 7 Nov 2022 15:00:12 -0500 Subject: Remove useless EmptyExternalProgram It is only used by Environment.get_exe_wrapper() and every callers were handling None already. Type annotation was wrong, it already could return None for the case an exe wrapper is needed but none is provided. --- mesonbuild/backend/backends.py | 5 +---- mesonbuild/environment.py | 8 +++----- mesonbuild/modules/gnome.py | 5 ++--- mesonbuild/programs.py | 19 ------------------- 4 files changed, 6 insertions(+), 31 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index f5b09f356..27004f870 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1108,10 +1108,7 @@ class Backend: break is_cross = self.environment.is_cross_build(test_for_machine) - if is_cross and self.environment.need_exe_wrapper(): - exe_wrapper = self.environment.get_exe_wrapper() - else: - exe_wrapper = None + exe_wrapper = self.environment.get_exe_wrapper() machine = self.environment.machines[exe.for_machine] if machine.is_windows() or machine.is_cygwin(): extra_bdeps: T.List[T.Union[build.BuildTarget, build.CustomTarget]] = [] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index a9df75ee3..9691cf126 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -25,9 +25,7 @@ from .mesonlib import ( search_version, MesonBugException ) from . import mlog -from .programs import ( - ExternalProgram, EmptyExternalProgram -) +from .programs import ExternalProgram from .envconfig import ( BinaryTable, MachineInfo, Properties, known_cpu_families, CMakeVariables, @@ -852,7 +850,7 @@ class Environment: return value return not machine_info_can_run(self.machines[for_machine]) - def get_exe_wrapper(self) -> ExternalProgram: + def get_exe_wrapper(self) -> T.Optional[ExternalProgram]: if not self.need_exe_wrapper(): - return EmptyExternalProgram() + return None return self.exe_wrapper diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 1d5e746b2..d0d7dbf89 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -39,7 +39,7 @@ from ..interpreterbase.decorators import typed_pos_args from ..mesonlib import ( MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, ) -from ..programs import OverrideProgram, EmptyExternalProgram +from ..programs import OverrideProgram from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: @@ -1464,9 +1464,8 @@ class GnomeModule(ExtensionModule): t_args.append(f'--{program_name}={path}') if namespace: t_args.append('--namespace=' + namespace) - # if not need_exe_wrapper, we get an EmptyExternalProgram. If none provided, we get NoneType exe_wrapper = state.environment.get_exe_wrapper() - if not isinstance(exe_wrapper, (NoneType, EmptyExternalProgram)): + if exe_wrapper: t_args.append('--run=' + ' '.join(exe_wrapper.get_command())) t_args.append(f'--htmlargs={"@@".join(kwargs["html_args"])}') t_args.append(f'--scanargs={"@@".join(kwargs["scan_args"])}') diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py index 458fabac9..1d616aaf9 100644 --- a/mesonbuild/programs.py +++ b/mesonbuild/programs.py @@ -345,25 +345,6 @@ class NonExistingExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-i return False -class EmptyExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-init] - ''' - A program object that returns an empty list of commands. Used for cases - such as a cross file exe_wrapper to represent that it's not required. - ''' - - def __init__(self) -> None: - self.name = None - self.command = [] - self.path = None - - def __repr__(self) -> str: - r = '<{} {!r} -> {!r}>' - return r.format(self.__class__.__name__, self.name, self.command) - - def found(self) -> bool: - return True - - class OverrideProgram(ExternalProgram): """A script overriding a program.""" -- cgit v1.2.3 From 2dbe976278b66e97bf600b3885d8eda4874f03cf Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 7 Nov 2022 15:05:32 -0500 Subject: devenv: Set QEMU_LD_PREFIX to sys_root When the cross file has a sys_root, it is most probably needed to run executables with qemu. --- mesonbuild/mdevenv.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 4b3d783a5..b25d73b81 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -6,7 +6,8 @@ import itertools from pathlib import Path from . import build, minstall, dependencies -from .mesonlib import MesonException, is_windows, setup_vsenv, OptionKey, get_wine_shortpath +from .mesonlib import (MesonException, is_windows, setup_vsenv, OptionKey, + get_wine_shortpath, MachineChoice) from . import mlog import typing as T @@ -52,6 +53,10 @@ def get_env(b: build.Build, dump: bool) -> T.Tuple[T.Dict[str, str], T.Set[str]] extra_env.set('MESON_DEVENV', ['1']) extra_env.set('MESON_PROJECT_NAME', [b.project_name]) + sysroot = b.environment.properties[MachineChoice.HOST].get_sys_root() + if sysroot: + extra_env.set('QEMU_LD_PREFIX', [sysroot]) + env = {} if dump else os.environ.copy() varnames = set() for i in itertools.chain(b.devenv, {extra_env}): -- cgit v1.2.3 From 09cbc53f57f63709c952333fe7d7950926dff7b7 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Mon, 7 Nov 2022 15:24:41 -0500 Subject: devenv: Document recent changes --- docs/markdown/Commands.md | 2 ++ docs/markdown/snippets/devenv.md | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 docs/markdown/snippets/devenv.md diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index c3284611a..4a00c4f4c 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -345,6 +345,8 @@ These variables are set in environment in addition to those set using [[meson.ad schemas is compiled. This is automatically set when using `gnome.compile_schemas()`. Note that this requires GLib >= 2.64 when `gnome.compile_schemas()` is used in more than one directory. +- `QEMU_LD_PREFIX` *Since 1.0.0* is set to the `sys_root` value from cross file + when cross compiling and that property is defined. Since *Since 0.62.0* if bash-completion scripts are being installed and the shell is bash, they will be automatically sourced. diff --git a/docs/markdown/snippets/devenv.md b/docs/markdown/snippets/devenv.md new file mode 100644 index 000000000..d8a38bacc --- /dev/null +++ b/docs/markdown/snippets/devenv.md @@ -0,0 +1,15 @@ +## Developer environment improvements + +When cross compiling, the developer environment now sets all environment +variables for the HOST machine. It now also sets `QEMU_LD_PREFIX` to the +`sys_root` value from cross file if property is defined. That means that cross +compiled executables can often be run transparently on the build machine, for +example when cross compiling for aarch64 linux from x86_64 linux. + +A new argument `--workdir` has been added, by default it is set to build +directory. For example, `meson devenv -C builddir --workdir .` can be used to +remain in the current dir (often source dir) instead. + +`--dump` now prints shell commands like `FOO="/prepend/path:$FOO:/append/path"`, +using the litteral `$FOO` instead of current value of `FOO` from environment. +This makes easier to evaluate those expressions in a different environment. -- cgit v1.2.3