diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2022-12-09 15:26:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-09 15:26:06 +0200 |
commit | 2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd (patch) | |
tree | 57c5fa9c668d8488042a6b71282c1035996cce0e | |
parent | 7b2c47eb45e7a5b4787e89a6b6f98a8753868d1a (diff) | |
parent | 09cbc53f57f63709c952333fe7d7950926dff7b7 (diff) | |
download | meson-2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd.zip meson-2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd.tar.gz meson-2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd.tar.bz2 |
Merge pull request #10990 from xclaesse/devenv
devenv: various improvements
-rw-r--r-- | docs/markdown/Commands.md | 2 | ||||
-rw-r--r-- | docs/markdown/snippets/devenv.md | 15 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 28 | ||||
-rw-r--r-- | mesonbuild/environment.py | 8 | ||||
-rw-r--r-- | mesonbuild/mdevenv.py | 55 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 5 | ||||
-rw-r--r-- | mesonbuild/programs.py | 19 | ||||
-rw-r--r-- | mesonbuild/utils/core.py | 15 |
8 files changed, 84 insertions, 63 deletions
diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index c328461..4a00c4f 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 0000000..d8a38ba --- /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. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 747d80e..27004f8 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]] = [] @@ -1850,14 +1847,12 @@ 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 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): @@ -1874,18 +1869,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, diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index a9df75e..9691cf1 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/mdevenv.py b/mesonbuild/mdevenv.py index b75d18b..b25d73b 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, RealPathAction, is_windows, setup_vsenv, OptionKey, quote_arg, get_wine_shortpath +from .mesonlib import (MesonException, is_windows, setup_vsenv, OptionKey, + get_wine_shortpath, MachineChoice) from . import mlog import typing as T @@ -16,8 +17,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', @@ -45,15 +48,19 @@ 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() + 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}): - env = i.get_env(env) + env = i.get_env(env, dump) varnames |= i.get_names() reduce_winepath(env) @@ -90,7 +97,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 @@ -120,26 +127,44 @@ 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.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) + 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 + 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) + write_gdb_script(privatedir, install_data, workdir) setup_vsenv(b.need_vsenv) @@ -182,7 +207,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: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 1d5e746..d0d7dbf 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 458faba..1d616aa 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.""" diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py index ed413ca..5450cdc 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 |