summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2022-12-09 15:26:06 +0200
committerGitHub <noreply@github.com>2022-12-09 15:26:06 +0200
commit2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd (patch)
tree57c5fa9c668d8488042a6b71282c1035996cce0e
parent7b2c47eb45e7a5b4787e89a6b6f98a8753868d1a (diff)
parent09cbc53f57f63709c952333fe7d7950926dff7b7 (diff)
downloadmeson-2ec3fe7a4af5bd4d8a83947d675f9233805f3dfd.tar.gz
Merge pull request #10990 from xclaesse/devenv
devenv: various improvements
-rw-r--r--docs/markdown/Commands.md2
-rw-r--r--docs/markdown/snippets/devenv.md15
-rw-r--r--mesonbuild/backend/backends.py28
-rw-r--r--mesonbuild/environment.py8
-rw-r--r--mesonbuild/mdevenv.py55
-rw-r--r--mesonbuild/modules/gnome.py5
-rw-r--r--mesonbuild/programs.py19
-rw-r--r--mesonbuild/utils/core.py15
8 files changed, 84 insertions, 63 deletions
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.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 747d80edd..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]] = []
@@ -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 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/mdevenv.py b/mesonbuild/mdevenv.py
index b75d18b83..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, 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 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."""
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