cppcheck/tools/creduce.py

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])