1# Copyright 2013-2016 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import os 16import sys 17import argparse 18import pickle 19import platform 20import subprocess 21 22from .. import mesonlib 23from ..backend.backends import ExecutableSerialisation 24 25options = None 26 27def buildparser(): 28 parser = argparse.ArgumentParser(description='Custom executable wrapper for Meson. Do not run on your own, mmm\'kay?') 29 parser.add_argument('--unpickle') 30 parser.add_argument('--capture') 31 return parser 32 33def is_windows(): 34 platname = platform.system().lower() 35 return platname == 'windows' or 'mingw' in platname 36 37def is_cygwin(): 38 platname = platform.system().lower() 39 return 'cygwin' in platname 40 41def run_exe(exe): 42 if exe.exe_runner: 43 if not exe.exe_runner.found(): 44 raise AssertionError('BUG: Can\'t run cross-compiled exe {!r} with not-found ' 45 'wrapper {!r}'.format(exe.cmd_args[0], exe.exe_runner.get_path())) 46 cmd_args = exe.exe_runner.get_command() + exe.cmd_args 47 else: 48 cmd_args = exe.cmd_args 49 child_env = os.environ.copy() 50 child_env.update(exe.env) 51 if exe.extra_paths: 52 child_env['PATH'] = (os.pathsep.join(exe.extra_paths + ['']) + 53 child_env['PATH']) 54 if exe.exe_runner and mesonlib.substring_is_in_list('wine', exe.exe_runner.get_command()): 55 child_env['WINEPATH'] = mesonlib.get_wine_shortpath( 56 exe.exe_runner.get_command(), 57 ['Z:' + p for p in exe.extra_paths] + child_env.get('WINEPATH', '').split(';') 58 ) 59 60 p = subprocess.Popen(cmd_args, env=child_env, cwd=exe.workdir, 61 close_fds=False, 62 stdout=subprocess.PIPE, 63 stderr=subprocess.PIPE) 64 stdout, stderr = p.communicate() 65 66 if p.returncode == 0xc0000135: 67 # STATUS_DLL_NOT_FOUND on Windows indicating a common problem that is otherwise hard to diagnose 68 raise FileNotFoundError('Missing DLLs on calling {!r}'.format(exe.name)) 69 70 if exe.capture and p.returncode == 0: 71 skip_write = False 72 try: 73 with open(exe.capture, 'rb') as cur: 74 skip_write = cur.read() == stdout 75 except IOError: 76 pass 77 if not skip_write: 78 with open(exe.capture, 'wb') as output: 79 output.write(stdout) 80 else: 81 sys.stdout.buffer.write(stdout) 82 if stderr: 83 sys.stderr.buffer.write(stderr) 84 return p.returncode 85 86def run(args): 87 global options 88 parser = buildparser() 89 options, cmd_args = parser.parse_known_args(args) 90 # argparse supports double dash to separate options and positional arguments, 91 # but the user has to remove it manually. 92 if cmd_args and cmd_args[0] == '--': 93 cmd_args = cmd_args[1:] 94 if not options.unpickle and not cmd_args: 95 parser.error('either --unpickle or executable and arguments are required') 96 if options.unpickle: 97 if cmd_args or options.capture: 98 parser.error('no other arguments can be used with --unpickle') 99 with open(options.unpickle, 'rb') as f: 100 exe = pickle.load(f) 101 else: 102 exe = ExecutableSerialisation(cmd_args, capture=options.capture) 103 104 return run_exe(exe) 105 106if __name__ == '__main__': 107 sys.exit(run(sys.argv[1:])) 108