#!/usr/bin/env python from collections import OrderedDict from sets import Set from shutil import copyfile import argparse import json import os import pprint import re import subprocess 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 main(): parser = argparse.ArgumentParser() parser.add_argument("--clang", help="The clang binary (could be a relative or absolute path)", action="store", required=True) parser.add_argument("--opts", help="other options", action="store", default='', type=str) parser.add_argument("--source", help="the source file. Command used to generate the json will be of the format -cc1 -ast-dump=json ", action="store", required=True) parser.add_argument("--filters", help="comma separated list of AST filters. Ex: --filters=TypedefDecl,BuiltinType", action="store", default='') args = parser.parse_args() if not args.source: print("Specify the source file to give to clang.") return -1 clang_binary = os.path.abspath(args.clang) if not os.path.isfile(clang_binary): print("clang binary specified not present.") return -1 options = args.opts.split(' ') filters = Set(args.filters.split(',')) if args.filters else Set([]) note = "// NOTE: CHECK lines have been autogenerated by " \ "gen_ast_dump_json_test.py" if (args.filters): note += "\n// using --filters=" + args.filters cmd = [clang_binary, "-cc1"] cmd.extend(options) using_ast_dump_filter = 'ast-dump-filter' in args.opts cmd.extend(["-ast-dump=json", args.source]) 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) partition = args.source.rpartition('.') dest_path = '%s-json%s%s' % (partition[0], partition[1], partition[2]) print("Writing json appended source file to %s." %(dest_path)) copyfile(args.source, dest_path) with open(dest_path, "a") as f: f.write("\n" + 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) index += 1 else: out_str += '// CHECK-NEXT: %s\n' %(append_line) f.write(out_str) return 0 if __name__ == '__main__': main()