[email protected] | 2fac375 | 2011-11-27 20:56:51 | [diff] [blame] | 1 | #!/usr/bin/env python |
[email protected] | 2b1fbb9d | 2012-02-08 07:30:51 | [diff] [blame] | 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | """Without any args, this simply loads the IDs out of a bunch of the Chrome GRD |
| 7 | files, and then checks the subset of the code that loads the strings to try |
| 8 | and figure out what isn't in use any more. |
| 9 | You can give paths to GRD files and source directories to control what is |
| 10 | check instead. |
| 11 | """ |
| 12 | |
| 13 | import os |
| 14 | import re |
| 15 | import sys |
| 16 | import xml.sax |
| 17 | |
| 18 | # Extra messages along the way |
| 19 | # 1 - Print ids that are found in sources but not in the found id set |
| 20 | # 2 - Files that aren't processes (don't match the source name regex) |
| 21 | DEBUG = 0 |
| 22 | |
| 23 | |
| 24 | class GrdIDExtractor(xml.sax.handler.ContentHandler): |
| 25 | """Extracts the IDs from messages in GRIT files""" |
| 26 | def __init__(self): |
| 27 | self.id_set_ = set() |
| 28 | |
| 29 | def startElement(self, name, attrs): |
| 30 | if name == 'message': |
| 31 | self.id_set_.add(attrs['name']) |
| 32 | |
| 33 | def allIDs(self): |
| 34 | """Return all the IDs found""" |
| 35 | return self.id_set_.copy() |
| 36 | |
| 37 | |
| 38 | def CheckForUnusedGrdIDsInSources(grd_files, src_dirs): |
| 39 | """Will collect the message ids out of the given GRD files and then scan |
| 40 | the source directories to try and figure out what ids are not currently |
| 41 | being used by any source. |
| 42 | |
| 43 | grd_files: |
| 44 | A list of GRD files to collect the ids from. |
| 45 | src_dirs: |
| 46 | A list of directories to walk looking for source files. |
| 47 | """ |
| 48 | # Collect all the ids into a large map |
| 49 | all_ids = set() |
| 50 | file_id_map = {} |
| 51 | for y in grd_files: |
| 52 | handler = GrdIDExtractor() |
| 53 | xml.sax.parse(y, handler) |
| 54 | files_ids = handler.allIDs() |
| 55 | file_id_map[y] = files_ids |
| 56 | all_ids |= files_ids |
| 57 | |
| 58 | |
| 59 | # The regex that will be used to check sources |
| 60 | id_regex = re.compile('IDS_[A-Z0-9_]+') |
| 61 | |
| 62 | # Make sure the regex matches every id found. |
| 63 | got_err = False |
| 64 | for x in all_ids: |
| 65 | match = id_regex.search(x) |
| 66 | if match is None: |
| 67 | print 'ERROR: "%s" did not match our regex' % (x) |
| 68 | got_err = True |
| 69 | if not match.group(0) is x: |
| 70 | print 'ERROR: "%s" did not fully match our regex' % (x) |
| 71 | got_err = True |
| 72 | if got_err: |
| 73 | return 1 |
| 74 | |
| 75 | # The regex for deciding what is a source file |
[email protected] | cf83d25 | 2011-11-30 00:20:21 | [diff] [blame] | 76 | src_regex = re.compile('\.(([chm])|(mm)|(cc)|(cp)|(cpp)|(xib)|(py))$') |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 77 | |
| 78 | ids_left = all_ids.copy() |
| 79 | |
| 80 | # Scanning time. |
| 81 | for src_dir in src_dirs: |
| 82 | for root, dirs, files in os.walk(src_dir): |
| 83 | # Remove svn directories from recursion |
| 84 | if '.svn' in dirs: |
| 85 | dirs.remove('.svn') |
| 86 | for file in files: |
| 87 | if src_regex.search(file.lower()): |
| 88 | full_path = os.path.join(root, file) |
| 89 | src_file_contents = open(full_path).read() |
| 90 | for match in sorted(set(id_regex.findall(src_file_contents))): |
| 91 | if match in ids_left: |
| 92 | ids_left.remove(match) |
| 93 | if DEBUG: |
| 94 | if not match in all_ids: |
| 95 | print '%s had "%s", which was not in the found IDs' % \ |
| 96 | (full_path, match) |
| 97 | elif DEBUG > 1: |
| 98 | full_path = os.path.join(root, file) |
| 99 | print 'Skipping %s.' % (full_path) |
| 100 | |
| 101 | # Anything left? |
| 102 | if len(ids_left) > 0: |
| 103 | print 'The following ids are in GRD files, but *appear* to be unused:' |
| 104 | for file_path, file_ids in file_id_map.iteritems(): |
| 105 | missing = ids_left.intersection(file_ids) |
| 106 | if len(missing) > 0: |
| 107 | print ' %s:' % (file_path) |
| 108 | print '\n'.join(' %s' % (x) for x in sorted(missing)) |
| 109 | |
| 110 | return 0 |
| 111 | |
| 112 | |
[email protected] | 2fac375 | 2011-11-27 20:56:51 | [diff] [blame] | 113 | def main(): |
tfarina | c9fc190 | 2014-08-29 16:19:04 | [diff] [blame] | 114 | # script lives in src/tools |
| 115 | tools_dir = os.path.dirname(os.path.abspath(sys.argv[0])) |
| 116 | src_dir = os.path.dirname(tools_dir) |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 117 | |
| 118 | # Collect the args into the right buckets |
| 119 | src_dirs = [] |
| 120 | grd_files = [] |
| 121 | for arg in sys.argv[1:]: |
Steven Bennetts | 528eec5 | 2017-11-10 19:21:19 | [diff] [blame^] | 122 | if arg.lower().endswith('.grd') or arg.lower().endswith('.grdp'): |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 123 | grd_files.append(arg) |
| 124 | else: |
| 125 | src_dirs.append(arg) |
| 126 | |
| 127 | # If no GRD files were given, default them: |
| 128 | if len(grd_files) == 0: |
[email protected] | 2b1fbb9d | 2012-02-08 07:30:51 | [diff] [blame] | 129 | ash_base_dir = os.path.join(src_dir, 'ash') |
tfarina | c9fc190 | 2014-08-29 16:19:04 | [diff] [blame] | 130 | chrome_dir = os.path.join(src_dir, 'chrome') |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 131 | chrome_app_dir = os.path.join(chrome_dir, 'app') |
| 132 | chrome_app_res_dir = os.path.join(chrome_app_dir, 'resources') |
[email protected] | 8a92bbd | 2013-02-22 01:07:29 | [diff] [blame] | 133 | device_base_dir = os.path.join(src_dir, 'device') |
[email protected] | 0204084 | 2014-08-21 04:29:10 | [diff] [blame] | 134 | ui_dir = os.path.join(src_dir, 'ui') |
| 135 | ui_strings_dir = os.path.join(ui_dir, 'strings') |
| 136 | ui_chromeos_dir = os.path.join(ui_dir, 'chromeos') |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 137 | grd_files = [ |
[email protected] | 2b1fbb9d | 2012-02-08 07:30:51 | [diff] [blame] | 138 | os.path.join(ash_base_dir, 'ash_strings.grd'), |
[email protected] | 056ab7e6 | 2012-09-14 03:13:15 | [diff] [blame] | 139 | os.path.join(ash_base_dir, 'resources', 'ash_resources.grd'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 140 | os.path.join(chrome_app_dir, 'chromium_strings.grd'), |
| 141 | os.path.join(chrome_app_dir, 'generated_resources.grd'), |
| 142 | os.path.join(chrome_app_dir, 'google_chrome_strings.grd'), |
| 143 | os.path.join(chrome_app_res_dir, 'locale_settings.grd'), |
[email protected] | 80b1adf | 2012-11-17 14:20:11 | [diff] [blame] | 144 | os.path.join(chrome_app_res_dir, 'locale_settings_chromiumos.grd'), |
| 145 | os.path.join(chrome_app_res_dir, 'locale_settings_google_chromeos.grd'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 146 | os.path.join(chrome_app_res_dir, 'locale_settings_linux.grd'), |
| 147 | os.path.join(chrome_app_res_dir, 'locale_settings_mac.grd'), |
| 148 | os.path.join(chrome_app_res_dir, 'locale_settings_win.grd'), |
| 149 | os.path.join(chrome_app_dir, 'theme', 'theme_resources.grd'), |
| 150 | os.path.join(chrome_dir, 'browser', 'browser_resources.grd'), |
| 151 | os.path.join(chrome_dir, 'common', 'common_resources.grd'), |
[email protected] | 8d0d4f7d | 2012-09-11 03:31:29 | [diff] [blame] | 152 | os.path.join(chrome_dir, 'renderer', 'resources', |
| 153 | 'renderer_resources.grd'), |
[email protected] | e3a9ccfc | 2013-06-03 00:22:30 | [diff] [blame] | 154 | os.path.join(device_base_dir, 'bluetooth', 'bluetooth_strings.grd'), |
jamescook | 488cf9d | 2017-04-06 19:39:35 | [diff] [blame] | 155 | os.path.join(src_dir, 'extensions', 'strings', 'extensions_strings.grd'), |
[email protected] | 29b25d09 | 2011-06-29 20:57:34 | [diff] [blame] | 156 | os.path.join(src_dir, 'ui', 'resources', 'ui_resources.grd'), |
[email protected] | 70fd3bb | 2013-02-28 08:55:04 | [diff] [blame] | 157 | os.path.join(src_dir, 'ui', 'webui', 'resources', 'webui_resources.grd'), |
[email protected] | 0204084 | 2014-08-21 04:29:10 | [diff] [blame] | 158 | os.path.join(ui_strings_dir, 'app_locale_settings.grd'), |
| 159 | os.path.join(ui_strings_dir, 'ui_strings.grd'), |
[email protected] | ece882c8 | 2014-07-13 04:03:42 | [diff] [blame] | 160 | os.path.join(ui_chromeos_dir, 'ui_chromeos_strings.grd'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 161 | ] |
| 162 | |
| 163 | # If no source directories were given, default them: |
| 164 | if len(src_dirs) == 0: |
| 165 | src_dirs = [ |
| 166 | os.path.join(src_dir, 'app'), |
[email protected] | 3923c13 | 2012-08-22 16:45:54 | [diff] [blame] | 167 | os.path.join(src_dir, 'ash'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 168 | os.path.join(src_dir, 'chrome'), |
[email protected] | 79c24cd | 2013-03-12 04:37:37 | [diff] [blame] | 169 | os.path.join(src_dir, 'components'), |
[email protected] | cf83d25 | 2011-11-30 00:20:21 | [diff] [blame] | 170 | os.path.join(src_dir, 'content'), |
[email protected] | 050ed684 | 2013-03-04 20:18:18 | [diff] [blame] | 171 | os.path.join(src_dir, 'device'), |
[email protected] | 99c0125b | 2014-04-17 05:21:57 | [diff] [blame] | 172 | os.path.join(src_dir, 'extensions'), |
[email protected] | 4c9101d | 2011-03-15 20:49:48 | [diff] [blame] | 173 | os.path.join(src_dir, 'ui'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 174 | # nsNSSCertHelper.cpp has a bunch of ids |
| 175 | os.path.join(src_dir, 'third_party', 'mozilla_security_manager'), |
[email protected] | cf83d25 | 2011-11-30 00:20:21 | [diff] [blame] | 176 | os.path.join(chrome_dir, 'installer'), |
[email protected] | f16943a1 | 2010-04-09 13:07:19 | [diff] [blame] | 177 | ] |
| 178 | |
[email protected] | 2fac375 | 2011-11-27 20:56:51 | [diff] [blame] | 179 | return CheckForUnusedGrdIDsInSources(grd_files, src_dirs) |
| 180 | |
| 181 | |
| 182 | if __name__ == '__main__': |
| 183 | sys.exit(main()) |