Files
clang-p2996/clang/test/AST/gen_ast_dump_json_test.py
Aaron Ballman bf830b01a2 Switch to opening the temp file in binary mode
This corrects an issue where the script would write the file with the
incorrect line endings on Windows.
2019-12-03 15:31:46 -05:00

219 lines
8.5 KiB
Python

#!/usr/bin/env python
from __future__ import print_function
from collections import OrderedDict
from shutil import copyfile
import argparse
import json
import os
import pprint
import re
import subprocess
import sys
import tempfile
def normalize(dict_var):
for k, v in dict_var.items():
if isinstance(v, OrderedDict):
normalize(v)
elif isinstance(v, list):
for e in v:
if isinstance(e, OrderedDict):
normalize(e)
elif type(v) is unicode:
st = v.encode('utf-8')
if v != "0x0" and re.match(r"0x[0-9A-Fa-f]+", v):
dict_var[k] = u'0x{{.*}}'
elif os.path.isfile(v):
dict_var[k] = u'{{.*}}'
else:
splits = (v.split(u' '))
out_splits = []
for split in splits:
inner_splits = split.rsplit(u':',2)
if os.path.isfile(inner_splits[0]):
out_splits.append(
u'{{.*}}:%s:%s'
%(inner_splits[1],
inner_splits[2]))
continue
out_splits.append(split)
dict_var[k] = ' '.join(out_splits)
def filter_json(dict_var, filters, out):
for k, v in dict_var.items():
if type(v) is unicode:
st = v.encode('utf-8')
if st in filters:
out.append(dict_var)
break
elif isinstance(v, OrderedDict):
filter_json(v, filters, out)
elif isinstance(v, list):
for e in v:
if isinstance(e, OrderedDict):
filter_json(e, filters, out)
def default_clang_path():
guessed_clang = os.path.join(os.path.dirname(__file__), "clang")
if os.path.isfile(guessed_clang):
return guessed_clang
return None
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--clang", help="The clang binary (could be a relative or absolute path)",
action="store", default=default_clang_path())
parser.add_argument("--source", help="the source file(s). Without --update, the command used to generate the JSON "
"will be of the format <clang> -cc1 -ast-dump=json <opts> <source>",
action="store", nargs=argparse.ONE_OR_MORE, required=True)
parser.add_argument("--filters", help="comma separated list of AST filters. Ex: --filters=TypedefDecl,BuiltinType",
action="store", default='')
update_or_generate_group = parser.add_mutually_exclusive_group()
update_or_generate_group.add_argument("--update", help="Update the file in-place", action="store_true")
update_or_generate_group.add_argument("--opts", help="other options",
action="store", default='', type=str)
parser.add_argument("--update-manual", help="When using --update, also update files that do not have the "
"autogenerated disclaimer", action="store_true")
args = parser.parse_args()
if not args.source:
sys.exit("Specify the source file to give to clang.")
clang_binary = os.path.abspath(args.clang)
if not os.path.isfile(clang_binary):
sys.exit("clang binary specified not present.")
for src in args.source:
process_file(src, clang_binary, cmdline_filters=args.filters,
cmdline_opts=args.opts, do_update=args.update,
force_update=args.update_manual)
def process_file(source_file, clang_binary, cmdline_filters, cmdline_opts,
do_update, force_update):
note_firstline = "// NOTE: CHECK lines have been autogenerated by " \
"gen_ast_dump_json_test.py"
filters_line_prefix = "// using --filters="
note = note_firstline
cmd = [clang_binary, "-cc1"]
if do_update:
# When updating the first line of the test must be a RUN: line
with open(source_file, "r") as srcf:
first_line = srcf.readline()
found_autogenerated_line = False
filters_line = None
for i, line in enumerate(srcf.readlines()):
if found_autogenerated_line:
# print("Filters line: '", line.rstrip(), "'", sep="")
if line.startswith(filters_line_prefix):
filters_line = line[len(filters_line_prefix):].rstrip()
break
if line.startswith(note_firstline):
found_autogenerated_line = True
# print("Found autogenerated disclaimer at line", i + 1)
if not found_autogenerated_line and not force_update:
print("Not updating", source_file, "since it is not autogenerated.",
file=sys.stderr)
return
if not cmdline_filters and filters_line:
cmdline_filters = filters_line
print("Inferred filters as '" + cmdline_filters + "'")
if "RUN: %clang_cc1 " not in first_line:
sys.exit("When using --update the first line of the input file must contain RUN: %clang_cc1")
clang_start = first_line.find("%clang_cc1") + len("%clang_cc1")
file_check_idx = first_line.rfind("| FileCheck")
if file_check_idx:
dump_cmd = first_line[clang_start:file_check_idx]
else:
dump_cmd = first_line[clang_start:]
print("Inferred run arguments as '", dump_cmd, "'", sep="")
options = dump_cmd.split()
if "-ast-dump=json" not in options:
sys.exit("ERROR: RUN: line does not contain -ast-dump=json")
if "%s" not in options:
sys.exit("ERROR: RUN: line does not contain %s")
options.remove("%s")
else:
options = cmdline_opts.split()
options.append("-ast-dump=json")
cmd.extend(options)
using_ast_dump_filter = any('ast-dump-filter' in arg for arg in cmd)
cmd.append(source_file)
print("Will run", cmd)
filters = set()
if cmdline_filters:
note += "\n" + filters_line_prefix + cmdline_filters
filters = set(cmdline_filters.split(','))
print("Will use the following filters:", filters)
try:
json_str = subprocess.check_output(cmd)
except Exception as ex:
print("The clang command failed with %s" % ex)
return -1
out_asts = []
if using_ast_dump_filter:
splits = re.split('Dumping .*:\n', json_str)
if len(splits) > 1:
for split in splits[1:]:
j = json.loads(split.decode('utf-8'), object_pairs_hook=OrderedDict)
normalize(j)
out_asts.append(j)
else:
j = json.loads(json_str.decode('utf-8'), object_pairs_hook=OrderedDict)
normalize(j)
if len(filters) == 0:
out_asts.append(j)
else:
#assert using_ast_dump_filter is False,\
# "Does not support using compiler's ast-dump-filter "\
# "and the tool's filter option at the same time yet."
filter_json(j, filters, out_asts)
with tempfile.NamedTemporaryFile("wb", delete=False) as f:
with open(source_file, "r") as srcf:
for line in srcf.readlines():
# copy up to the note:
if line.rstrip() == note_firstline:
break
f.write(line)
f.write(note + "\n")
for out_ast in out_asts:
append_str = json.dumps(out_ast, indent=1, ensure_ascii=False)
out_str = '\n\n'
index = 0
for append_line in append_str.splitlines()[2:]:
if index == 0:
out_str += '// CHECK: %s\n' %(append_line.rstrip())
index += 1
else:
out_str += '// CHECK-NEXT: %s\n' %(append_line.rstrip())
f.write(out_str)
f.flush()
f.close()
if do_update:
print("Updating json appended source file to %s." % source_file)
copyfile(f.name, source_file)
else:
partition = source_file.rpartition('.')
dest_path = '%s-json%s%s' % (partition[0], partition[1], partition[2])
print("Writing json appended source file to %s." % dest_path)
copyfile(f.name, dest_path)
os.remove(f.name)
return 0
if __name__ == '__main__':
main()