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 |