OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """generate_idl_diff.py is a script that generates a diff of two given IDL files . | |
7 Usage: generate_idl_diff.py old_file.json new_file.json diff_file.json | |
8 old_file.json: An input json file including idl data of old Chrome version | |
9 new_file.json: An input json file including idl data of new Chrome version | |
10 diff_file.json: An output json file expressing a diff between old_file.json | |
11 and new_file.json | |
12 """ | |
13 | |
14 import json | |
15 import os | |
16 import sys | |
17 | |
18 | |
19 """Data structure of input files of this script. | |
20 The format of the json files is as follows. Each json file contains multiple | |
21 "interface"s. Each "interface" contains 'ExtAttributes', 'Consts', 'Attributes' | |
22 and 'Operations'. Each item in them are called a "member". | |
23 {'InterfaceName': { | |
Yuki
2015/09/25 03:55:59
nit: Better to add 'Name' key in the example.
shimadaa
2015/09/25 05:13:25
Done.
| |
24 'ExtAttributes': [{'Name': '...'}, | |
25 ..., | |
26 ], | |
27 'Consts': [{'Type': '...', | |
28 'Name': '...', | |
29 'Value': '...' | |
30 }, | |
31 ..., | |
32 ], | |
33 'Attributes': [{'Type': '...', | |
34 'Name': '...', | |
35 'ExtAttributes':[{'Name': '...'}, | |
36 ..., | |
37 ] | |
38 }, | |
39 ..., | |
40 ], | |
41 'Operations': [{'Type': '...', | |
42 'Name': '...', | |
43 'ExtAttributes': [{'Name': '...'}, | |
44 ..., | |
45 ] | |
46 'Arguments': [{'Type': '...', | |
47 'Name': '...'}, | |
48 ..., | |
49 ] | |
50 }, | |
51 ..., | |
52 ] | |
53 }, | |
54 ..., | |
55 } | |
56 """ | |
57 | |
58 | |
59 EXTATTRIBUTES_AND_MEMBER_TYPES = ['ExtAttributes', 'Consts', 'Attributes', 'Oper ations'] | |
60 DIFF_INSENSITIVE_FIELDS = ['Name'] | |
61 DIFF_TAG = 'diff_tag' | |
62 DIFF_TAG_ADDED = 'added' | |
63 DIFF_TAG_DELETED = 'deleted' | |
64 | |
65 | |
66 def load_json_file(filepath): | |
67 """Load a json file into a dictionary. | |
68 Args: | |
69 filepath: A json file path of a json file that we want to load | |
70 Returns: | |
71 An "interfaces" object loaded from the json file | |
72 """ | |
73 with open(filepath, 'r') as f: | |
74 return json.load(f) | |
75 | |
76 | |
77 def members_diff(old_interface, new_interface): | |
78 """Create a diff between two "interface" objects by adding annotations to | |
79 "member" objects that are not common in them. | |
80 Args: | |
81 old_interface: An "interface" object | |
82 new_interface: An "interface" object | |
83 Returns: | |
84 (annotated, is_changed) where | |
85 annotated: An annotated "interface" object | |
86 is_changed: True if two interfaces are not identical, otherwise False | |
87 """ | |
88 annotated = {} | |
89 is_changed = False | |
90 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
91 annotated_members = [] | |
92 unannotated_members = [] | |
93 for member in new_interface[member_type]: | |
94 if member in old_interface[member_type]: | |
95 unannotated_members.append(member) | |
96 old_interface[member_type].remove(member) | |
97 else: | |
98 is_changed = True | |
99 member[DIFF_TAG] = DIFF_TAG_ADDED | |
100 annotated_members.append(member) | |
101 annotated[member_type] = annotated_members | |
102 annotated[member_type].extend(unannotated_members) | |
103 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
104 for member in old_interface[member_type]: | |
105 is_changed = True | |
106 member[DIFF_TAG] = DIFF_TAG_DELETED | |
107 annotated[member_type].extend(old_interface[member_type]) | |
108 for field in DIFF_INSENSITIVE_FIELDS: | |
109 annotated[field] = old_interface[field] | |
110 return (annotated, is_changed) | |
111 | |
112 | |
113 def annotate_all_members(interface, diff_tag): | |
114 """Add annotations to all "member" objects of |interface|. | |
115 Args: | |
116 interface: An "interface" object whose members should be annotated with | |
117 |diff_tag|. | |
118 diff_tag: DIFF_TAG_ADDED or DIFF_TAG_DELETED | |
119 Returns: | |
120 Annotated "interface" object | |
121 """ | |
122 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
123 for member in interface[member_type]: | |
124 member[DIFF_TAG] = diff_tag | |
125 return interface | |
126 | |
127 | |
128 def interfaces_diff(old_interfaces, new_interfaces): | |
129 """Compare two "interfaces" objects and create a diff between them by | |
130 adding annotations (DIFF_TAG_ADDED or DIFF_TAG_DELETED) to each member | |
131 and/or interface. | |
132 Args: | |
133 old_interfaces: An "interfaces" object | |
134 new_interfaces: An "interfaces" object | |
135 Returns: | |
136 An "interfaces" object representing diff between |old_interfaces| and | |
137 |new_interfaces| | |
138 """ | |
139 annotated = {} | |
140 for interface_name, interface in new_interfaces.items(): | |
141 if interface_name in old_interfaces: | |
142 annotated_interface, is_changed = members_diff(old_interfaces[interf ace_name], interface) | |
143 if is_changed: | |
144 annotated[interface_name] = annotated_interface | |
145 del old_interfaces[interface_name] | |
146 else: | |
147 interface = annotate_all_members(interface, DIFF_TAG_ADDED) | |
148 interface[DIFF_TAG] = DIFF_TAG_ADDED | |
149 annotated[interface_name] = interface | |
150 for interface_name, interface in old_interfaces.items(): | |
151 interface = annotate_all_members(interface, DIFF_TAG_DELETED) | |
152 interface[DIFF_TAG] = DIFF_TAG_DELETED | |
153 annotated.update(old_interfaces) | |
154 return annotated | |
155 | |
156 | |
157 def write_diff(diff, filepath): | |
158 """Write a diff dictionary to a json file. | |
159 Args: | |
160 diff: An "interfaces" object that represents a diff | |
161 filepath: An output file path | |
162 """ | |
163 with open(filepath, 'w') as f: | |
164 json.dump(diff, f, indent=4) | |
165 | |
166 | |
167 def main(argv): | |
168 if len(argv) != 3: | |
169 sys.stdout.write( | |
170 'Usage: make_diff.py <old_file.json> <new_file.json> ' | |
171 '<diff_file.jsson>\n') | |
172 exit(1) | |
173 old_json_file = argv[0] | |
174 new_json_file = argv[1] | |
175 output_file = argv[2] | |
176 old_interfaces = load_json_file(old_json_file) | |
177 new_interfaces = load_json_file(new_json_file) | |
178 diff = interfaces_diff(old_interfaces, new_interfaces) | |
179 write_diff(diff, output_file) | |
180 | |
181 | |
182 if __name__ == '__main__': | |
183 main(sys.argv[1:]) | |
OLD | NEW |