blob: 06213e03a3e4c994da1891ee06336c4cf104ebd8 [file] [edit]
#
# 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 json
import os
import shlex
import shutil
import signal
import subprocess
import sys
import tempfile
# Get signal names from numbers in Python
# http://stackoverflow.com/a/2549950
SIGNAMES = {
k: v for v, k in sorted(signal.__dict__.items(), reverse=True)
if v.startswith('SIG') and not v.startswith('SIG_')
}
class Error(Exception):
pass
class Executable:
def __init__(self, exe, *after_args, **kwargs):
self.exe = exe
self.after_args = list(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] + list(args) + self.after_args
cmd_str = shlex.join(cmd)
if self.verbose:
print(cmd_str)
if self.error_cmdline:
err_cmd_str = cmd_str
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(f'Signal raised running "{err_cmd_str}": {signame}\n{stderr}')
elif process.returncode > 0:
error = Error(f'Error running "{err_cmd_str}" ({process.returncode}):\n{stdout}\n{stderr}')
except OSError as e:
error = Error(f'Error running "{err_cmd_str}": {e!s}')
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(f'{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 = f'{p:07x}: '
while p < line_end:
for i in range(DUMP_OCTETS_PER_GROUP):
if p < end:
line += f'{data[p]:02x}'
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 += f'{x:c}'
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]