1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
#!/usr/bin/env python3
import argparse
import difflib
import logging
import os
import subprocess
import sys
import tempfile
from functools import reduce
logging.basicConfig(format='%(message)s', level=logging.INFO)
class RoundTripTask(object):
def __init__(self, input_filename, action, swift_syntax_test,
skip_bad_syntax):
assert action == '-round-trip-parse' or action == '-round-trip-lex'
if sys.version_info[0] < 3:
assert isinstance(input_filename, str)
assert isinstance(swift_syntax_test, str)
assert os.path.isfile(input_filename), \
"Input file {} is not accessible!".format(input_filename)
assert os.path.isfile(swift_syntax_test), \
"{} tool is not accessible!".format(swift_syntax_test)
self.input_filename = input_filename
self.action = action
self.swift_syntax_test = swift_syntax_test
self.skip_bad_syntax = skip_bad_syntax
self.returncode = None
self.stdout = None
self.stderr = None
@property
def test_command(self):
return [self.swift_syntax_test, self.action,
'-input-source-filename', self.input_filename]
def run(self):
command = self.test_command
logging.debug(' '.join(command))
self.output_file = tempfile.NamedTemporaryFile('w', delete=False)
self.stderr_file = tempfile.NamedTemporaryFile('w', delete=False)
process = subprocess.Popen(command, stdout=self.output_file,
stderr=self.stderr_file)
process.wait()
self.returncode = process.returncode
self.output_file.close()
self.stderr_file.close()
with open(self.output_file.name, 'rb') as stdout_in:
self.stdout = stdout_in.read()
with open(self.stderr_file.name, 'rb') as stderr_in:
self.stderr = stderr_in.read()
os.remove(self.output_file.name)
os.remove(self.stderr_file.name)
if self.returncode != 0:
if self.skip_bad_syntax:
logging.warning('---===WARNING===--- Lex/parse had error'
' diagnostics, so not diffing. Skipping'
' this file due to -skip-bad-syntax.')
logging.error(' '.join(command))
return None
else:
logging.error('---===ERROR===--- Lex/parse had error'
' diagnostics, so not diffing.')
logging.error(' '.join(command))
logging.error(self.stdout.decode('utf-8', errors='replace'))
logging.error(self.stderr.decode('utf-8', errors='replace'))
raise RuntimeError()
contents = ''.join(map(lambda _: _.decode('utf-8', errors='replace'),
open(self.input_filename, 'rb').readlines()))
stdout_contents = self.stdout.decode('utf-8', errors='replace')
if contents == stdout_contents:
return None
lines = difflib.unified_diff(contents, stdout_contents,
fromfile=self.input_filename,
tofile='-')
diff = '\n'.join(line for line in lines)
return diff if diff else None
def swift_files_in_dir(d):
swift_files = []
for root, dirs, files in os.walk(d):
for basename in files:
if not basename.endswith('.swift'):
continue
abs_file = os.path.abspath(os.path.join(root, basename))
swift_files.append(abs_file)
return swift_files
def run_task(task):
try:
diff = task.run()
if diff is not None:
logging.error('---===ERROR===--- Diff failed!')
logging.error(' '.join(task.test_command))
logging.error(diff)
logging.error('')
return True
except RuntimeError as e:
logging.error(e.message)
return True
return False
def main():
tool_description = '''
Checks for round-trip lex/parse/print compatibility.
Swift's syntax representation should be "full-fidelity", meaning that there is
a perfect representation of what is in the source. When printing a syntax tree
to a file, that file should be identical to the file that was
originally parsed.
This driver invokes swift-syntax-test using -round-trip-lex and
-round-trip-parse on .swift files and .swift files in directories.
'''
parser = argparse.ArgumentParser(description=tool_description)
parser.add_argument('--directory', '-d', action='append',
dest='input_directories', default=[],
help='Add a directory, searching for .swift files'
' within')
parser.add_argument('--file', '-f', action='append',
dest='individual_input_files', default=[],
help='Add an individual file to test')
parser.add_argument('--swift-syntax-test', '-t', required=True,
dest='tool_path',
help='Absolute path to the swift-syntax-test tool')
parser.add_argument('--skip-bad-syntax',
action='store_true',
default=False,
help="Skip files that caused lex or parse diagnostics"
" to be emitted")
args = parser.parse_args()
dir_listings = [swift_files_in_dir(d) for d in args.input_directories]
all_input_files = [filename for dir_listing in dir_listings
for filename in dir_listing]
all_input_files += args.individual_input_files
if sys.version_info[0] < 3:
all_input_files = [f.decode('utf-8') for f in all_input_files]
if len(all_input_files) == 0:
logging.error('No input files!')
sys.exit(1)
if not os.path.isfile(args.tool_path):
raise RuntimeError("Couldn't find swift-syntax-test at {}"
.format(args.tool_path))
lex_tasks = [RoundTripTask(filename, '-round-trip-lex', args.tool_path,
args.skip_bad_syntax)
for filename in all_input_files]
parse_tasks = [RoundTripTask(filename, '-round-trip-parse', args.tool_path,
args.skip_bad_syntax)
for filename in all_input_files]
failed = reduce(lambda a, b: a or b,
map(run_task, lex_tasks + parse_tasks))
sys.exit(1 if failed else 0)
if __name__ == '__main__':
main()
|