| 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 resources = set() | |
| 82 for node_type in ('message', 'include', 'structure'): | |
| 83 resources_node = FindNodeWithTag(release_node, node_type + 's') | |
| 84 if resources_node != None: | |
| 85 resources = resources.union( | |
| 86 set(GetResourcesForNode(resources_node, grd_file, node_type))) | |
| 87 return resources | |
| 88 | |
| 89 | |
| 90 def GetOutputFileForNode(node): | |
| 91 """Find the output file starting from a given node. | |
| 92 | |
| 93 Args: | |
| 94 node: The root node to scan from. | |
| 95 | |
| 96 Returns: | |
| 97 A grit header file name. | |
| 98 """ | |
| 99 output_file = None | |
| 100 for child in node.getchildren(): | |
| 101 if child.tag == 'output': | |
| 102 if child.attrib['type'] == 'rc_header': | |
| 103 assert output_file is None | |
| 104 output_file = child.attrib['filename'] | |
| 105 elif child.tag in IF_ELSE_TAGS: | |
| 106 child_output_file = GetOutputFileForNode(child) | |
| 107 if not child_output_file: | |
| 108 continue | |
| 109 assert output_file is None | |
| 110 output_file = child_output_file | |
| 111 else: | |
| 112 raise Exception('unknown tag:', child.tag) | |
| 113 return output_file | |
| 114 | |
| 115 | |
| 116 def GetOutputHeaderFile(tree): | |
| 117 """Find the output file for a given tree. | |
| 118 | |
| 119 Args: | |
| 120 tree: The tree to scan. | |
| 121 | |
| 122 Returns: | |
| 123 A grit header file name. | |
| 124 """ | |
| 125 root = tree.getroot() | |
| 126 assert root.tag == 'grit' | |
| 127 output_node = FindNodeWithTag(root, 'outputs') | |
| 128 assert output_node != None | |
| 129 return GetOutputFileForNode(output_node) | |
| 130 | |
| 131 | |
| 132 def ShouldScanFile(filename): | |
| 133 """Return if the filename has one of the extensions below.""" | |
| 134 extensions = ['.cc', '.cpp', '.h', '.mm'] | |
| 135 file_extension = os.path.splitext(filename)[1] | |
| 136 return file_extension in extensions | |
| 137 | |
| 138 | |
| 139 def NeedsGritInclude(grit_header, resources, filename): | |
| 140 """Return whether a file needs a given grit header or not. | |
| 141 | |
| 142 Args: | |
| 143 grit_header: The grit header file name. | |
| 144 resources: The list of resource names in grit_header. | |
| 145 filename: The file to scan. | |
| 146 | |
| 147 Returns: | |
| 148 True if the file should include the grit header. | |
| 149 """ | |
| 150 # A list of special keywords that implies the file needs grit headers. | |
| 151 # To be more thorough, one would need to run a pre-processor. | |
| 152 SPECIAL_KEYWORDS = ( | |
| 153 'IMAGE_GRID(', # Macro in nine_image_painter_factory.h | |
| 154 '#include "ui_localizer_table.h"', # ui_localizer.mm | |
| 155 'DEFINE_RESOURCE_ID', # chrome/browser/android/resource_mapper.cc | |
| 156 ) | |
| 157 with open(filename, 'rb') as f: | |
| 158 grit_header_line = grit_header + '"\n' | |
| 159 has_grit_header = False | |
| 160 while True: | |
| 161 line = f.readline() | |
| 162 if not line: | |
| 163 break | |
| 164 if line.endswith(grit_header_line): | |
| 165 has_grit_header = True | |
| 166 break | |
| 167 | |
| 168 if not has_grit_header: | |
| 169 return True | |
| 170 rest_of_the_file = f.read() | |
| 171 return (any(resource in rest_of_the_file for resource in resources) or | |
| 172 any(keyword in rest_of_the_file for keyword in SPECIAL_KEYWORDS)) | |
| 173 | |
| 174 | |
| 175 def main(argv): | |
| 176 if len(argv) < 3: | |
| 177 Usage(argv[0]) | |
| 178 return 1 | |
| 179 grd_file = argv[1] | |
| 180 dirs_to_scan = argv[2:] | |
| 181 for f in dirs_to_scan: | |
| 182 if not os.path.exists(f): | |
| 183 print 'Error: %s does not exist' % f | |
| 184 return 1 | |
| 185 | |
| 186 tree = xml.etree.ElementTree.parse(grd_file) | |
| 187 grit_header = GetOutputHeaderFile(tree) | |
| 188 if not grit_header: | |
| 189 print 'Error: %s does not generate any output headers.' % grit_header | |
| 190 return 1 | |
| 191 resources = GetResourcesForGrdFile(tree, grd_file) | |
| 192 | |
| 193 files_with_unneeded_grit_includes = [] | |
| 194 for dir_to_scan in dirs_to_scan: | |
| 195 for root, dirs, files in os.walk(dir_to_scan): | |
| 196 if '.git' in dirs: | |
| 197 dirs.remove('.git') | |
| 198 full_paths = [os.path.join(root, f) for f in files if ShouldScanFile(f)] | |
| 199 files_with_unneeded_grit_includes.extend( | |
| 200 [f for f in full_paths | |
| 201 if not NeedsGritInclude(grit_header, resources, f)]) | |
| 202 if files_with_unneeded_grit_includes: | |
| 203 print '\n'.join(files_with_unneeded_grit_includes) | |
| 204 return 2 | |
| 205 return 0 | |
| 206 | |
| 207 | |
| 208 if __name__ == '__main__': | |
| 209 sys.exit(main(sys.argv)) | |
| OLD | NEW |