Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 """Wrapper script to help run clang tools across Chromium code. | 5 """Wrapper script to help run clang tools across Chromium code. |
| 6 | 6 |
| 7 How to use this tool: | 7 How to use this tool: |
| 8 If you want to run the tool across all Chromium code: | 8 If you want to run the tool across all Chromium code: |
| 9 run_tool.py <tool> <path/to/compiledb> | 9 run_tool.py <tool> <path/to/compiledb> |
| 10 | 10 |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 107 edit_type, path, offset, length, replacement = line.split(':::', 4) | 107 edit_type, path, offset, length, replacement = line.split(':::', 4) |
| 108 replacement = replacement.replace('\0', '\n') | 108 replacement = replacement.replace('\0', '\n') |
| 109 # Normalize the file path emitted by the clang tool. | 109 # Normalize the file path emitted by the clang tool. |
| 110 path = os.path.realpath(os.path.join(build_directory, path)) | 110 path = os.path.realpath(os.path.join(build_directory, path)) |
| 111 edits[path].append(Edit(edit_type, int(offset), int(length), replacement)) | 111 edits[path].append(Edit(edit_type, int(offset), int(length), replacement)) |
| 112 except ValueError: | 112 except ValueError: |
| 113 print 'Unable to parse edit: %s' % line | 113 print 'Unable to parse edit: %s' % line |
| 114 return edits | 114 return edits |
| 115 | 115 |
| 116 | 116 |
| 117 def _ExecuteTool(toolname, build_directory, filename): | 117 def _ExecuteTool(toolname, tool_args, build_directory, filename): |
| 118 """Executes the tool. | 118 """Executes the tool. |
| 119 | 119 |
| 120 This is defined outside the class so it can be pickled for the multiprocessing | 120 This is defined outside the class so it can be pickled for the multiprocessing |
| 121 module. | 121 module. |
| 122 | 122 |
| 123 Args: | 123 Args: |
| 124 toolname: Path to the tool to execute. | 124 toolname: Path to the tool to execute. |
| 125 tool_args: Arguments to be passed to the tool. Can be None. | |
| 125 build_directory: Directory that contains the compile database. | 126 build_directory: Directory that contains the compile database. |
| 126 filename: The file to run the tool over. | 127 filename: The file to run the tool over. |
| 127 | 128 |
| 128 Returns: | 129 Returns: |
| 129 A dictionary that must contain the key "status" and a boolean value | 130 A dictionary that must contain the key "status" and a boolean value |
| 130 associated with it. | 131 associated with it. |
| 131 | 132 |
| 132 If status is True, then the generated edits are stored with the key "edits" | 133 If status is True, then the generated edits are stored with the key "edits" |
| 133 in the dictionary. | 134 in the dictionary. |
| 134 | 135 |
| 135 Otherwise, the filename and the output from stderr are associated with the | 136 Otherwise, the filename and the output from stderr are associated with the |
| 136 keys "filename" and "stderr" respectively. | 137 keys "filename" and "stderr" respectively. |
| 137 """ | 138 """ |
| 138 command = subprocess.Popen( | 139 args = [toolname, '-p', build_directory, filename] |
| 139 (toolname, '-p', build_directory, filename), | 140 if (tool_args): |
| 140 stdout=subprocess.PIPE, | 141 args.extend(tool_args) |
| 141 stderr=subprocess.PIPE) | 142 command = subprocess.Popen(tuple(args), |
|
dcheng
2016/12/01 01:09:26
Nit: remove tuple() here, args just needs to be an
Ramin Halavati
2016/12/01 02:32:48
Done.
| |
| 143 stdout=subprocess.PIPE, | |
| 144 stderr=subprocess.PIPE) | |
| 142 stdout, stderr = command.communicate() | 145 stdout, stderr = command.communicate() |
| 143 if command.returncode != 0: | 146 if command.returncode != 0: |
| 144 return {'status': False, 'filename': filename, 'stderr': stderr} | 147 return {'status': False, 'filename': filename, 'stderr': stderr} |
| 145 else: | 148 else: |
| 146 return {'status': True, | 149 return {'status': True, |
| 147 'edits': _ExtractEditsFromStdout(build_directory, stdout)} | 150 'edits': _ExtractEditsFromStdout(build_directory, stdout)} |
| 148 | 151 |
| 149 | 152 |
| 150 class _CompilerDispatcher(object): | 153 class _CompilerDispatcher(object): |
| 151 """Multiprocessing controller for running clang tools in parallel.""" | 154 """Multiprocessing controller for running clang tools in parallel.""" |
| 152 | 155 |
| 153 def __init__(self, toolname, build_directory, filenames): | 156 def __init__(self, toolname, tool_args, build_directory, filenames): |
| 154 """Initializer method. | 157 """Initializer method. |
| 155 | 158 |
| 156 Args: | 159 Args: |
| 157 toolname: Path to the tool to execute. | 160 toolname: Path to the tool to execute. |
| 161 tool_args: Arguments to be passed to the tool. Can be None. | |
| 158 build_directory: Directory that contains the compile database. | 162 build_directory: Directory that contains the compile database. |
| 159 filenames: The files to run the tool over. | 163 filenames: The files to run the tool over. |
| 160 """ | 164 """ |
| 161 self.__toolname = toolname | 165 self.__toolname = toolname |
| 166 self.__tool_args = tool_args | |
| 162 self.__build_directory = build_directory | 167 self.__build_directory = build_directory |
| 163 self.__filenames = filenames | 168 self.__filenames = filenames |
| 164 self.__success_count = 0 | 169 self.__success_count = 0 |
| 165 self.__failed_count = 0 | 170 self.__failed_count = 0 |
| 166 self.__edit_count = 0 | 171 self.__edit_count = 0 |
| 167 self.__edits = collections.defaultdict(list) | 172 self.__edits = collections.defaultdict(list) |
| 168 | 173 |
| 169 @property | 174 @property |
| 170 def edits(self): | 175 def edits(self): |
| 171 return self.__edits | 176 return self.__edits |
| 172 | 177 |
| 173 @property | 178 @property |
| 174 def failed_count(self): | 179 def failed_count(self): |
| 175 return self.__failed_count | 180 return self.__failed_count |
| 176 | 181 |
| 177 def Run(self): | 182 def Run(self): |
| 178 """Does the grunt work.""" | 183 """Does the grunt work.""" |
| 179 pool = multiprocessing.Pool() | 184 pool = multiprocessing.Pool() |
| 180 result_iterator = pool.imap_unordered( | 185 result_iterator = pool.imap_unordered( |
| 181 functools.partial(_ExecuteTool, self.__toolname, | 186 functools.partial(_ExecuteTool, self.__toolname, self.__tool_args, |
| 182 self.__build_directory), self.__filenames) | 187 self.__build_directory), |
| 188 self.__filenames) | |
| 183 for result in result_iterator: | 189 for result in result_iterator: |
| 184 self.__ProcessResult(result) | 190 self.__ProcessResult(result) |
| 185 sys.stdout.write('\n') | 191 sys.stdout.write('\n') |
| 186 sys.stdout.flush() | 192 sys.stdout.flush() |
| 187 | 193 |
| 188 def __ProcessResult(self, result): | 194 def __ProcessResult(self, result): |
| 189 """Handles result processing. | 195 """Handles result processing. |
| 190 | 196 |
| 191 Args: | 197 Args: |
| 192 result: The result dictionary returned by _ExecuteTool. | 198 result: The result dictionary returned by _ExecuteTool. |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 '--generate-compdb', | 298 '--generate-compdb', |
| 293 action='store_true', | 299 action='store_true', |
| 294 help='regenerate the compile database before running the tool') | 300 help='regenerate the compile database before running the tool') |
| 295 parser.add_argument( | 301 parser.add_argument( |
| 296 'compile_database', | 302 'compile_database', |
| 297 help='path to the directory that contains the compile database') | 303 help='path to the directory that contains the compile database') |
| 298 parser.add_argument( | 304 parser.add_argument( |
| 299 'path_filter', | 305 'path_filter', |
| 300 nargs='*', | 306 nargs='*', |
| 301 help='optional paths to filter what files the tool is run on') | 307 help='optional paths to filter what files the tool is run on') |
| 308 parser.add_argument( | |
| 309 '--tool-args', nargs='*', | |
| 310 help='optional arguments passed to the tool') | |
| 302 args = parser.parse_args() | 311 args = parser.parse_args() |
| 303 | 312 |
| 304 os.environ['PATH'] = '%s%s%s' % ( | 313 os.environ['PATH'] = '%s%s%s' % ( |
| 305 os.path.abspath(os.path.join( | 314 os.path.abspath(os.path.join( |
| 306 os.path.dirname(__file__), | 315 os.path.dirname(__file__), |
| 307 '../../../third_party/llvm-build/Release+Asserts/bin')), | 316 '../../../third_party/llvm-build/Release+Asserts/bin')), |
| 308 os.pathsep, | 317 os.pathsep, |
| 309 os.environ['PATH']) | 318 os.environ['PATH']) |
| 310 | 319 |
| 311 if args.generate_compdb: | 320 if args.generate_compdb: |
| 312 compile_db.GenerateWithNinja(args.compile_database) | 321 compile_db.GenerateWithNinja(args.compile_database) |
| 313 | 322 |
| 314 if args.all: | 323 if args.all: |
| 315 filenames = set(_GetFilesFromCompileDB(args.compile_database)) | 324 filenames = set(_GetFilesFromCompileDB(args.compile_database)) |
| 316 source_filenames = filenames | 325 source_filenames = filenames |
| 317 else: | 326 else: |
| 318 filenames = set(_GetFilesFromGit(args.path_filter)) | 327 filenames = set(_GetFilesFromGit(args.path_filter)) |
| 319 # Filter out files that aren't C/C++/Obj-C/Obj-C++. | 328 # Filter out files that aren't C/C++/Obj-C/Obj-C++. |
| 320 extensions = frozenset(('.c', '.cc', '.cpp', '.m', '.mm')) | 329 extensions = frozenset(('.c', '.cc', '.cpp', '.m', '.mm')) |
| 321 source_filenames = [f | 330 source_filenames = [f |
| 322 for f in filenames | 331 for f in filenames |
| 323 if os.path.splitext(f)[1] in extensions] | 332 if os.path.splitext(f)[1] in extensions] |
| 324 dispatcher = _CompilerDispatcher(args.tool, args.compile_database, | 333 dispatcher = _CompilerDispatcher(args.tool, args.tool_args, |
| 334 args.compile_database, | |
| 325 source_filenames) | 335 source_filenames) |
| 326 dispatcher.Run() | 336 dispatcher.Run() |
| 327 # Filter out edits to files that aren't in the git repository, since it's not | 337 # Filter out edits to files that aren't in the git repository, since it's not |
| 328 # useful to modify files that aren't under source control--typically, these | 338 # useful to modify files that aren't under source control--typically, these |
| 329 # are generated files or files in a git submodule that's not part of Chromium. | 339 # are generated files or files in a git submodule that's not part of Chromium. |
| 330 _ApplyEdits({k: v | 340 _ApplyEdits({k: v |
| 331 for k, v in dispatcher.edits.iteritems() | 341 for k, v in dispatcher.edits.iteritems() |
| 332 if os.path.realpath(k) in filenames}) | 342 if os.path.realpath(k) in filenames}) |
| 333 return -dispatcher.failed_count | 343 return -dispatcher.failed_count |
| 334 | 344 |
| 335 | 345 |
| 336 if __name__ == '__main__': | 346 if __name__ == '__main__': |
| 337 sys.exit(main()) | 347 sys.exit(main()) |
| OLD | NEW |