diff options
-rw-r--r-- | authors.txt | 1 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 22 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 10 | ||||
-rw-r--r-- | mesonbuild/build.py | 8 | ||||
-rw-r--r-- | mesonbuild/scripts/meson_exe.py | 9 | ||||
-rw-r--r-- | test cases/common/117 custom target capture/data_source.txt | 1 | ||||
-rw-r--r-- | test cases/common/117 custom target capture/installed_files.txt | 1 | ||||
-rw-r--r-- | test cases/common/117 custom target capture/meson.build | 16 | ||||
-rwxr-xr-x | test cases/common/117 custom target capture/my_compiler.py | 13 |
9 files changed, 70 insertions, 11 deletions
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 diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 806a6f3..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,18 +177,25 @@ 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 - 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,9 +207,9 @@ 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) + 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..ace4853 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'] @@ -1010,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): diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index f075fa0..1a0fcda 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -59,6 +59,13 @@ 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) + if stderr: + sys.stderr.buffer.write(stderr) + return p.returncode def run(args): global options @@ -68,7 +75,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.') |