diff options
author | Simon Ser <contact@emersion.fr> | 2021-06-23 10:58:26 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2021-06-29 20:54:13 +0300 |
commit | 1f3adc4dbe20196eb45b7c0cfe502ce108618ada (patch) | |
tree | 60771f94cfca0c0761a95eafa584c83f478edeaa | |
parent | 4bfee181c5a166e3d429bd265e06d299dce50f30 (diff) | |
download | meson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.zip meson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.tar.gz meson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.tar.bz2 |
Add feed arg to custom_target()
-rw-r--r-- | docs/markdown/Reference-manual.md | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/custom-target-feed.md | 4 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 32 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 1 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 1 | ||||
-rw-r--r-- | mesonbuild/build.py | 13 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 4 | ||||
-rw-r--r-- | mesonbuild/scripts/meson_exe.py | 11 | ||||
-rw-r--r-- | test cases/common/243 custom target feed/data_source.txt | 1 | ||||
-rw-r--r-- | test cases/common/243 custom target feed/meson.build | 24 | ||||
-rwxr-xr-x | test cases/common/243 custom target feed/my_compiler.py | 14 | ||||
-rw-r--r-- | test cases/common/243 custom target feed/test.json | 5 |
12 files changed, 95 insertions, 19 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 388c975..16fcbbf 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -383,6 +383,10 @@ following. `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`, or an [`environment()` object](#environment-object) which allows more sophisticated environment juggling. +- `feed` *(since 0.59.0)*: there are some compilers that can't be told to read + their input from a file and instead read it from standard input. When this + argument is set to true, Meson feeds the input file to `stdin`. Note that + your argument list may not contain `@INPUT@` when feed mode is active. The list of strings passed to the `command` keyword argument accept the following special string substitutions: diff --git a/docs/markdown/snippets/custom-target-feed.md b/docs/markdown/snippets/custom-target-feed.md new file mode 100644 index 0000000..f965152 --- /dev/null +++ b/docs/markdown/snippets/custom-target-feed.md @@ -0,0 +1,4 @@ +## The custom_target() function now accepts a feed argument + +It is now possible to provide a `feed: true` argument to `custom_target()` to +pipe the target's input file to the program's standard input. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 21a6c83..aa8e844 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -141,7 +141,7 @@ class SubdirInstallData(InstallDataBase): class ExecutableSerialisation: def __init__(self, cmd_args, env: T.Optional[build.EnvironmentVariables] = None, exe_wrapper=None, - workdir=None, extra_paths=None, capture=None) -> None: + workdir=None, extra_paths=None, capture=None, feed=None) -> None: self.cmd_args = cmd_args self.env = env if exe_wrapper is not None: @@ -150,6 +150,7 @@ class ExecutableSerialisation: self.workdir = workdir self.extra_paths = extra_paths self.capture = capture + self.feed = feed self.pickled = False self.skip_if_destdir = False self.verbose = False @@ -441,7 +442,7 @@ class Backend: return result def get_executable_serialisation(self, cmd, workdir=None, - extra_bdeps=None, capture=None, + extra_bdeps=None, capture=None, feed=None, env: T.Optional[build.EnvironmentVariables] = None): exe = cmd[0] cmd_args = cmd[1:] @@ -489,17 +490,18 @@ class Backend: workdir = workdir or self.environment.get_build_dir() return ExecutableSerialisation(exe_cmd + cmd_args, env, exe_wrapper, workdir, - extra_paths, capture) + extra_paths, capture, feed) def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None, - extra_bdeps=None, capture=None, force_serialize=False, + extra_bdeps=None, capture=None, feed=None, + force_serialize=False, env: T.Optional[build.EnvironmentVariables] = None, verbose: bool = False): ''' Serialize an executable for running with a generator or a custom target ''' cmd = [exe] + cmd_args - es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, env) + es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, feed, env) es.verbose = verbose reasons = [] if es.extra_paths: @@ -521,12 +523,19 @@ class Backend: if capture: reasons.append('to capture output') + if feed: + reasons.append('to feed input') if not force_serialize: - if not capture: + if not capture and not feed: return es.cmd_args, '' + args = [] + if capture: + args += ['--capture', capture] + if feed: + args += ['--feed', feed] return ((self.environment.get_build_command() + - ['--internal', 'exe', '--capture', capture, '--'] + es.cmd_args), + ['--internal', 'exe'] + args + ['--'] + es.cmd_args), ', '.join(reasons)) if isinstance(exe, (programs.ExternalProgram, @@ -538,10 +547,11 @@ class Backend: basename = os.path.basename(exe) # Can't just use exe.name here; it will likely be run more than once - # Take a digest of the cmd args, env, workdir, and capture. This avoids - # collisions and also makes the name deterministic over regenerations - # which avoids a rebuild by Ninja because the cmdline stays the same. - data = bytes(str(es.env) + str(es.cmd_args) + str(es.workdir) + str(capture), + # Take a digest of the cmd args, env, workdir, capture, and feed. This + # avoids collisions and also makes the name deterministic over + # regenerations which avoids a rebuild by Ninja because the cmdline + # stays the same. + data = bytes(str(es.env) + str(es.cmd_args) + str(es.workdir) + str(capture) + str(feed), encoding='utf-8') digest = hashlib.sha1(data).hexdigest() scratch_file = f'meson_exe_{basename}_{digest}.dat' diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 597789b..d6b535b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -975,6 +975,7 @@ class NinjaBackend(backends.Backend): cmd, reason = self.as_meson_exe_cmdline(target.name, target.command[0], cmd[1:], extra_bdeps=target.get_transitive_build_target_deps(), capture=ofilenames[0] if target.capture else None, + feed=srcs[0] if target.feed else None, env=target.env) if reason: cmd_type = f' (wrapped by meson {reason})' diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 7fcc324..6e6e47f 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -563,6 +563,7 @@ class Vs2010Backend(backends.Backend): workdir=tdir_abs, extra_bdeps=extra_bdeps, capture=ofilenames[0] if target.capture else None, + feed=srcs[0] if target.feed else None, force_serialize=True, env=target.env) if target.build_always_stale: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index f9de5c4..d72aab8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2198,6 +2198,7 @@ class CustomTarget(Target, CommandBase): 'output', 'command', 'capture', + 'feed', 'install', 'install_dir', 'install_mode', @@ -2296,6 +2297,9 @@ class CustomTarget(Target, CommandBase): self.capture = kwargs.get('capture', False) if self.capture and len(self.outputs) != 1: raise InvalidArguments('Capturing can only output to a single file.') + self.feed = kwargs.get('feed', False) + if self.feed and len(self.sources) != 1: + raise InvalidArguments('Feeding can only input from a single file.') self.console = kwargs.get('console', False) if not isinstance(self.console, bool): raise InvalidArguments('"console" kwarg only accepts booleans') @@ -2311,10 +2315,11 @@ class CustomTarget(Target, CommandBase): raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') self.depfile = depfile self.command = self.flatten_command(kwargs['command']) - if self.capture: - for c in self.command: - if isinstance(c, str) and '@OUTPUT@' in c: - raise InvalidArguments('@OUTPUT@ is not allowed when capturing output.') + for c in self.command: + if self.capture and isinstance(c, str) and '@OUTPUT@' in c: + raise InvalidArguments('@OUTPUT@ is not allowed when capturing output.') + if self.feed and isinstance(c, str) and '@INPUT@' in c: + raise InvalidArguments('@INPUT@ is not allowed when feeding input.') if 'install' in kwargs: self.install = kwargs['install'] if not isinstance(self.install, bool): diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1a9872d..693924f 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1694,9 +1694,11 @@ external dependencies (including libraries) must go to "dependencies".''') @FeatureNewKwargs('custom_target', '0.48.0', ['console']) @FeatureNewKwargs('custom_target', '0.47.0', ['install_mode', 'build_always_stale']) @FeatureNewKwargs('custom_target', '0.40.0', ['build_by_default']) + @FeatureNewKwargs('custom_target', '0.59.0', ['feed']) @permittedKwargs({'input', 'output', 'command', 'install', 'install_dir', 'install_mode', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', - 'build_by_default', 'build_always_stale', 'console', 'env'}) + 'build_by_default', 'build_always_stale', 'console', 'env', + 'feed'}) def func_custom_target(self, node, args, kwargs): if len(args) != 1: raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name') diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index ea0fef6..9c1ae59 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -29,6 +29,7 @@ def buildparser() -> argparse.ArgumentParser: 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') + parser.add_argument('--feed') return parser def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) -> int: @@ -53,13 +54,17 @@ def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) -> ['Z:' + p for p in exe.extra_paths] + child_env.get('WINEPATH', '').split(';') ) + stdin = None + if exe.feed: + stdin = open(exe.feed, 'rb') + pipe = subprocess.PIPE if exe.verbose: assert not exe.capture, 'Cannot capture and print to console at the same time' pipe = None p = subprocess.Popen(cmd_args, env=child_env, cwd=exe.workdir, - close_fds=False, stdout=pipe, stderr=pipe) + close_fds=False, stdin=stdin, stdout=pipe, stderr=pipe) stdout, stderr = p.communicate() if p.returncode == 0xc0000135: @@ -103,13 +108,13 @@ def run(args: T.List[str]) -> int: 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: + if cmd_args or options.capture or options.feed: parser.error('no other arguments can be used with --unpickle') with open(options.unpickle, 'rb') as f: exe = pickle.load(f) exe.pickled = True else: - exe = ExecutableSerialisation(cmd_args, capture=options.capture) + exe = ExecutableSerialisation(cmd_args, capture=options.capture, feed=options.feed) return run_exe(exe) diff --git a/test cases/common/243 custom target feed/data_source.txt b/test cases/common/243 custom target feed/data_source.txt new file mode 100644 index 0000000..0c23cc0 --- /dev/null +++ b/test cases/common/243 custom target feed/data_source.txt @@ -0,0 +1 @@ +This is a text only input file. diff --git a/test cases/common/243 custom target feed/meson.build b/test cases/common/243 custom target feed/meson.build new file mode 100644 index 0000000..1cda37d --- /dev/null +++ b/test cases/common/243 custom target feed/meson.build @@ -0,0 +1,24 @@ +project('custom target feed', 'c') + +python3 = import('python3').find_python() + +# Note that this will not add a dependency to the compiler executable. +# Code will not be rebuilt if it changes. +comp = '@0@/@1@'.format(meson.current_source_dir(), 'my_compiler.py') + +mytarget = custom_target('bindat', + output : 'data.dat', + input : 'data_source.txt', + feed : true, + command : [python3, comp, '@OUTPUT@'], + install : true, + install_dir : 'subdir' +) + +ct_output_exists = '''import os, sys +if not os.path.exists(sys.argv[1]): + print("could not find {!r} in {!r}".format(sys.argv[1], os.getcwd())) + sys.exit(1) +''' + +test('capture-wrote', python3, args : ['-c', ct_output_exists, mytarget]) diff --git a/test cases/common/243 custom target feed/my_compiler.py b/test cases/common/243 custom target feed/my_compiler.py new file mode 100755 index 0000000..97d4208 --- /dev/null +++ b/test cases/common/243 custom target feed/my_compiler.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import sys + +if __name__ == '__main__': + if len(sys.argv) != 2: + print(sys.argv[0], 'output_file') + sys.exit(1) + ifile = sys.stdin.read() + if ifile != 'This is a text only input file.\n': + print('Malformed input') + sys.exit(1) + with open(sys.argv[1], 'w+') as f: + f.write('This is a binary output file.') diff --git a/test cases/common/243 custom target feed/test.json b/test cases/common/243 custom target feed/test.json new file mode 100644 index 0000000..ba66b02 --- /dev/null +++ b/test cases/common/243 custom target feed/test.json @@ -0,0 +1,5 @@ +{ + "installed": [ + {"type": "file", "file": "usr/subdir/data.dat"} + ] +} |