108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
|
import argparse, contextlib, multiprocessing, os, tempfile, shutil, subprocess
|
||
|
|
||
|
@contextlib.contextmanager
|
||
|
def mkdtemp():
|
||
|
d = tempfile.mkdtemp()
|
||
|
yield d
|
||
|
shutil.rmtree(d, ignore_errors=True)
|
||
|
|
||
|
def print_lines(lines):
|
||
|
for line in lines:
|
||
|
print(line)
|
||
|
|
||
|
def write_to(file, lines):
|
||
|
content = list((line + "\n" for line in lines))
|
||
|
if (len(content) > 0):
|
||
|
with open(file, 'w') as f:
|
||
|
f.writelines(content)
|
||
|
|
||
|
def make_executable(p):
|
||
|
os.chmod(p, 509)
|
||
|
|
||
|
def quote(s):
|
||
|
text = s.replace("'", "'\"'\"'")
|
||
|
return "'{}'".format(text)
|
||
|
|
||
|
class ScriptBuilder:
|
||
|
def __init__(self):
|
||
|
self.commands = ['#!/bin/bash', '', 'rm -rf .tmp.output', '']
|
||
|
|
||
|
def blank(self):
|
||
|
self.commands.append('')
|
||
|
|
||
|
def add_command(self, cmd, save=False):
|
||
|
if save:
|
||
|
self.commands.append(cmd + ' 2>&1 | tee .tmp.output')
|
||
|
else:
|
||
|
self.commands.append(cmd)
|
||
|
|
||
|
def grep(self, text, file=None):
|
||
|
self.add_command("grep -q -F {} {}".format(quote(text), file or '.tmp.output'))
|
||
|
|
||
|
def check(self, equal_zero=False, result=1):
|
||
|
op = 'eq' if equal_zero else 'ne'
|
||
|
cmds = ['RES=$?',
|
||
|
'if [ $RES -{} "0" ]; then'.format(op),
|
||
|
' exit {}'.format(result),
|
||
|
'fi']
|
||
|
self.commands.extend(cmds)
|
||
|
|
||
|
def write(self, p):
|
||
|
write_to(p, self.commands)
|
||
|
make_executable(p)
|
||
|
|
||
|
def show(self):
|
||
|
print_lines(self.commands)
|
||
|
|
||
|
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument('--file', '-f', help='file to reduce', required=True)
|
||
|
parser.add_argument('--text', '-t', action='append', help='expected output text')
|
||
|
parser.add_argument('--keep', '-k', action='append', help='text to keep in source file')
|
||
|
parser.add_argument('--check', '-c', action='append', help='command to check syntax')
|
||
|
parser.add_argument('--dry-run', '-d', action='store_true', default=False, help='dry run')
|
||
|
parser.add_argument('--timeout', action='store', default=300, help='Interestingness test timeout in seconds')
|
||
|
parser.add_argument('--no-cache', action='store_true', default=False, help="Don't cache behavior of passes")
|
||
|
parser.add_argument('--no-give-up', action='store_true', default=False, help="Don't give up on a pass that hasn't made progress for 50000 iterations")
|
||
|
parser.add_argument('--sllooww', '--slow', action='store_true', default=False, help="Try harder to reduce, but perhaps take a long time to do so")
|
||
|
parser.add_argument('command')
|
||
|
parser.add_argument('args', nargs=argparse.REMAINDER)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
file = os.path.basename(args.file)
|
||
|
sb = ScriptBuilder()
|
||
|
for keep in args.keep or []:
|
||
|
sb.grep(keep, file=file)
|
||
|
sb.check()
|
||
|
sb.blank()
|
||
|
for check in args.check or []:
|
||
|
sb.add_command(check+' '+file)
|
||
|
sb.check()
|
||
|
sb.blank()
|
||
|
command = ' '.join([os.path.abspath(args.command)]+args.args+[file])
|
||
|
sb.add_command(command, save=args.text)
|
||
|
if args.text:
|
||
|
for t in args.text:
|
||
|
sb.grep(t)
|
||
|
sb.check()
|
||
|
else:
|
||
|
sb.check(equal_zero=True)
|
||
|
sb.show()
|
||
|
with mkdtemp() as d:
|
||
|
script = os.path.join(d, 'reduce.sh')
|
||
|
sb.write(script)
|
||
|
if args.dry_run:
|
||
|
shutil.copy(args.file, d)
|
||
|
subprocess.check_call([script], cwd=d)
|
||
|
else:
|
||
|
creduce = ['creduce', '--n', str(multiprocessing.cpu_count())]
|
||
|
creduce.append("--timeout")
|
||
|
creduce.append(str(args.timeout))
|
||
|
if args.no_cache:
|
||
|
creduce.append("--no-cache")
|
||
|
if args.no_give_up:
|
||
|
creduce.append("--no-give-up")
|
||
|
if args.sllooww:
|
||
|
creduce.append("--sllooww")
|
||
|
subprocess.check_call(creduce + [script, args.file])
|