3 from __future__
import print_function
4 from collections
import OrderedDict
5 from shutil
import copyfile
15 def normalize(dict_var
):
16 for k
, v
in dict_var
.items():
17 if isinstance(v
, OrderedDict
):
19 elif isinstance(v
, list):
21 if isinstance(e
, OrderedDict
):
24 if v
!= "0x0" and re
.match(r
"0x[0-9A-Fa-f]+", v
):
25 dict_var
[k
] = "0x{{.*}}"
26 elif os
.path
.isfile(v
):
27 dict_var
[k
] = "{{.*}}"
32 inner_splits
= split
.rsplit(":", 2)
33 if os
.path
.isfile(inner_splits
[0]):
35 "{{.*}}:%s:%s" % (inner_splits
[1], inner_splits
[2])
38 out_splits
.append(split
)
40 dict_var
[k
] = " ".join(out_splits
)
43 def filter_json(dict_var
, filters
, out
):
44 for k
, v
in dict_var
.items():
49 elif isinstance(v
, OrderedDict
):
50 filter_json(v
, filters
, out
)
51 elif isinstance(v
, list):
53 if isinstance(e
, OrderedDict
):
54 filter_json(e
, filters
, out
)
57 def default_clang_path():
58 guessed_clang
= os
.path
.join(os
.path
.dirname(__file__
), "clang")
59 if os
.path
.isfile(guessed_clang
):
65 parser
= argparse
.ArgumentParser()
68 help="The clang binary (could be a relative or absolute path)",
70 default
=default_clang_path(),
74 help="the source file(s). Without --update, the command used to generate the JSON "
75 "will be of the format <clang> -cc1 -ast-dump=json <opts> <source>",
77 nargs
=argparse
.ONE_OR_MORE
,
82 help="comma separated list of AST filters. Ex: --filters=TypedefDecl,BuiltinType",
86 update_or_generate_group
= parser
.add_mutually_exclusive_group()
87 update_or_generate_group
.add_argument(
88 "--update", help="Update the file in-place", action
="store_true"
90 update_or_generate_group
.add_argument(
91 "--opts", help="other options", action
="store", default
="", type=str
95 help="When using --update, also update files that do not have the "
96 "autogenerated disclaimer",
99 args
= parser
.parse_args()
102 sys
.exit("Specify the source file to give to clang.")
104 clang_binary
= os
.path
.abspath(args
.clang
)
105 if not os
.path
.isfile(clang_binary
):
106 sys
.exit("clang binary specified not present.")
108 for src
in args
.source
:
112 cmdline_filters
=args
.filters
,
113 cmdline_opts
=args
.opts
,
114 do_update
=args
.update
,
115 force_update
=args
.update_manual
,
120 source_file
, clang_binary
, cmdline_filters
, cmdline_opts
, do_update
, force_update
123 "// NOTE: CHECK lines have been autogenerated by " "gen_ast_dump_json_test.py"
125 filters_line_prefix
= "// using --filters="
126 note
= note_firstline
128 cmd
= [clang_binary
, "-cc1"]
130 # When updating the first line of the test must be a RUN: line
131 with
open(source_file
, "r") as srcf
:
132 first_line
= srcf
.readline()
133 found_autogenerated_line
= False
135 for i
, line
in enumerate(srcf
.readlines()):
136 if found_autogenerated_line
:
137 # print("Filters line: '", line.rstrip(), "'", sep="")
138 if line
.startswith(filters_line_prefix
):
139 filters_line
= line
[len(filters_line_prefix
) :].rstrip()
141 if line
.startswith(note_firstline
):
142 found_autogenerated_line
= True
143 # print("Found autogenerated disclaimer at line", i + 1)
144 if not found_autogenerated_line
and not force_update
:
148 "since it is not autogenerated.",
152 if not cmdline_filters
and filters_line
:
153 cmdline_filters
= filters_line
154 print("Inferred filters as '" + cmdline_filters
+ "'")
156 if "RUN: %clang_cc1 " not in first_line
:
158 "When using --update the first line of the input file must contain RUN: %clang_cc1"
160 clang_start
= first_line
.find("%clang_cc1") + len("%clang_cc1")
161 file_check_idx
= first_line
.rfind("| FileCheck")
163 dump_cmd
= first_line
[clang_start
:file_check_idx
]
165 dump_cmd
= first_line
[clang_start
:]
166 print("Inferred run arguments as '", dump_cmd
, "'", sep
="")
167 options
= dump_cmd
.split()
168 if "-ast-dump=json" not in options
:
169 sys
.exit("ERROR: RUN: line does not contain -ast-dump=json")
170 if "%s" not in options
:
171 sys
.exit("ERROR: RUN: line does not contain %s")
174 options
= cmdline_opts
.split()
175 options
.append("-ast-dump=json")
177 using_ast_dump_filter
= any("ast-dump-filter" in arg
for arg
in cmd
)
178 cmd
.append(source_file
)
179 print("Will run", cmd
)
182 note
+= "\n" + filters_line_prefix
+ cmdline_filters
183 filters
= set(cmdline_filters
.split(","))
184 print("Will use the following filters:", filters
)
187 json_str
= subprocess
.check_output(cmd
).decode()
188 except Exception as ex
:
189 print("The clang command failed with %s" % ex
)
193 if using_ast_dump_filter
:
194 # If we're using a filter, then we might have multiple JSON objects
195 # in the output. To parse each out, we use a manual JSONDecoder in
196 # "raw" mode and update our location in the string based on where the
197 # last document ended.
198 decoder
= json
.JSONDecoder(object_hook
=OrderedDict
)
204 (j
, doc_start
) = decoder
.raw_decode(json_str
[doc_start
:])
205 doc_start
+= prev_end
+ 1
211 j
= json
.loads(json_str
, object_pairs_hook
=OrderedDict
)
214 if len(filters
) == 0:
217 filter_json(j
, filters
, out_asts
)
219 with tempfile
.NamedTemporaryFile("w", delete
=False) as f
:
220 with
open(source_file
, "r") as srcf
:
221 for line
in srcf
.readlines():
222 # copy up to the note:
223 if line
.rstrip() == note_firstline
:
227 for out_ast
in out_asts
:
228 append_str
= json
.dumps(out_ast
, indent
=1, ensure_ascii
=False)
230 out_str
+= "// CHECK-NOT: {{^}}Dumping\n"
232 for append_line
in append_str
.splitlines()[2:]:
234 out_str
+= "// CHECK: %s\n" % (append_line
.rstrip())
237 out_str
+= "// CHECK-NEXT: %s\n" % (append_line
.rstrip())
243 print("Updating json appended source file to %s." % source_file
)
244 copyfile(f
.name
, source_file
)
246 partition
= source_file
.rpartition(".")
247 dest_path
= "%s-json%s%s" % (partition
[0], partition
[1], partition
[2])
248 print("Writing json appended source file to %s." % dest_path
)
249 copyfile(f
.name
, dest_path
)
254 if __name__
== "__main__":