blob: 791d0d104db40baede030437b7c461d79bbac2c9 [file] [log] [blame] [edit]
#!/usr/bin/env python3
#
# Copyright 2016 WebAssembly Community Group participants
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import contextlib
import os
import json
import shutil
import signal
import subprocess
import sys
import tempfile
# Get signal names from numbers in Python
# http://stackoverflow.com/a/2549950
SIGNAMES = dict((k, v) for v, k in reversed(sorted(signal.__dict__.items()))
if v.startswith('SIG') and not v.startswith('SIG_'))
class Error(Exception):
pass
class Executable(object):
def __init__(self, exe, *before_args, **kwargs):
self.exe = exe
self.before_args = list(before_args)
self.after_args = []
self.basename = kwargs.get('basename',
os.path.basename(exe)).replace('.exe', '')
self.error_cmdline = kwargs.get('error_cmdline', True)
self.stdout_handle = self._ForwardHandle(kwargs.get('forward_stdout'))
self.stderr_handle = self._ForwardHandle(kwargs.get('forward_stderr'))
self.verbose = False
def _ForwardHandle(self, forward):
return None if forward else subprocess.PIPE
def _RunWithArgsInternal(self, *args, **kwargs):
cmd = [self.exe] + self.before_args + list(args) + self.after_args
cmd_str = ' '.join(cmd)
if self.verbose:
print(cmd_str)
if self.error_cmdline:
err_cmd_str = cmd_str.replace('.exe', '')
else:
err_cmd_str = self.basename
stdout = ''
stderr = ''
error = None
try:
process = subprocess.run(cmd, check=False, text=True,
stdout=self.stdout_handle,
stderr=self.stderr_handle, **kwargs)
stdout = process.stdout
stderr = process.stderr
if process.returncode < 0:
# Terminated by signal
signame = SIGNAMES.get(-process.returncode, '<unknown>')
error = Error('Signal raised running "%s": %s\n%s' % (err_cmd_str,
signame, stderr))
elif process.returncode > 0:
error = Error('Error running "%s" (%d):\n%s' % (err_cmd_str, process.returncode, stderr))
except OSError as e:
error = Error('Error running "%s": %s' % (err_cmd_str, str(e)))
return stdout, stderr, error
def RunWithArgsForStdout(self, *args, **kwargs):
stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs)
if error:
raise error
return stdout
def RunWithArgs(self, *args, **kwargs):
stdout, stderr, error = self._RunWithArgsInternal(*args, **kwargs)
if stdout:
sys.stdout.write(stdout)
if error:
raise error
def AppendArg(self, arg):
self.after_args.append(arg)
def AppendOptionalArgs(self, option_dict):
for option, value in option_dict.items():
if value:
if value is True:
self.AppendArg(option)
else:
self.AppendArg('%s=%s' % (option, value))
@contextlib.contextmanager
def TempDirectory(out_dir, prefix=None):
if out_dir:
out_dir_is_temp = False
if not os.path.exists(out_dir):
os.makedirs(out_dir)
else:
out_dir = tempfile.mkdtemp(prefix=prefix)
out_dir_is_temp = True
try:
yield out_dir
finally:
if out_dir_is_temp:
shutil.rmtree(out_dir)
def ChangeExt(path, new_ext):
return os.path.splitext(path)[0] + new_ext
def ChangeDir(path, new_dir):
return os.path.join(new_dir, os.path.basename(path))
def Hexdump(data):
DUMP_OCTETS_PER_LINE = 16
DUMP_OCTETS_PER_GROUP = 2
p = 0
end = len(data)
lines = []
while p < end:
line_start = p
line_end = p + DUMP_OCTETS_PER_LINE
line = '%07x: ' % p
while p < line_end:
for i in range(DUMP_OCTETS_PER_GROUP):
if p < end:
line += '%02x' % data[p]
else:
line += ' '
p += 1
line += ' '
line += ' '
p = line_start
for i in range(DUMP_OCTETS_PER_LINE):
if p >= end:
break
x = data[p]
if x >= 32 and x < 0x7f:
line += '%c' % x
else:
line += '.'
p += 1
line += '\n'
lines.append(line)
return lines
def GetModuleFilenamesFromSpecJSON(json_filename):
with open(json_filename) as json_file:
json_data = json.load(json_file)
return [m['filename'] for m in json_data['commands'] if 'filename' in m]