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_params, 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 build_directory: Directory that contains the compile database. | 125 build_directory: Directory that contains the compile database. |
126 filename: The file to run the tool over. | 126 filename: The file to run the tool over. |
127 | 127 |
128 Returns: | 128 Returns: |
129 A dictionary that must contain the key "status" and a boolean value | 129 A dictionary that must contain the key "status" and a boolean value |
130 associated with it. | 130 associated with it. |
131 | 131 |
132 If status is True, then the generated edits are stored with the key "edits" | 132 If status is True, then the generated edits are stored with the key "edits" |
133 in the dictionary. | 133 in the dictionary. |
134 | 134 |
135 Otherwise, the filename and the output from stderr are associated with the | 135 Otherwise, the filename and the output from stderr are associated with the |
136 keys "filename" and "stderr" respectively. | 136 keys "filename" and "stderr" respectively. |
137 """ | 137 """ |
138 command = subprocess.Popen( | 138 command = subprocess.Popen( |
139 (toolname, '-p', build_directory, filename), | 139 (toolname, '-p', build_directory, filename) if tool_params is None else |
140 (toolname, '-p', build_directory, filename, tool_params), | |
140 stdout=subprocess.PIPE, | 141 stdout=subprocess.PIPE, |
141 stderr=subprocess.PIPE) | 142 stderr=subprocess.PIPE) |
142 stdout, stderr = command.communicate() | 143 stdout, stderr = command.communicate() |
143 if command.returncode != 0: | 144 if command.returncode != 0: |
144 return {'status': False, 'filename': filename, 'stderr': stderr} | 145 return {'status': False, 'filename': filename, 'stderr': stderr} |
145 else: | 146 else: |
146 return {'status': True, | 147 return {'status': True, |
147 'edits': _ExtractEditsFromStdout(build_directory, stdout)} | 148 'edits': _ExtractEditsFromStdout(build_directory, stdout)} |
148 | 149 |
149 | 150 |
150 class _CompilerDispatcher(object): | 151 class _CompilerDispatcher(object): |
151 """Multiprocessing controller for running clang tools in parallel.""" | 152 """Multiprocessing controller for running clang tools in parallel.""" |
152 | 153 |
153 def __init__(self, toolname, build_directory, filenames): | 154 def __init__(self, toolname, tool_params, build_directory, filenames): |
154 """Initializer method. | 155 """Initializer method. |
155 | 156 |
156 Args: | 157 Args: |
157 toolname: Path to the tool to execute. | 158 toolname: Path to the tool to execute. |
158 build_directory: Directory that contains the compile database. | 159 build_directory: Directory that contains the compile database. |
159 filenames: The files to run the tool over. | 160 filenames: The files to run the tool over. |
160 """ | 161 """ |
161 self.__toolname = toolname | 162 self.__toolname = toolname |
163 self.__tool_params = tool_params | |
162 self.__build_directory = build_directory | 164 self.__build_directory = build_directory |
163 self.__filenames = filenames | 165 self.__filenames = filenames |
164 self.__success_count = 0 | 166 self.__success_count = 0 |
165 self.__failed_count = 0 | 167 self.__failed_count = 0 |
166 self.__edit_count = 0 | 168 self.__edit_count = 0 |
167 self.__edits = collections.defaultdict(list) | 169 self.__edits = collections.defaultdict(list) |
168 | 170 |
169 @property | 171 @property |
170 def edits(self): | 172 def edits(self): |
171 return self.__edits | 173 return self.__edits |
172 | 174 |
173 @property | 175 @property |
174 def failed_count(self): | 176 def failed_count(self): |
175 return self.__failed_count | 177 return self.__failed_count |
176 | 178 |
177 def Run(self): | 179 def Run(self): |
178 """Does the grunt work.""" | 180 """Does the grunt work.""" |
179 pool = multiprocessing.Pool() | 181 pool = multiprocessing.Pool() |
180 result_iterator = pool.imap_unordered( | 182 result_iterator = pool.imap_unordered( |
181 functools.partial(_ExecuteTool, self.__toolname, | 183 functools.partial(_ExecuteTool, self.__toolname, self.__tool_params, |
182 self.__build_directory), self.__filenames) | 184 self.__build_directory), |
185 self.__filenames) | |
183 for result in result_iterator: | 186 for result in result_iterator: |
184 self.__ProcessResult(result) | 187 self.__ProcessResult(result) |
185 sys.stdout.write('\n') | 188 sys.stdout.write('\n') |
186 sys.stdout.flush() | 189 sys.stdout.flush() |
187 | 190 |
188 def __ProcessResult(self, result): | 191 def __ProcessResult(self, result): |
189 """Handles result processing. | 192 """Handles result processing. |
190 | 193 |
191 Args: | 194 Args: |
192 result: The result dictionary returned by _ExecuteTool. | 195 result: The result dictionary returned by _ExecuteTool. |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
292 '--generate-compdb', | 295 '--generate-compdb', |
293 action='store_true', | 296 action='store_true', |
294 help='regenerate the compile database before running the tool') | 297 help='regenerate the compile database before running the tool') |
295 parser.add_argument( | 298 parser.add_argument( |
296 'compile_database', | 299 'compile_database', |
297 help='path to the directory that contains the compile database') | 300 help='path to the directory that contains the compile database') |
298 parser.add_argument( | 301 parser.add_argument( |
299 'path_filter', | 302 'path_filter', |
300 nargs='*', | 303 nargs='*', |
301 help='optional paths to filter what files the tool is run on') | 304 help='optional paths to filter what files the tool is run on') |
305 parser.add_argument( | |
306 '--tool-params', type=str, | |
307 help='parameters passed to the tool') | |
battre
2016/10/26 14:29:18
please fix indentation
Ramin Halavati
2016/10/27 09:40:08
Done.
| |
302 args = parser.parse_args() | 308 args = parser.parse_args() |
303 | 309 |
304 os.environ['PATH'] = '%s%s%s' % ( | 310 os.environ['PATH'] = '%s%s%s' % ( |
305 os.path.abspath(os.path.join( | 311 os.path.abspath(os.path.join( |
306 os.path.dirname(__file__), | 312 os.path.dirname(__file__), |
307 '../../../third_party/llvm-build/Release+Asserts/bin')), | 313 '../../../third_party/llvm-build/Release+Asserts/bin')), |
308 os.pathsep, | 314 os.pathsep, |
309 os.environ['PATH']) | 315 os.environ['PATH']) |
310 | 316 |
311 if args.generate_compdb: | 317 if args.generate_compdb: |
312 compile_db.GenerateWithNinja(args.compile_database) | 318 compile_db.GenerateWithNinja(args.compile_database) |
313 | 319 |
314 if args.all: | 320 if args.all: |
315 filenames = set(_GetFilesFromCompileDB(args.compile_database)) | 321 filenames = set(_GetFilesFromCompileDB(args.compile_database)) |
316 source_filenames = filenames | 322 source_filenames = filenames |
317 else: | 323 else: |
318 filenames = set(_GetFilesFromGit(args.path_filter)) | 324 filenames = set(_GetFilesFromGit(args.path_filter)) |
319 # Filter out files that aren't C/C++/Obj-C/Obj-C++. | 325 # Filter out files that aren't C/C++/Obj-C/Obj-C++. |
320 extensions = frozenset(('.c', '.cc', '.cpp', '.m', '.mm')) | 326 extensions = frozenset(('.c', '.cc', '.cpp', '.m', '.mm')) |
321 source_filenames = [f | 327 source_filenames = [f |
322 for f in filenames | 328 for f in filenames |
323 if os.path.splitext(f)[1] in extensions] | 329 if os.path.splitext(f)[1] in extensions] |
324 dispatcher = _CompilerDispatcher(args.tool, args.compile_database, | 330 dispatcher = _CompilerDispatcher(args.tool, args.tool_params, |
331 args.compile_database, | |
325 source_filenames) | 332 source_filenames) |
326 dispatcher.Run() | 333 dispatcher.Run() |
327 # Filter out edits to files that aren't in the git repository, since it's not | 334 # 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 | 335 # 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. | 336 # are generated files or files in a git submodule that's not part of Chromium. |
330 _ApplyEdits({k: v | 337 _ApplyEdits({k: v |
331 for k, v in dispatcher.edits.iteritems() | 338 for k, v in dispatcher.edits.iteritems() |
332 if os.path.realpath(k) in filenames}) | 339 if os.path.realpath(k) in filenames}) |
333 return -dispatcher.failed_count | 340 return -dispatcher.failed_count |
334 | 341 |
335 | 342 |
336 if __name__ == '__main__': | 343 if __name__ == '__main__': |
337 sys.exit(main()) | 344 sys.exit(main()) |
OLD | NEW |