From dcaf2d7b3d010526eb5035fec788f1b9a854262c Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 24 Aug 2016 04:19:23 -0400 Subject: Accept string exe with Backend.serialise_executable. Normally, this accepts a build.Executable, but it accept build.BuildTarget and build.CustomTarget as well. Now it will also accept a string path. --- mesonbuild/backend/backends.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 806a6f3..bdba44f 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -179,15 +179,21 @@ class Backend(): def serialise_executable(self, exe, cmd_args, workdir, env={}): import uuid # Can't just use exe.name here; it will likely be run more than once - scratch_file = 'meson_exe_{0}_{1}.dat'.format(exe.name, + if isinstance(exe, (dependencies.ExternalProgram, + build.BuildTarget, build.CustomTarget)): + basename = exe.name + else: + basename = os.path.basename(exe) + scratch_file = 'meson_exe_{0}_{1}.dat'.format(basename, str(uuid.uuid4())[:8]) exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file) with open(exe_data, 'wb') as f: if isinstance(exe, dependencies.ExternalProgram): exe_fullpath = exe.fullpath + elif isinstance(exe, (build.BuildTarget, build.CustomTarget)): + exe_fullpath = [self.get_target_filename_abs(exe)] else: - exe_fullpath = [os.path.join(self.environment.get_build_dir(), - self.get_target_filename(exe))] + exe_fullpath = [exe] is_cross = self.environment.is_cross_build() and \ self.environment.cross_info.need_cross_compiler() and \ self.environment.cross_info.need_exe_wrapper() @@ -199,7 +205,7 @@ class Backend(): extra_paths = self.determine_windows_extra_paths(exe) else: extra_paths = [] - es = ExecutableSerialisation(exe.name, exe_fullpath, cmd_args, env, + es = ExecutableSerialisation(basename, exe_fullpath, cmd_args, env, is_cross, exe_wrapper, workdir, extra_paths) pickle.dump(es, f) -- cgit v1.1 From 70d94a555004563178878b73274e345adff5b4e8 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 24 Aug 2016 04:35:15 -0400 Subject: Allow capturing command output of a custom target. For commands that always output to stdout and don't have a "-o" or "--output" or some other similar option, this 'capture' setting allows the build to capture the result and place it in the output file. --- mesonbuild/backend/backends.py | 8 +++++--- mesonbuild/backend/ninjabackend.py | 10 +++++++--- mesonbuild/build.py | 5 +++++ mesonbuild/scripts/meson_exe.py | 7 ++++++- .../common/117 custom target capture/data_source.txt | 1 + .../common/117 custom target capture/installed_files.txt | 1 + test cases/common/117 custom target capture/meson.build | 16 ++++++++++++++++ .../common/117 custom target capture/my_compiler.py | 13 +++++++++++++ 8 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test cases/common/117 custom target capture/data_source.txt create mode 100644 test cases/common/117 custom target capture/installed_files.txt create mode 100644 test cases/common/117 custom target capture/meson.build create mode 100755 test cases/common/117 custom target capture/my_compiler.py diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index bdba44f..fbc5079 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -37,7 +37,7 @@ class InstallData(): class ExecutableSerialisation(): def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper, - workdir, extra_paths): + workdir, extra_paths, capture): self.name = name self.fname = fname self.cmd_args = cmd_args @@ -46,6 +46,7 @@ class ExecutableSerialisation(): self.exe_runner = exe_wrapper self.workdir = workdir self.extra_paths = extra_paths + self.capture = capture class TestSerialisation: def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env, @@ -176,7 +177,8 @@ class Backend(): raise MesonException('Unknown data type in object list.') return obj_list - def serialise_executable(self, exe, cmd_args, workdir, env={}): + def serialise_executable(self, exe, cmd_args, workdir, env={}, + capture=None): import uuid # Can't just use exe.name here; it will likely be run more than once if isinstance(exe, (dependencies.ExternalProgram, @@ -207,7 +209,7 @@ class Backend(): extra_paths = [] es = ExecutableSerialisation(basename, exe_fullpath, cmd_args, env, is_cross, exe_wrapper, workdir, - extra_paths) + extra_paths, capture) pickle.dump(es, f) return exe_data diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 15f298b..7855729 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -379,15 +379,19 @@ int dummy; tmp = [tmp] for fname in tmp: elem.add_dep(os.path.join(self.get_target_dir(d), fname)) + # If the target requires capturing stdout, then use the serialized + # executable wrapper to capture that output and save it to a file. + # # Windows doesn't have -rpath, so for EXEs that need DLLs built within # the project, we need to set PATH so the DLLs are found. We use # a serialized executable wrapper for that and check if the # CustomTarget command needs extra paths first. - if mesonlib.is_windows() and \ - self.determine_windows_extra_paths(target.command[0]): + if target.capture or (mesonlib.is_windows() and + self.determine_windows_extra_paths(target.command[0])): exe_data = self.serialise_executable(target.command[0], cmd[1:], # All targets are built from the build dir - self.environment.get_build_dir()) + self.environment.get_build_dir(), + capture=ofilenames[0] if target.capture else None) cmd = [sys.executable, self.environment.get_build_command(), '--internal', 'exe', exe_data] cmd_type = 'meson_exe.py custom' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index dd03d81..48d7843 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -927,6 +927,7 @@ class CustomTarget: known_kwargs = {'input' : True, 'output' : True, 'command' : True, + 'capture' : False, 'install' : True, 'install_dir' : True, 'build_always' : True, @@ -982,6 +983,10 @@ class CustomTarget: raise InvalidArguments('Output argument not a string.') if '/' in i: raise InvalidArguments('Output must not contain a path segment.') + self.capture = kwargs.get('capture', False) + if self.capture and len(self.output) != 1: + raise InvalidArguments( + 'Capturing can only output to a single file.') if 'command' not in kwargs: raise InvalidArguments('Missing keyword argument "command".') cmd = kwargs['command'] diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index f075fa0..cdfed09 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -59,6 +59,11 @@ def run_exe(exe): stderr=subprocess.PIPE, env=child_env, cwd=exe.workdir) + stdout, stderr = p.communicate() + if exe.capture and p.returncode == 0: + with open(exe.capture, 'wb') as output: + output.write(stdout) + return p.returncode def run(args): global options @@ -68,7 +73,7 @@ def run(args): print(sys.argv[0] + ' [data file]') exe_data_file = options.args[0] exe = pickle.load(open(exe_data_file, 'rb')) - run_exe(exe) + return run_exe(exe) if __name__ == '__main__': sys.exit(run(sys.argv[1:])) diff --git a/test cases/common/117 custom target capture/data_source.txt b/test cases/common/117 custom target capture/data_source.txt new file mode 100644 index 0000000..0c23cc0 --- /dev/null +++ b/test cases/common/117 custom target capture/data_source.txt @@ -0,0 +1 @@ +This is a text only input file. diff --git a/test cases/common/117 custom target capture/installed_files.txt b/test cases/common/117 custom target capture/installed_files.txt new file mode 100644 index 0000000..d90a6b0 --- /dev/null +++ b/test cases/common/117 custom target capture/installed_files.txt @@ -0,0 +1 @@ +usr/subdir/data.dat diff --git a/test cases/common/117 custom target capture/meson.build b/test cases/common/117 custom target capture/meson.build new file mode 100644 index 0000000..6c19752 --- /dev/null +++ b/test cases/common/117 custom target capture/meson.build @@ -0,0 +1,16 @@ +project('custom target', 'c') + +python = find_program('python3') + +# 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', +capture : true, +command : [python, comp, '@INPUT@'], +install : true, +install_dir : 'subdir' +) diff --git a/test cases/common/117 custom target capture/my_compiler.py b/test cases/common/117 custom target capture/my_compiler.py new file mode 100755 index 0000000..3e9ec23 --- /dev/null +++ b/test cases/common/117 custom target capture/my_compiler.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import sys + +if __name__ == '__main__': + if len(sys.argv) != 2: + print(sys.argv[0], 'input_file') + sys.exit(1) + ifile = open(sys.argv[1]).read() + if ifile != 'This is a text only input file.\n': + print('Malformed input') + sys.exit(1) + print('This is a binary output file.') -- cgit v1.1 From acdcc3ccf2245f7ceddf5addf4df57b3c82476bb Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 24 Aug 2016 15:29:38 -0400 Subject: Add myself to authors.txt. --- authors.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/authors.txt b/authors.txt index e9fceb0..ddccebc 100644 --- a/authors.txt +++ b/authors.txt @@ -41,3 +41,4 @@ Vincent Szolnoky Zhe Wang Wim Taymans Matthias Klumpp +Elliott Sales de Andrade -- cgit v1.1 From 3e09aa9f11e855bc0979d4e0920c1059a4c2e319 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 24 Aug 2016 15:47:07 -0400 Subject: Don't allow @OUTPUT@ when capturing output. --- mesonbuild/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 48d7843..ace4853 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1015,6 +1015,9 @@ class CustomTarget: else: raise InvalidArguments('Argument %s in "command" is invalid.' % i) self.command = final_cmd + if self.capture and '@OUTPUT@' in self.command: + raise InvalidArguments( + '@OUTPUT@ is not allowed when capturing output.') if 'install' in kwargs: self.install = kwargs['install'] if not isinstance(self.install, bool): -- cgit v1.1 From b7757189e4eb8a17182d07cdcad53e8f5ebad0ce Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 25 Aug 2016 00:03:02 -0400 Subject: Echo stderr from captured command. This helps with debugging if the command fails. --- mesonbuild/scripts/meson_exe.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index cdfed09..1a0fcda 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -63,6 +63,8 @@ def run_exe(exe): if exe.capture and p.returncode == 0: with open(exe.capture, 'wb') as output: output.write(stdout) + if stderr: + sys.stderr.buffer.write(stderr) return p.returncode def run(args): -- cgit v1.1