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

Side by Side Diff: tools/clang/scripts/run_tool.py

Issue 12746010: Implement clang tool that converts std::string("") to std::string(). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Whee Created 7 years, 8 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 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.
Nico 2013/03/27 16:35:29 Doesn't say why this is used instead of tooling's
dcheng 2013/03/28 00:10:17 Done now =)
5
6 import collections
7 import functools
8 import multiprocessing
9 import subprocess
10 import sys
11
12
13 """Represents an edit to a file."""
14 Edit= collections.namedtuple(
15 'Edit', ('edit_type', 'offset', 'length', 'replacement'))
16
17
18 """Gets the list of files to run the tool over from git."""
19 def _GetFilesFromGit(paths = None):
20 try:
21 args = ['git', 'ls-files']
22 if paths:
23 args.extend(paths)
24 command = subprocess.Popen(args, stdout=subprocess.PIPE)
25 output, _ = command.communicate()
26 return [f for f in output.splitlines() if f[-3:] == '.cc']
Nico 2013/03/27 16:35:29 f.endswith('.cc') (there's also .m, .mm, .cpp, .h
dcheng 2013/03/28 00:10:17 1) I didn't really want to parse compile_commands.
27 except OSError:
28 # TODO(dcheng): Handle errors.
29 sys.exit(1)
30
31
32 """Executes the tool.
33
34 This is outside the class so it can be pickled."""
35 def _ExecuteTool(toolname, build_directory, filename):
36 # TODO(dcheng): Scrape output and handle errors.
37 command = subprocess.Popen((toolname, '-p', build_directory, filename),
38 stdout=subprocess.PIPE,
39 stderr=subprocess.PIPE)
40 stdout, stderr = command.communicate()
41 if command.returncode != 0:
42 return {'status': False, 'filename': filename, 'stderr': stderr}
43 else:
44 return {'status': True, 'stdout': stdout}
45
46
47 """God class that does everything."""
48 class _CompilerDispatcher(object):
49 def __init__(self, toolname, build_directory, filenames):
50 self.__toolname = toolname
51 self.__build_directory = build_directory
52 self.__filenames = filenames
53 self.__success_count = 0
54 self.__failed_count = 0
55 self.__edits = collections.defaultdict(list)
56
57 @property
58 def edits(self):
59 return self.__edits
60
61 """God function that does everything."""
62 def Run(self):
63 # TODO(dcheng): Add some timing.
64 pool = multiprocessing.Pool()
65 result_iterator = pool.imap_unordered(
66 functools.partial(_ExecuteTool, self.__toolname,
67 self.__build_directory),
68 self.__filenames)
69 for result in result_iterator:
70 self.__ProcessResult(result)
71 sys.stdout.write('\n')
72 sys.stdout.flush()
73
74 """Process the result."""
75 def __ProcessResult(self, result):
76 if result['status']:
77 self.__success_count += 1
78 self.__AddEditsFromStdout(result['stdout'])
79 else:
80 self.__failed_count += 1
81 sys.stdout.write('\nFailed to process %s\n' % result['filename'])
82 sys.stdout.write(result['stderr'])
83 sys.stdout.write('\n')
84 percentage = (
85 float(self.__success_count + self.__failed_count) /
86 len(self.__filenames)) * 100
87 sys.stdout.write('Succeeded: %d, Failed: %d [%.2f%%]\r' % (
88 self.__success_count, self.__failed_count, percentage))
89 sys.stdout.flush()
90
91 """Extracts and add the list of edits generated on the tool's stdout."""
92 def __AddEditsFromStdout(self, stdout):
93 lines = stdout.splitlines()
94 start_index = lines.index('==== BEGIN EDITS ====')
95 end_index = lines.index('==== END EDITS ====')
96 for line in lines[start_index + 1:end_index]:
97 edit_type, path, offset, length, replacement = line.split(':', 4)
98 # TODO(dcheng): [6:] is a horrible hack to trim off ../../ and is fragile.
99 self.__edits[path[6:]].append(
100 Edit(edit_type, int(offset), int(length), replacement))
101
102
103 """Applies the edits!"""
104 def _ApplyEdits(edits):
105 edit_count = 0
106 for k, v in edits.iteritems():
107 # Sort the edits and iterate through them in reverse order. Sorting allows
108 # duplicate edits to be quickly skipped, while reversing means that
109 # subsequent edits don't need to have their offsets updated with each edit
110 # applied.
111 v.sort()
112 last_edit = None
113 with open(k, 'r+') as f:
114 contents = f.read()
115 for edit in reversed(v):
116 if edit == last_edit:
117 continue
118 last_edit = edit
119 # TODO(dcheng): Would bytearray be better?
120 contents = (contents[:edit.offset] +
121 edit.replacement +
122 contents[edit.offset + edit.length:])
123 edit_count += 1
124 f.seek(0)
125 f.truncate()
126 f.write(contents)
127 print 'Applied %d edits to %d files' % (edit_count, len(edits))
128
129
130 def main(argv):
131 # TODO(dcheng): Assert that we're running from chrome/src.
132 filenames = frozenset(_GetFilesFromGit(argv[2:]))
133 dispatcher = _CompilerDispatcher(argv[0], argv[1], filenames)
134 dispatcher.Run()
135 # Filter out any files that aren't in the git repository, since it's not
136 # useful to modify files that aren't under source control--typically, these
137 # are generated files or files in a git submodule that's not part of Chrome.
138 filtered_edits = {k : v for k, v in dispatcher.edits.iteritems()
139 if k in filenames}
140 _ApplyEdits(filtered_edits)
141
142
143 if __name__ == '__main__':
144 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698