diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4e6c2591f..8328c57f0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -69,12 +69,12 @@ class TargetInstallData: self.optional = optional class ExecutableSerialisation: - def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper, - workdir, extra_paths, capture, needs_exe_wrapper: bool): + def __init__(self, name, fname, cmd_args, env=None, is_cross=False, exe_wrapper=None, + workdir=None, extra_paths=None, capture=None, needs_exe_wrapper: bool = False): self.name = name self.fname = fname self.cmd_args = cmd_args - self.env = env + self.env = env or {} self.is_cross = is_cross if exe_wrapper is not None: assert(isinstance(exe_wrapper, dependencies.ExternalProgram)) @@ -345,6 +345,7 @@ class Backend: else: exe_cmd = [exe] exe_for_machine = MachineChoice.BUILD + is_cross_built = not self.environment.machines.matches_build_machine(exe_for_machine) if is_cross_built and self.environment.need_exe_wrapper(): exe_wrapper = self.environment.get_exe_wrapper() @@ -356,10 +357,13 @@ class Backend: else: exe_wrapper = None - force_serialize = force_serialize or extra_paths or capture or workdir or \ + force_serialize = force_serialize or extra_paths or workdir or \ exe_wrapper or any('\n' in c for c in cmd_args) if not force_serialize: - return None + if not capture: + return None + return (self.environment.get_build_command() + + ['--internal', 'exe', '--capture', capture, '--'] + exe_cmd + cmd_args) workdir = workdir or self.environment.get_build_dir() env = {} @@ -384,7 +388,7 @@ class Backend: extra_paths, capture, self.environment.need_exe_wrapper()) pickle.dump(es, f) - return self.environment.get_build_command() + ['--internal', 'exe', exe_data] + return self.environment.get_build_command() + ['--internal', 'exe', '--unpickle', exe_data] def serialize_tests(self): test_data = os.path.join(self.environment.get_scratch_dir(), 'meson_test_setup.dat') diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index c1d0d64e9..e5bc9dc3c 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -20,12 +20,14 @@ import platform import subprocess from .. import mesonlib +from ..backend.backends import ExecutableSerialisation options = None def buildparser(): - parser = argparse.ArgumentParser() - parser.add_argument('args', nargs='+') + parser = argparse.ArgumentParser(description='Custom executable wrapper for Meson. Do not run on your own, mmm\'kay?') + parser.add_argument('--unpickle') + parser.add_argument('--capture') return parser def is_windows(): @@ -101,13 +103,26 @@ def run_exe(exe): def run(args): global options - options = buildparser().parse_args(args) - if len(options.args) != 1: - print('Test runner for Meson. Do not run on your own, mmm\'kay?') - print(sys.argv[0] + ' [data file]') - exe_data_file = options.args[0] - with open(exe_data_file, 'rb') as f: - exe = pickle.load(f) + parser = buildparser() + options, cmd_args = parser.parse_known_args(args) + # argparse supports double dash to separate options and positional arguments, + # but the user has to remove it manually. + if cmd_args and cmd_args[0] == '--': + cmd_args = cmd_args[1:] + if not options.unpickle and not cmd_args: + parser.error('either --unpickle or executable and arguments are required') + if options.unpickle: + if cmd_args or options.capture: + parser.error('no other arguments can be used with --unpickle') + with open(options.unpickle, 'rb') as f: + exe = pickle.load(f) + else: + exe_cmd = cmd_args[0] + cmd_args = cmd_args[1:] + basename = os.path.basename(exe_cmd) + exe = ExecutableSerialisation(basename, [exe_cmd], cmd_args, + capture=options.capture) + return run_exe(exe) if __name__ == '__main__':