diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md index 8e34800a44b3..6203565a9df9 100644 --- a/docs/markdown/Commands.md +++ b/docs/markdown/Commands.md @@ -425,6 +425,22 @@ format should be used. There are currently 3 formats supported: {{ devenv_arguments.inc }} +### run + +*Since 1.7.0* + +{{ run_usage.inc }} + +Compile and run a target inside Meson's [`devenv`](#devenv). + +In the case the project installs more than one executable, `--bin ` +argument can be used to specify which one to run. See [`compile`](#compile) +command for `target` syntax. + +The remainder of the command line are arguments passed to the target executable: +``` +meson run -C builddir --bin gst-launch-1.0 videotestsrc ! glimagesink +``` ### format diff --git a/docs/markdown/snippets/run.md b/docs/markdown/snippets/run.md new file mode 100644 index 000000000000..181a324c8356 --- /dev/null +++ b/docs/markdown/snippets/run.md @@ -0,0 +1,13 @@ +## Build and run the project + +[`Meson run`](Commands.md#run) compiles and runs a target inside Meson's +[`devenv`](Commands.md#devenv). + +In the case the project installs more than one executable, `--bin ` +argument can be used to specify which one to run. See [`compile`](#compile) +command for `target` syntax. + +The remainder of the command line are arguments passed to the target executable: +``` +meson run -C builddir --bin gst-launch-1.0 videotestsrc ! glimagesink +``` diff --git a/mesonbuild/mcompile.py b/mesonbuild/mcompile.py index 495f407e48b6..b5c9a472a1f1 100644 --- a/mesonbuild/mcompile.py +++ b/mesonbuild/mcompile.py @@ -356,6 +356,33 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: help='Arguments to pass to `xcodebuild` (applied only on `xcode` backend).' ) +# Used by mrun.py +def compile_single_executable(environment: Environment, target: T.Optional[str]) -> T.Tuple[T.Optional[str], int]: + builddir = Path(environment.build_dir) + intro_data = parse_introspect_data(builddir) + if target: + intro_target = get_target_from_intro_data(target, builddir, intro_data) + if intro_target['type'] != 'executable': + raise MesonException('--bin argument must be an executable') + else: + found_targets: T.List[IntroTarget] = [] + for name, targets in intro_data.items(): + for intro_target in targets: + # Restrict the search to installable executables. + if intro_target['type'] == 'executable' and intro_target['installed']: + found_targets.append(intro_target) + if not found_targets: + raise MesonException('No executable targets found.') + elif len(found_targets) > 1: + suggestions_str = get_suggestions(builddir, found_targets) + raise MesonException('Multiple executable targets found.' + f' Specify --bin argument:\n{suggestions_str}') + intro_target = found_targets[0] + returncode = run_compile(environment, [intro_target]) + if returncode != 0: + return None, returncode + return intro_target['filename'][0], 0 + def run_compile(environment: Environment, targets: T.List[IntroTarget], jobs: int = 0, load_average: int = 0, verbose: bool = False, clean: bool = False, ninja_args: T.Optional[T.List[str]] = None, diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 2c1ca97a386f..6b1861e1369f 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -64,7 +64,8 @@ def errorhandler(e: Exception, command: str) -> int: class CommandLineParser: def __init__(self) -> None: # only import these once we do full argparse processing - from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, mcompile, mdevenv, mformat + from . import (mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, + mcompile, mdevenv, mformat, mrun) from .scripts import env2mfile, reprotest from .wrap import wraptool import shutil @@ -101,6 +102,8 @@ def __init__(self) -> None: help_msg='Build the project') self.add_command('devenv', mdevenv.add_arguments, mdevenv.run, help_msg='Run commands in developer environment') + self.add_command('run', mrun.add_arguments, mrun.run, + help_msg='Compile and run an executable') self.add_command('env2mfile', env2mfile.add_arguments, env2mfile.run, help_msg='Convert current environment to a cross or native file') self.add_command('reprotest', reprotest.add_arguments, reprotest.run, diff --git a/mesonbuild/mrun.py b/mesonbuild/mrun.py new file mode 100644 index 000000000000..421164281b0e --- /dev/null +++ b/mesonbuild/mrun.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import argparse +import typing as T + +from pathlib import Path +from . import build +from .mesonlib import setup_vsenv, MesonException +from .options import OptionKey +from .mcompile import compile_single_executable +from .mdevenv import run_cmd + + +# Note: when adding arguments, please also add them to the completion +# scripts in $MESONSRC/data/shell-completions/ +def add_arguments(parser: argparse.ArgumentParser) -> None: + parser.add_argument('-C', dest='builddir', type=Path, default='.', + help='Path to build directory') + parser.add_argument('--bin', default=None, + help='Executable target to build and run') + parser.add_argument('arguments', nargs=argparse.REMAINDER, + help='Arguments to pass to the executable') + + +def run(options: argparse.Namespace) -> int: + buildfile = Path(options.builddir, 'meson-private', 'build.dat') + if not buildfile.is_file(): + raise MesonException(f'Directory {options.builddir!r} does not seem to be a Meson build directory.') + b = build.load(options.builddir) + + need_vsenv = T.cast('bool', b.environment.coredata.get_option(OptionKey('vsenv'))) + setup_vsenv(need_vsenv) + + path, returncode = compile_single_executable(b.environment, options.bin) + if returncode != 0: + return returncode + + cmd = [path] + options.arguments + return run_cmd(b, cmd)