aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2021-06-23 10:58:26 +0200
committerJussi Pakkanen <jpakkane@gmail.com>2021-06-29 20:54:13 +0300
commit1f3adc4dbe20196eb45b7c0cfe502ce108618ada (patch)
tree60771f94cfca0c0761a95eafa584c83f478edeaa
parent4bfee181c5a166e3d429bd265e06d299dce50f30 (diff)
downloadmeson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.zip
meson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.tar.gz
meson-1f3adc4dbe20196eb45b7c0cfe502ce108618ada.tar.bz2
Add feed arg to custom_target()
-rw-r--r--docs/markdown/Reference-manual.md4
-rw-r--r--docs/markdown/snippets/custom-target-feed.md4
-rw-r--r--mesonbuild/backend/backends.py32
-rw-r--r--mesonbuild/backend/ninjabackend.py1
-rw-r--r--mesonbuild/backend/vs2010backend.py1
-rw-r--r--mesonbuild/build.py13
-rw-r--r--mesonbuild/interpreter/interpreter.py4
-rw-r--r--mesonbuild/scripts/meson_exe.py11
-rw-r--r--test cases/common/243 custom target feed/data_source.txt1
-rw-r--r--test cases/common/243 custom target feed/meson.build24
-rwxr-xr-xtest cases/common/243 custom target feed/my_compiler.py14
-rw-r--r--test cases/common/243 custom target feed/test.json5
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"}
+ ]
+}