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': { |
| 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 'Name': '...' |
| 54 }, |
| 55 ..., |
| 56 } |
| 57 """ |
| 58 |
| 59 |
| 60 EXTATTRIBUTES_AND_MEMBER_TYPES = ['ExtAttributes', 'Consts', 'Attributes', 'Oper
ations'] |
| 61 DIFF_INSENSITIVE_FIELDS = ['Name'] |
| 62 DIFF_TAG = 'diff_tag' |
| 63 DIFF_TAG_ADDED = 'added' |
| 64 DIFF_TAG_DELETED = 'deleted' |
| 65 |
| 66 |
| 67 def load_json_file(filepath): |
| 68 """Load a json file into a dictionary. |
| 69 Args: |
| 70 filepath: A json file path of a json file that we want to load |
| 71 Returns: |
| 72 An "interfaces" object loaded from the json file |
| 73 """ |
| 74 with open(filepath, 'r') as f: |
| 75 return json.load(f) |
| 76 |
| 77 |
| 78 def members_diff(old_interface, new_interface): |
| 79 """Create a diff between two "interface" objects by adding annotations to |
| 80 "member" objects that are not common in them. |
| 81 Args: |
| 82 old_interface: An "interface" object |
| 83 new_interface: An "interface" object |
| 84 Returns: |
| 85 (annotated, is_changed) where |
| 86 annotated: An annotated "interface" object |
| 87 is_changed: True if two interfaces are not identical, otherwise False |
| 88 """ |
| 89 annotated = {} |
| 90 is_changed = False |
| 91 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| 92 annotated_members = [] |
| 93 unannotated_members = [] |
| 94 for member in new_interface[member_type]: |
| 95 if member in old_interface[member_type]: |
| 96 unannotated_members.append(member) |
| 97 old_interface[member_type].remove(member) |
| 98 else: |
| 99 is_changed = True |
| 100 member[DIFF_TAG] = DIFF_TAG_ADDED |
| 101 annotated_members.append(member) |
| 102 annotated[member_type] = annotated_members |
| 103 annotated[member_type].extend(unannotated_members) |
| 104 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| 105 for member in old_interface[member_type]: |
| 106 is_changed = True |
| 107 member[DIFF_TAG] = DIFF_TAG_DELETED |
| 108 annotated[member_type].extend(old_interface[member_type]) |
| 109 for field in DIFF_INSENSITIVE_FIELDS: |
| 110 annotated[field] = old_interface[field] |
| 111 return (annotated, is_changed) |
| 112 |
| 113 |
| 114 def annotate_all_members(interface, diff_tag): |
| 115 """Add annotations to all "member" objects of |interface|. |
| 116 Args: |
| 117 interface: An "interface" object whose members should be annotated with |
| 118 |diff_tag|. |
| 119 diff_tag: DIFF_TAG_ADDED or DIFF_TAG_DELETED |
| 120 Returns: |
| 121 Annotated "interface" object |
| 122 """ |
| 123 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| 124 for member in interface[member_type]: |
| 125 member[DIFF_TAG] = diff_tag |
| 126 return interface |
| 127 |
| 128 |
| 129 def interfaces_diff(old_interfaces, new_interfaces): |
| 130 """Compare two "interfaces" objects and create a diff between them by |
| 131 adding annotations (DIFF_TAG_ADDED or DIFF_TAG_DELETED) to each member |
| 132 and/or interface. |
| 133 Args: |
| 134 old_interfaces: An "interfaces" object |
| 135 new_interfaces: An "interfaces" object |
| 136 Returns: |
| 137 An "interfaces" object representing diff between |old_interfaces| and |
| 138 |new_interfaces| |
| 139 """ |
| 140 annotated = {} |
| 141 for interface_name, interface in new_interfaces.items(): |
| 142 if interface_name in old_interfaces: |
| 143 annotated_interface, is_changed = members_diff(old_interfaces[interf
ace_name], interface) |
| 144 if is_changed: |
| 145 annotated[interface_name] = annotated_interface |
| 146 del old_interfaces[interface_name] |
| 147 else: |
| 148 interface = annotate_all_members(interface, DIFF_TAG_ADDED) |
| 149 interface[DIFF_TAG] = DIFF_TAG_ADDED |
| 150 annotated[interface_name] = interface |
| 151 for interface_name, interface in old_interfaces.items(): |
| 152 interface = annotate_all_members(interface, DIFF_TAG_DELETED) |
| 153 interface[DIFF_TAG] = DIFF_TAG_DELETED |
| 154 annotated.update(old_interfaces) |
| 155 return annotated |
| 156 |
| 157 |
| 158 def write_diff(diff, filepath): |
| 159 """Write a diff dictionary to a json file. |
| 160 Args: |
| 161 diff: An "interfaces" object that represents a diff |
| 162 filepath: An output file path |
| 163 """ |
| 164 with open(filepath, 'w') as f: |
| 165 json.dump(diff, f, indent=4) |
| 166 |
| 167 |
| 168 def main(argv): |
| 169 if len(argv) != 3: |
| 170 sys.stdout.write( |
| 171 'Usage: make_diff.py <old_file.json> <new_file.json> ' |
| 172 '<diff_file.json>\n') |
| 173 exit(1) |
| 174 old_json_file = argv[0] |
| 175 new_json_file = argv[1] |
| 176 output_file = argv[2] |
| 177 old_interfaces = load_json_file(old_json_file) |
| 178 new_interfaces = load_json_file(new_json_file) |
| 179 diff = interfaces_diff(old_interfaces, new_interfaces) |
| 180 write_diff(diff, output_file) |
| 181 |
| 182 |
| 183 if __name__ == '__main__': |
| 184 main(sys.argv[1:]) |
OLD | NEW |