diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2021-02-20 12:04:01 -0500 |
---|---|---|
committer | Xavier Claessens <xclaesse@gmail.com> | 2021-03-16 09:00:50 -0400 |
commit | 598e968993da58c89f773dc732c708a54b0ec8db (patch) | |
tree | 7aff62faa24d580ea64fdcf65922c7f0d109a712 /mesonbuild | |
parent | 567c96b68b1dfe3cd6b52b0d26dfc78e5c0e6b76 (diff) | |
download | meson-598e968993da58c89f773dc732c708a54b0ec8db.zip meson-598e968993da58c89f773dc732c708a54b0ec8db.tar.gz meson-598e968993da58c89f773dc732c708a54b0ec8db.tar.bz2 |
Add `meson devenv` command and meson.add_devenv()
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/backend/backends.py | 19 | ||||
-rw-r--r-- | mesonbuild/build.py | 3 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 19 | ||||
-rw-r--r-- | mesonbuild/mdevenv.py | 79 | ||||
-rw-r--r-- | mesonbuild/mesonmain.py | 4 | ||||
-rw-r--r-- | mesonbuild/msetup.py | 1 | ||||
-rw-r--r-- | mesonbuild/scripts/cmd_or_ps.ps1 | 22 |
7 files changed, 139 insertions, 8 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 9f7f45d..e2297a3 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1491,3 +1491,22 @@ class Backend: }] return [] + + def get_devenv(self) -> build.EnvironmentVariables: + env = build.EnvironmentVariables() + extra_paths = set() + 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() + in_bindir = t.should_install() and not t.get_install_dir(self.environment)[1] + if isinstance(t, build.Executable) and can_run and in_bindir: + # Add binaries that are going to be installed in bindir into PATH + # so they get used by default instead of searching on system when + # in developer environment. + extra_paths.add(os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))) + if mesonlib.is_windows() or mesonlib.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, [])) + env.prepend('PATH', list(extra_paths)) + return env diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e3fad3d..b81e5dd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -229,6 +229,7 @@ class Build: self.find_overrides = {} self.searched_programs = set() # The list of all programs that have been searched for. self.dependency_overrides = PerMachine({}, {}) + self.devenv: T.List[EnvironmentVariables] = [] def get_build_targets(self): build_targets = OrderedDict() @@ -393,7 +394,7 @@ class ExtractedObjects: ] class EnvironmentVariables: - def __init__(self): + def __init__(self) -> None: self.envvars = [] # The set of all env vars we have operations for. Only used for self.has_name() self.varnames = set() diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 84d5e5c..3e39720 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -32,7 +32,6 @@ from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabl from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs from .interpreterbase import ObjectHolder, MesonVersionString from .interpreterbase import TYPE_var, TYPE_nkwargs -from .interpreterbase import typed_pos_args from .modules import ModuleReturnValue, ModuleObject, ModuleState from .cmake import CMakeInterpreter from .backend.backends import TestProtocol, Backend, ExecutableSerialisation @@ -256,8 +255,8 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En if isinstance(initial_values, dict): for k, v in initial_values.items(): self.set_method([k, v], {}) - elif isinstance(initial_values, list): - for e in initial_values: + elif initial_values is not None: + for e in mesonlib.stringlistify(initial_values): if '=' not in e: raise InterpreterException('Env var definition must be of type key=val.') (k, val) = e.split('=', 1) @@ -266,8 +265,6 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En if ' ' in k: raise InterpreterException('Env var key must not have spaces in it.') self.set_method([k, val], {}) - elif initial_values: - raise AssertionError('Unsupported EnvironmentVariablesHolder initial_values') def __repr__(self) -> str: repr_str = "<{0}: {1}>" @@ -1915,6 +1912,7 @@ class MesonMain(InterpreterObject): 'get_external_property': self.get_external_property_method, 'has_external_property': self.has_external_property_method, 'backend': self.backend_method, + 'add_devenv': self.add_devenv_method, }) def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args): @@ -2241,6 +2239,16 @@ class MesonMain(InterpreterObject): for_machine = self.interpreter.machine_from_native_kwarg(kwargs) return prop_name in self.interpreter.environment.properties[for_machine] + @FeatureNew('add_devenv', '0.58.0') + @noKwargs + @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder)) + def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None: + env = args[0] + if isinstance(env, (str, list, dict)): + env = EnvironmentVariablesHolder(env) + self.build.devenv.append(env.held_object) + + known_library_kwargs = ( build.known_shlib_kwargs | build.known_stlib_kwargs @@ -4084,7 +4092,6 @@ This will become a hard error in the future.''' % kwargs['input'], location=self env = EnvironmentVariablesHolder(envlist) env = env.held_object else: - envlist = listify(envlist) # Convert from array to environment object env = EnvironmentVariablesHolder(envlist) env = env.held_object diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py new file mode 100644 index 0000000..7594db2 --- /dev/null +++ b/mesonbuild/mdevenv.py @@ -0,0 +1,79 @@ +import os, subprocess +import argparse +import tempfile + +from pathlib import Path +from . import build +from .mesonlib import MesonException, is_windows + +import typing as T + +def add_arguments(parser: argparse.ArgumentParser) -> None: + parser.add_argument('-C', default='.', dest='wd', + help='directory to cd into before running') + parser.add_argument('command', nargs=argparse.REMAINDER, + help='Command to run in developer environment (default: interactive shell)') + +def get_windows_shell() -> str: + mesonbuild = Path(__file__).parent + script = mesonbuild / 'scripts' / 'cmd_or_ps.ps1' + command = ['powershell.exe', '-noprofile', '-executionpolicy', 'bypass', '-file', str(script)] + result = subprocess.check_output(command) + return result.decode().strip() + +def get_env(b: build.Build, build_dir: str) -> T.Dict[str, str]: + env = os.environ.copy() + for i in b.devenv: + env = i.get_env(env) + + extra_env = build.EnvironmentVariables() + extra_env.set('MESON_DEVENV', ['1']) + extra_env.set('MESON_PROJECT_NAME', [b.project_name]) + + meson_uninstalled = Path(build_dir) / 'meson-uninstalled' + if meson_uninstalled.is_dir(): + extra_env.prepend('PKG_CONFIG_PATH', [str(meson_uninstalled)]) + + return extra_env.get_env(env) + +def run(options: argparse.Namespace) -> int: + options.wd = os.path.abspath(options.wd) + buildfile = Path(options.wd) / 'meson-private' / '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) + + devenv = get_env(b, options.wd) + + args = options.command + if not args: + prompt_prefix = f'[{b.project_name}]' + if is_windows(): + shell = get_windows_shell() + if shell == 'powershell.exe': + args = ['powershell.exe'] + args += ['-NoLogo', '-NoExit'] + prompt = f'function global:prompt {{ "{prompt_prefix} PS " + $PWD + "> "}}' + args += ['-Command', prompt] + else: + args = [os.environ.get("COMSPEC", r"C:\WINDOWS\system32\cmd.exe")] + args += ['/k', f'prompt {prompt_prefix} $P$G'] + else: + args = [os.environ.get("SHELL", os.path.realpath("/bin/sh"))] + if "bash" in args[0] and not os.environ.get("MESON_DISABLE_PS1_OVERRIDE"): + tmprc = tempfile.NamedTemporaryFile(mode='w') + bashrc = os.path.expanduser('~/.bashrc') + if os.path.exists(bashrc): + tmprc.write(f'. {bashrc}\n') + tmprc.write(f'export PS1="{prompt_prefix} $PS1"') + tmprc.flush() + # Let the GC remove the tmp file + args.append("--rcfile") + args.append(tmprc.name) + + try: + return subprocess.call(args, close_fds=False, + env=devenv, + cwd=options.wd) + except subprocess.CalledProcessError as e: + return e.returncode diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 173e998..208cfe4 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -22,7 +22,7 @@ import shutil from . import mesonlib from . import mlog -from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, mcompile +from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, mcompile, mdevenv from .mesonlib import MesonException from .environment import detect_msys2_arch from .wrap import wraptool @@ -64,6 +64,8 @@ class CommandLineParser: help_msg='Modify the project definition') self.add_command('compile', mcompile.add_arguments, mcompile.run, help_msg='Build the project') + self.add_command('devenv', mdevenv.add_arguments, mdevenv.run, + help_msg='Run commands in developer environment') # Hidden commands self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command, diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index f42d013..139b476 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -243,6 +243,7 @@ class MesonApp: profile.runctx('intr.backend.generate()', globals(), locals(), filename=fname) else: intr.backend.generate() + b.devenv.append(intr.backend.get_devenv()) build.save(b, dumpfile) if env.first_invocation: coredata.write_cmd_line_file(self.build_dir, self.options) diff --git a/mesonbuild/scripts/cmd_or_ps.ps1 b/mesonbuild/scripts/cmd_or_ps.ps1 new file mode 100644 index 0000000..ccef8e8 --- /dev/null +++ b/mesonbuild/scripts/cmd_or_ps.ps1 @@ -0,0 +1,22 @@ +# Copyied from GStreamer project +# Author: Seungha Yang <seungha.yang@navercorp.com> + +$i=1 +$ppid=(gwmi win32_process -Filter "processid='$pid'").parentprocessid +$pname=(Get-Process -id $ppid).Name +While($true) { + if($pname -eq "cmd" -Or $pname -eq "powershell") { + Write-Host ("{0}.exe" -f $pname) + Break + } + + # 10 times iteration seems to be sufficient + if($i -gt 10) { + Break + } + + # not found yet, find grand parant + $ppid=(gwmi win32_process -Filter "processid='$ppid'").parentprocessid + $pname=(Get-Process -id $ppid).Name + $i++ +} |