OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright 2014 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 """A tool to scan source files for unneeded grit includes.""" |
| 7 |
| 8 import os |
| 9 import sys |
| 10 import xml.etree.ElementTree |
| 11 |
| 12 IF_ELSE_TAGS = ('if', 'else') |
| 13 |
| 14 |
| 15 def Usage(prog_name): |
| 16 print prog_name, 'GRD_FILE DIRS_TO_SCAN' |
| 17 |
| 18 |
| 19 def GetResourcesForNode(node, parent_file, resource_tag): |
| 20 """Recursively iterate through a node and extract resource names. |
| 21 |
| 22 Args: |
| 23 node: The node to iterate through. |
| 24 parent_file: The file that contains node. |
| 25 resource_tag: The resource tag to extract names from. |
| 26 |
| 27 Returns: |
| 28 A list of resource names. |
| 29 """ |
| 30 resources = [] |
| 31 for child in node.getchildren(): |
| 32 if child.tag == resource_tag: |
| 33 resources.append(child.attrib['name']) |
| 34 elif child.tag in IF_ELSE_TAGS: |
| 35 resources.extend(GetResourcesForNode(child, parent_file, resource_tag)) |
| 36 elif child.tag == 'part': |
| 37 parent_dir = os.path.dirname(parent_file) |
| 38 part_file = os.path.join(parent_dir, child.attrib['file']) |
| 39 part_tree = xml.etree.ElementTree.parse(part_file) |
| 40 part_root = part_tree.getroot() |
| 41 assert part_root.tag == 'grit-part' |
| 42 resources.extend(GetResourcesForNode(part_root, part_file, resource_tag)) |
| 43 else: |
| 44 raise Exception('unknown tag:', child.tag) |
| 45 return resources |
| 46 |
| 47 |
| 48 def FindNodeWithTag(node, tag): |
| 49 """Look through a node's children for a child node with a given tag. |
| 50 |
| 51 Args: |
| 52 root: The node to examine. |
| 53 tag: The tag on a child node to look for. |
| 54 |
| 55 Returns: |
| 56 A child node with the given tag, or None. |
| 57 """ |
| 58 result = None |
| 59 for n in node.getchildren(): |
| 60 if n.tag == tag: |
| 61 assert not result |
| 62 result = n |
| 63 return result |
| 64 |
| 65 |
| 66 def GetResourcesForGrdFile(tree, grd_file): |
| 67 """Find all the message and include resources from a given grit file. |
| 68 |
| 69 Args: |
| 70 tree: The XML tree. |
| 71 grd_file: The file that contains the XML tree. |
| 72 |
| 73 Returns: |
| 74 A list of resource names. |
| 75 """ |
| 76 root = tree.getroot() |
| 77 assert root.tag == 'grit' |
| 78 release_node = FindNodeWithTag(root, 'release') |
| 79 assert release_node != None |
| 80 |
| 81 messages_node = FindNodeWithTag(root, 'messages') |
| 82 messages = set() |
| 83 if messages_node != None: |
| 84 messages = set(GetResourcesForNode(messages_node, grd_file, 'message')) |
| 85 |
| 86 includes_node = FindNodeWithTag(root, 'includes') |
| 87 includes = set() |
| 88 if includes_node != None: |
| 89 includes = set(GetResourcesForNode(includes_node, grd_file, 'include')) |
| 90 return messages.union(includes) |
| 91 |
| 92 |
| 93 def GetOutputFileForNode(node): |
| 94 """Find the output file starting from a given node. |
| 95 |
| 96 Args: |
| 97 node: The root node to scan from. |
| 98 |
| 99 Returns: |
| 100 A grit header file name. |
| 101 """ |
| 102 output_file = None |
| 103 for child in node.getchildren(): |
| 104 if child.tag == 'output': |
| 105 if child.attrib['type'] == 'rc_header': |
| 106 assert output_file is None |
| 107 output_file = child.attrib['filename'] |
| 108 elif child.tag in IF_ELSE_TAGS: |
| 109 child_output_file = GetOutputFileForNode(child) |
| 110 if not child_output_file: |
| 111 continue |
| 112 assert output_file is None |
| 113 output_file = child_output_file |
| 114 else: |
| 115 raise Exception('unknown tag:', child.tag) |
| 116 return output_file |
| 117 |
| 118 |
| 119 def GetOutputHeaderFile(tree): |
| 120 """Find the output file for a given tree. |
| 121 |
| 122 Args: |
| 123 tree: The tree to scan. |
| 124 |
| 125 Returns: |
| 126 A grit header file name. |
| 127 """ |
| 128 root = tree.getroot() |
| 129 assert root.tag == 'grit' |
| 130 output_node = FindNodeWithTag(root, 'outputs') |
| 131 assert output_node != None |
| 132 return GetOutputFileForNode(output_node) |
| 133 |
| 134 |
| 135 def ShouldScanFile(filename): |
| 136 """Return if the filename has one of the extensions below.""" |
| 137 extensions = ['.cc', '.cpp', '.h', '.mm'] |
| 138 file_extension = os.path.splitext(filename)[1] |
| 139 return file_extension in extensions |
| 140 |
| 141 |
| 142 def NeedsGritInclude(grit_header, resources, filename): |
| 143 """Return whether a file needs a given grit header or not. |
| 144 |
| 145 Args: |
| 146 grit_header: The grit header file name. |
| 147 resources: The list of resource names in grit_header. |
| 148 filename: The file to scan. |
| 149 |
| 150 Returns: |
| 151 True if the file should include the grit header. |
| 152 """ |
| 153 with open(filename, 'rb') as f: |
| 154 grit_header_line = grit_header + '"\n' |
| 155 has_grit_header = False |
| 156 while True: |
| 157 line = f.readline() |
| 158 if not line: |
| 159 break |
| 160 if line.endswith(grit_header_line): |
| 161 has_grit_header = True |
| 162 break |
| 163 |
| 164 if not has_grit_header: |
| 165 return True |
| 166 rest_of_the_file = f.read() |
| 167 return any(resource in rest_of_the_file for resource in resources) |
| 168 |
| 169 |
| 170 def main(argv): |
| 171 if len(argv) < 3: |
| 172 Usage(argv[0]) |
| 173 return 1 |
| 174 grd_file = argv[1] |
| 175 dirs_to_scan = argv[2:] |
| 176 for f in dirs_to_scan: |
| 177 if not os.path.exists(f): |
| 178 print 'Error: %s does not exist' % f |
| 179 return 1 |
| 180 |
| 181 tree = xml.etree.ElementTree.parse(grd_file) |
| 182 grit_header = GetOutputHeaderFile(tree) |
| 183 resources = GetResourcesForGrdFile(tree, grd_file) |
| 184 |
| 185 files_with_unneeded_grit_includes = [] |
| 186 for dir_to_scan in dirs_to_scan: |
| 187 for root, dirs, files in os.walk(dir_to_scan): |
| 188 if '.git' in dirs: |
| 189 dirs.remove('.git') |
| 190 full_paths = [os.path.join(root, f) for f in files if ShouldScanFile(f)] |
| 191 files_with_unneeded_grit_includes.extend( |
| 192 [f for f in full_paths |
| 193 if not NeedsGritInclude(grit_header, resources, f)]) |
| 194 if files_with_unneeded_grit_includes: |
| 195 print '\n'.join(files_with_unneeded_grit_includes) |
| 196 return 2 |
| 197 return 0 |
| 198 |
| 199 |
| 200 if __name__ == '__main__': |
| 201 sys.exit(main(sys.argv)) |
OLD | NEW |