Chromium Code Reviews| 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.""" | |
|
Nico
2014/08/24 23:58:25
Include a usage line
Lei Zhang
2014/08/26 02:35:24
Usage() is literally 6 lines below...
| |
| 7 | |
| 8 import os | |
| 9 import sys | |
| 10 import xml.etree.ElementTree | |
| 11 | |
|
Nico
2014/08/24 23:58:26
2 empty lines between toplevels
Lei Zhang
2014/08/26 02:35:25
Done.
| |
| 12 def Usage(prog_name): | |
| 13 print prog_name, 'GRD_FILE DIRS_TO_SCAN' | |
|
Nico
2014/08/24 23:58:25
Consider using optparse or argparse (see e.g. tool
Lei Zhang
2014/08/26 02:35:24
There's no options. If we do add options later, I'
Nico
2014/08/26 02:43:37
It also offers usage printing (you can hand it the
| |
| 14 | |
| 15 | |
| 16 def GetResourcesForNode(node, parent_file, resource_tag): | |
| 17 """Recursively iterate through a node and extract resource names. | |
| 18 | |
| 19 Args: | |
| 20 node: The node to iterate through. | |
| 21 parent_file: The file that contains node. | |
| 22 resource_tag: The resource tag to extract names from. | |
| 23 | |
| 24 Returns: | |
| 25 A list of resource names. | |
| 26 """ | |
| 27 resources = [] | |
| 28 for child in node.getchildren(): | |
| 29 if child.tag == resource_tag: | |
| 30 resources.append(child.attrib['name']) | |
| 31 elif child.tag == 'if': | |
| 32 resources.extend(GetResourcesForNode(child, parent_file, resource_tag)) | |
| 33 elif child.tag == 'part': | |
| 34 parent_dir = os.path.dirname(parent_file) | |
| 35 part_file = os.path.join(parent_dir, child.attrib['file']) | |
| 36 part_tree = xml.etree.ElementTree.parse(part_file) | |
| 37 part_root = part_tree.getroot() | |
| 38 assert part_root.tag == 'grit-part' | |
| 39 resources.extend(GetResourcesForNode(part_root, part_file, resource_tag)) | |
| 40 else: | |
| 41 raise Exception('unknown tag:', child.tag) | |
| 42 return resources | |
| 43 | |
| 44 | |
| 45 def GetResourcesForGrdFile(tree, grd_file): | |
| 46 """Find all the message and include resources from a given grit file. | |
| 47 | |
| 48 Args: | |
| 49 tree: The XML tree. | |
| 50 grd_file: The file that contains the XML tree. | |
| 51 | |
| 52 Returns: | |
| 53 A list of resource names. | |
| 54 """ | |
| 55 root = tree.getroot() | |
| 56 assert root.tag == 'grit' | |
| 57 for node in root.getchildren(): | |
| 58 if node.tag == 'release': | |
| 59 release_node = node | |
| 60 break | |
| 61 assert release_node != None | |
| 62 | |
| 63 for node in release_node.getchildren(): | |
| 64 if node.tag == 'messages': | |
| 65 messages_node = node | |
| 66 break | |
| 67 messages = set() | |
| 68 if messages_node != None: | |
| 69 messages = set(GetResourcesForNode(messages_node, grd_file, 'message')) | |
| 70 | |
| 71 for node in release_node.getchildren(): | |
| 72 if node.tag == 'includes': | |
| 73 includes_node = node | |
| 74 break | |
|
Nico
2014/08/24 23:58:25
Since you have this for loop 3 times, consider hav
Lei Zhang
2014/08/26 02:35:24
Done.
| |
| 75 includes = set() | |
| 76 if includes_node != None: | |
| 77 includes = set(GetResourcesForNode(includes_node, grd_file, 'include')) | |
| 78 return messages.union(includes) | |
| 79 | |
| 80 | |
| 81 def GetOutputFileForNode(node): | |
| 82 """Find the output file starting from a given node. | |
| 83 | |
| 84 Args: | |
| 85 node: The root node to scan from. | |
| 86 | |
| 87 Returns: | |
| 88 A grit header file name. | |
| 89 """ | |
| 90 output_file = None | |
| 91 for child in node.getchildren(): | |
| 92 if child.tag == 'output': | |
| 93 if child.attrib['type'] == 'rc_header': | |
| 94 assert output_file == None | |
| 95 output_file = child.attrib['filename'] | |
| 96 elif child.tag == 'if': | |
|
Nico
2014/08/24 23:58:26
do you need to look at 'else' children of if nodes
Lei Zhang
2014/08/26 02:35:25
I don't think grit has <else> nodes? It may be pos
Nico
2014/08/26 02:43:37
see https://codereview.chromium.org/11155024
| |
| 97 child_output_file = GetOutputFileForNode(child) | |
| 98 if not child_output_file: | |
| 99 continue | |
| 100 assert output_file == None | |
|
Nico
2014/08/24 23:58:25
nit: it's more common to see "output_file is None"
Lei Zhang
2014/08/26 02:35:25
Done.
| |
| 101 output_file = child_output_file | |
| 102 else: | |
| 103 raise Exception('unknown tag:', child.tag) | |
| 104 return output_file | |
| 105 | |
| 106 | |
| 107 def GetOutputHeaderFile(tree): | |
| 108 """Find the output file for a given tree. | |
| 109 | |
| 110 Args: | |
| 111 tree: The tree to scan. | |
| 112 | |
| 113 Returns: | |
| 114 A grit header file name. | |
| 115 """ | |
| 116 root = tree.getroot() | |
| 117 assert root.tag == 'grit' | |
| 118 for node in root.getchildren(): | |
| 119 if node.tag == 'outputs': | |
| 120 output_node = node | |
| 121 break | |
|
Nico
2014/08/24 23:58:26
Maybe `assert not output_node` before the assignme
Lei Zhang
2014/08/26 02:35:24
Done. And elsewhere above.
| |
| 122 assert output_node != None | |
| 123 return GetOutputFileForNode(output_node) | |
| 124 | |
| 125 | |
| 126 def ShouldScanFile(filename): | |
| 127 """Return if the filename has one of the extensions below.""" | |
| 128 extensions = ['.cc', '.cpp', '.h', '.mm'] | |
| 129 file_extension = os.path.splitext(filename)[1] | |
| 130 return file_extension in extensions | |
| 131 | |
|
Nico
2014/08/24 23:58:26
add a newline
Lei Zhang
2014/08/26 02:35:24
Done.
| |
| 132 def NeedsGritInclude(grit_header, resources, filename): | |
| 133 """Return whether a file needs a given grit header or not. | |
| 134 | |
| 135 Args: | |
| 136 grit_header: The grit header file name. | |
| 137 resources: The list of resource names in grit_header. | |
| 138 filename: The file to scan. | |
| 139 | |
| 140 Returns: | |
| 141 True if the file should include the grit header. | |
| 142 """ | |
| 143 with open(filename, 'rb') as f: | |
| 144 grit_header_line = grit_header + '"\n' | |
| 145 has_grit_header = False | |
| 146 while True: | |
| 147 line = f.readline() | |
| 148 if not line: | |
| 149 break | |
| 150 if line.endswith(grit_header_line): | |
| 151 has_grit_header = True | |
| 152 break | |
| 153 | |
| 154 if not has_grit_header: | |
| 155 return True | |
| 156 rest_of_the_file = f.read() | |
| 157 return any(resource in rest_of_the_file for resource in resources) | |
| 158 | |
| 159 | |
| 160 def main(argv): | |
| 161 if len(argv) < 3: | |
| 162 Usage(argv[0]) | |
| 163 sys.exit(1) | |
| 164 grd_file = argv[1] | |
| 165 dirs_to_scan = argv[2:] | |
| 166 for f in dirs_to_scan: | |
| 167 if not os.path.exists(f): | |
| 168 print 'Error: %s does not exist' % f | |
| 169 sys.exit(1) | |
| 170 | |
| 171 tree = xml.etree.ElementTree.parse(grd_file) | |
| 172 grit_header = GetOutputHeaderFile(tree) | |
| 173 resources = GetResourcesForGrdFile(tree, grd_file) | |
| 174 | |
| 175 files_with_unneeded_grit_includes = [] | |
| 176 for dir_to_scan in dirs_to_scan: | |
| 177 for root, dirs, files in os.walk(dir_to_scan): | |
| 178 if '.git' in dirs: | |
| 179 dirs.remove('.git') | |
| 180 full_paths = [os.path.join(root, f) for f in files if ShouldScanFile(f)] | |
| 181 files_with_unneeded_grit_includes.extend( | |
| 182 [f for f in full_paths | |
| 183 if not NeedsGritInclude(grit_header, resources, f)]) | |
| 184 if files_with_unneeded_grit_includes: | |
| 185 print '\n'.join(files_with_unneeded_grit_includes) | |
| 186 sys.exit(2) | |
| 187 sys.exit(0) | |
| 188 | |
|
Nico
2014/08/24 23:58:25
add newline
Lei Zhang
2014/08/26 02:35:24
Done.
| |
| 189 if __name__ == '__main__': | |
| 190 main(sys.argv) | |
|
Nico
2014/08/24 23:58:25
nit: the usual thing is `sys.exit(main(sys.argv))
Lei Zhang
2014/08/26 02:35:24
Done.
| |
| OLD | NEW |