Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Unified Diff: tools/unused-grit-header.py

Issue 504503002: Add a tool to look for source files that have unneeded grit header includes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/unused-grit-header.py
diff --git a/tools/unused-grit-header.py b/tools/unused-grit-header.py
new file mode 100755
index 0000000000000000000000000000000000000000..86b9b0a4c6aeb7a374df6bb00437b625249e1516
--- /dev/null
+++ b/tools/unused-grit-header.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""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...
+
+import os
+import sys
+import xml.etree.ElementTree
+
Nico 2014/08/24 23:58:26 2 empty lines between toplevels
Lei Zhang 2014/08/26 02:35:25 Done.
+def Usage(prog_name):
+ 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
+
+
+def GetResourcesForNode(node, parent_file, resource_tag):
+ """Recursively iterate through a node and extract resource names.
+
+ Args:
+ node: The node to iterate through.
+ parent_file: The file that contains node.
+ resource_tag: The resource tag to extract names from.
+
+ Returns:
+ A list of resource names.
+ """
+ resources = []
+ for child in node.getchildren():
+ if child.tag == resource_tag:
+ resources.append(child.attrib['name'])
+ elif child.tag == 'if':
+ resources.extend(GetResourcesForNode(child, parent_file, resource_tag))
+ elif child.tag == 'part':
+ parent_dir = os.path.dirname(parent_file)
+ part_file = os.path.join(parent_dir, child.attrib['file'])
+ part_tree = xml.etree.ElementTree.parse(part_file)
+ part_root = part_tree.getroot()
+ assert part_root.tag == 'grit-part'
+ resources.extend(GetResourcesForNode(part_root, part_file, resource_tag))
+ else:
+ raise Exception('unknown tag:', child.tag)
+ return resources
+
+
+def GetResourcesForGrdFile(tree, grd_file):
+ """Find all the message and include resources from a given grit file.
+
+ Args:
+ tree: The XML tree.
+ grd_file: The file that contains the XML tree.
+
+ Returns:
+ A list of resource names.
+ """
+ root = tree.getroot()
+ assert root.tag == 'grit'
+ for node in root.getchildren():
+ if node.tag == 'release':
+ release_node = node
+ break
+ assert release_node != None
+
+ for node in release_node.getchildren():
+ if node.tag == 'messages':
+ messages_node = node
+ break
+ messages = set()
+ if messages_node != None:
+ messages = set(GetResourcesForNode(messages_node, grd_file, 'message'))
+
+ for node in release_node.getchildren():
+ if node.tag == 'includes':
+ includes_node = node
+ 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.
+ includes = set()
+ if includes_node != None:
+ includes = set(GetResourcesForNode(includes_node, grd_file, 'include'))
+ return messages.union(includes)
+
+
+def GetOutputFileForNode(node):
+ """Find the output file starting from a given node.
+
+ Args:
+ node: The root node to scan from.
+
+ Returns:
+ A grit header file name.
+ """
+ output_file = None
+ for child in node.getchildren():
+ if child.tag == 'output':
+ if child.attrib['type'] == 'rc_header':
+ assert output_file == None
+ output_file = child.attrib['filename']
+ 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
+ child_output_file = GetOutputFileForNode(child)
+ if not child_output_file:
+ continue
+ 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.
+ output_file = child_output_file
+ else:
+ raise Exception('unknown tag:', child.tag)
+ return output_file
+
+
+def GetOutputHeaderFile(tree):
+ """Find the output file for a given tree.
+
+ Args:
+ tree: The tree to scan.
+
+ Returns:
+ A grit header file name.
+ """
+ root = tree.getroot()
+ assert root.tag == 'grit'
+ for node in root.getchildren():
+ if node.tag == 'outputs':
+ output_node = node
+ 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.
+ assert output_node != None
+ return GetOutputFileForNode(output_node)
+
+
+def ShouldScanFile(filename):
+ """Return if the filename has one of the extensions below."""
+ extensions = ['.cc', '.cpp', '.h', '.mm']
+ file_extension = os.path.splitext(filename)[1]
+ return file_extension in extensions
+
Nico 2014/08/24 23:58:26 add a newline
Lei Zhang 2014/08/26 02:35:24 Done.
+def NeedsGritInclude(grit_header, resources, filename):
+ """Return whether a file needs a given grit header or not.
+
+ Args:
+ grit_header: The grit header file name.
+ resources: The list of resource names in grit_header.
+ filename: The file to scan.
+
+ Returns:
+ True if the file should include the grit header.
+ """
+ with open(filename, 'rb') as f:
+ grit_header_line = grit_header + '"\n'
+ has_grit_header = False
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ if line.endswith(grit_header_line):
+ has_grit_header = True
+ break
+
+ if not has_grit_header:
+ return True
+ rest_of_the_file = f.read()
+ return any(resource in rest_of_the_file for resource in resources)
+
+
+def main(argv):
+ if len(argv) < 3:
+ Usage(argv[0])
+ sys.exit(1)
+ grd_file = argv[1]
+ dirs_to_scan = argv[2:]
+ for f in dirs_to_scan:
+ if not os.path.exists(f):
+ print 'Error: %s does not exist' % f
+ sys.exit(1)
+
+ tree = xml.etree.ElementTree.parse(grd_file)
+ grit_header = GetOutputHeaderFile(tree)
+ resources = GetResourcesForGrdFile(tree, grd_file)
+
+ files_with_unneeded_grit_includes = []
+ for dir_to_scan in dirs_to_scan:
+ for root, dirs, files in os.walk(dir_to_scan):
+ if '.git' in dirs:
+ dirs.remove('.git')
+ full_paths = [os.path.join(root, f) for f in files if ShouldScanFile(f)]
+ files_with_unneeded_grit_includes.extend(
+ [f for f in full_paths
+ if not NeedsGritInclude(grit_header, resources, f)])
+ if files_with_unneeded_grit_includes:
+ print '\n'.join(files_with_unneeded_grit_includes)
+ sys.exit(2)
+ sys.exit(0)
+
Nico 2014/08/24 23:58:25 add newline
Lei Zhang 2014/08/26 02:35:24 Done.
+if __name__ == '__main__':
+ 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.
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698