OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2013 the V8 project authors. All rights reserved. | 2 # Copyright 2013 the V8 project authors. All rights reserved. |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following | 10 # copyright notice, this list of conditions and the following |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 def FileToText(file_name): | 63 def FileToText(file_name): |
64 with open(file_name) as f: | 64 with open(file_name) as f: |
65 return f.read() | 65 return f.read() |
66 | 66 |
67 | 67 |
68 def MSub(rexp, replacement, text): | 68 def MSub(rexp, replacement, text): |
69 return re.sub(rexp, replacement, text, flags=re.MULTILINE) | 69 return re.sub(rexp, replacement, text, flags=re.MULTILINE) |
70 | 70 |
71 | 71 |
72 def Fill80(line): | 72 def Fill80(line): |
| 73 # Replace tabs and remove surrounding space. |
| 74 line = re.sub(r"\t", r" ", line.strip()) |
| 75 |
| 76 # Format with 8 characters indentation and line width 80. |
73 return textwrap.fill(line, width=80, initial_indent=" ", | 77 return textwrap.fill(line, width=80, initial_indent=" ", |
74 subsequent_indent=" ") | 78 subsequent_indent=" ") |
75 | 79 |
76 | 80 |
77 def GetLastChangeLogEntries(change_log_file): | 81 def GetLastChangeLogEntries(change_log_file): |
78 result = [] | 82 result = [] |
79 for line in LinesInFile(change_log_file): | 83 for line in LinesInFile(change_log_file): |
80 if re.search(r"^\d{4}-\d{2}-\d{2}:", line) and result: break | 84 if re.search(r"^\d{4}-\d{2}-\d{2}:", line) and result: break |
81 result.append(line) | 85 result.append(line) |
82 return "".join(result) | 86 return "".join(result) |
83 | 87 |
84 | 88 |
85 def MakeComment(text): | 89 def MakeComment(text): |
86 return MSub(r"^( ?)", "#", text) | 90 return MSub(r"^( ?)", "#", text) |
87 | 91 |
88 | 92 |
89 def StripComments(text): | 93 def StripComments(text): |
90 # Use split not splitlines to keep terminal newlines. | 94 # Use split not splitlines to keep terminal newlines. |
91 return "\n".join(filter(lambda x: not x.startswith("#"), text.split("\n"))) | 95 return "\n".join(filter(lambda x: not x.startswith("#"), text.split("\n"))) |
92 | 96 |
93 | 97 |
94 def MakeChangeLogBody(commit_messages, auto_format=False): | 98 def MakeChangeLogBody(commit_messages, auto_format=False): |
95 result = "" | 99 result = "" |
96 added_titles = set() | 100 added_titles = set() |
97 for (title, body, author) in commit_messages: | 101 for (title, body, author) in commit_messages: |
98 # TODO(machenbach): Reload the commit description from rietveld in order to | 102 # TODO(machenbach): Reload the commit description from rietveld in order to |
99 # catch late changes. | 103 # catch late changes. |
100 title = title.rstrip() | 104 title = title.strip() |
101 if auto_format: | 105 if auto_format: |
102 # Only add commits that set the LOG flag correctly. | 106 # Only add commits that set the LOG flag correctly. |
103 log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:Y(?:ES)?)|TRUE" | 107 log_exp = r"^[ \t]*LOG[ \t]*=[ \t]*(?:Y(?:ES)?)|TRUE" |
104 if not re.search(log_exp, body, flags=re.I | re.M): | 108 if not re.search(log_exp, body, flags=re.I | re.M): |
105 continue | 109 continue |
106 # Never include reverts. | 110 # Never include reverts. |
107 if title.startswith("Revert "): | 111 if title.startswith("Revert "): |
108 continue | 112 continue |
109 # Don't include duplicates. | 113 # Don't include duplicates. |
110 if title in added_titles: | 114 if title in added_titles: |
111 continue | 115 continue |
112 | 116 |
113 # TODO(machenbach): Let python do all formatting. Get raw git title, attach | 117 # TODO(machenbach): Let python do all formatting. Get raw git title, attach |
114 # issue and add/move dot to the end - all in one line. Make formatting and | 118 # issue and add/move dot to the end - all in one line. Make formatting and |
115 # indentation afterwards. | 119 # indentation afterwards. |
116 | 120 |
117 # Add the commit's title line. | 121 # Add the commit's title line. |
118 result += "%s\n" % title | 122 result += "%s\n" % Fill80(title) |
119 added_titles.add(title) | 123 added_titles.add(title) |
120 | 124 |
121 # Add bug references. | 125 # Add bug references. |
122 result += MakeChangeLogBugReference(body) | 126 result += MakeChangeLogBugReference(body) |
123 | 127 |
124 # Append the commit's author for reference if not in auto-format mode. | 128 # Append the commit's author for reference if not in auto-format mode. |
125 if not auto_format: | 129 if not auto_format: |
126 result += "%s\n" % author.rstrip() | 130 result += "%s\n" % Fill80("(%s)" % author.strip()) |
127 | 131 |
128 result += "\n" | 132 result += "\n" |
129 return result | 133 return result |
130 | 134 |
131 | 135 |
132 def MakeChangeLogBugReference(body): | 136 def MakeChangeLogBugReference(body): |
133 """Grep for "BUG=xxxx" lines in the commit message and convert them to | 137 """Grep for "BUG=xxxx" lines in the commit message and convert them to |
134 "(issue xxxx)". | 138 "(issue xxxx)". |
135 """ | 139 """ |
136 crbugs = [] | 140 crbugs = [] |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 url_fh = urllib2.urlopen(url, None, 60) | 202 url_fh = urllib2.urlopen(url, None, 60) |
199 try: | 203 try: |
200 return url_fh.read() | 204 return url_fh.read() |
201 finally: | 205 finally: |
202 url_fh.close() | 206 url_fh.close() |
203 | 207 |
204 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() | 208 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler() |
205 | 209 |
206 | 210 |
207 class Step(object): | 211 class Step(object): |
208 def __init__(self, text="", requires=None): | 212 def __init__(self, text, requires, number, config, state, options, handler): |
209 self._text = text | 213 self._text = text |
210 self._number = -1 | |
211 self._options = None | |
212 self._requires = requires | 214 self._requires = requires |
213 self._side_effect_handler = DEFAULT_SIDE_EFFECT_HANDLER | |
214 | |
215 def SetNumber(self, number): | |
216 self._number = number | 215 self._number = number |
217 | |
218 def SetConfig(self, config): | |
219 self._config = config | 216 self._config = config |
220 | |
221 def SetState(self, state): | |
222 self._state = state | 217 self._state = state |
223 | |
224 def SetOptions(self, options): | |
225 self._options = options | 218 self._options = options |
226 | |
227 def SetSideEffectHandler(self, handler): | |
228 self._side_effect_handler = handler | 219 self._side_effect_handler = handler |
| 220 assert self._number >= 0 |
| 221 assert self._config is not None |
| 222 assert self._state is not None |
| 223 assert self._side_effect_handler is not None |
229 | 224 |
230 def Config(self, key): | 225 def Config(self, key): |
231 return self._config[key] | 226 return self._config[key] |
232 | 227 |
233 def Run(self): | 228 def Run(self): |
234 assert self._number >= 0 | |
235 assert self._config is not None | |
236 assert self._state is not None | |
237 assert self._side_effect_handler is not None | |
238 if self._requires: | 229 if self._requires: |
239 self.RestoreIfUnset(self._requires) | 230 self.RestoreIfUnset(self._requires) |
240 if not self._state[self._requires]: | 231 if not self._state[self._requires]: |
241 return | 232 return |
242 print ">>> Step %d: %s" % (self._number, self._text) | 233 print ">>> Step %d: %s" % (self._number, self._text) |
243 self.RunStep() | 234 self.RunStep() |
244 | 235 |
245 def RunStep(self): | 236 def RunStep(self): |
246 raise NotImplementedError | 237 raise NotImplementedError |
247 | 238 |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 answer = self.ReadLine() | 395 answer = self.ReadLine() |
405 | 396 |
406 # Takes a file containing the patch to apply as first argument. | 397 # Takes a file containing the patch to apply as first argument. |
407 def ApplyPatch(self, patch_file, reverse_patch=""): | 398 def ApplyPatch(self, patch_file, reverse_patch=""): |
408 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) | 399 args = "apply --index --reject %s \"%s\"" % (reverse_patch, patch_file) |
409 if self.Git(args) is None: | 400 if self.Git(args) is None: |
410 self.WaitForResolvingConflicts(patch_file) | 401 self.WaitForResolvingConflicts(patch_file) |
411 | 402 |
412 | 403 |
413 class UploadStep(Step): | 404 class UploadStep(Step): |
414 def __init__(self): | 405 MESSAGE = "Upload for code review." |
415 Step.__init__(self, "Upload for code review.") | |
416 | 406 |
417 def RunStep(self): | 407 def RunStep(self): |
418 if self._options.r: | 408 if self._options.r: |
419 print "Using account %s for review." % self._options.r | 409 print "Using account %s for review." % self._options.r |
420 reviewer = self._options.r | 410 reviewer = self._options.r |
421 else: | 411 else: |
422 print "Please enter the email address of a V8 reviewer for your patch: ", | 412 print "Please enter the email address of a V8 reviewer for your patch: ", |
423 self.DieInForcedMode("A reviewer must be specified in forced mode.") | 413 self.DieInForcedMode("A reviewer must be specified in forced mode.") |
424 reviewer = self.ReadLine() | 414 reviewer = self.ReadLine() |
425 force_flag = " -f" if self._options.f else "" | 415 force_flag = " -f" if self._options.f else "" |
426 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) | 416 args = "cl upload -r \"%s\" --send-mail%s" % (reviewer, force_flag) |
427 # TODO(machenbach): Check output in forced mode. Verify that all required | 417 # TODO(machenbach): Check output in forced mode. Verify that all required |
428 # base files were uploaded, if not retry. | 418 # base files were uploaded, if not retry. |
429 if self.Git(args, pipe=False) is None: | 419 if self.Git(args, pipe=False) is None: |
430 self.Die("'git cl upload' failed, please try again.") | 420 self.Die("'git cl upload' failed, please try again.") |
431 | 421 |
432 | 422 |
| 423 def MakeStep(step_class=Step, number=0, state=None, config=None, |
| 424 options=None, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
| 425 # Allow to pass in empty dictionaries. |
| 426 state = state if state is not None else {} |
| 427 config = config if config is not None else {} |
| 428 |
| 429 try: |
| 430 message = step_class.MESSAGE |
| 431 except AttributeError: |
| 432 message = step_class.__name__ |
| 433 try: |
| 434 requires = step_class.REQUIRES |
| 435 except AttributeError: |
| 436 requires = None |
| 437 |
| 438 return step_class(message, requires, number=number, config=config, |
| 439 state=state, options=options, |
| 440 handler=side_effect_handler) |
| 441 |
| 442 |
433 def RunScript(step_classes, | 443 def RunScript(step_classes, |
434 config, | 444 config, |
435 options, | 445 options, |
436 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): | 446 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): |
437 state = {} | 447 state = {} |
438 steps = [] | 448 steps = [] |
439 number = 0 | 449 for (number, step_class) in enumerate(step_classes): |
440 | 450 steps.append(MakeStep(step_class, number, state, config, |
441 for step_class in step_classes: | 451 options, side_effect_handler)) |
442 # TODO(machenbach): Factory methods. | |
443 step = step_class() | |
444 step.SetNumber(number) | |
445 step.SetConfig(config) | |
446 step.SetOptions(options) | |
447 step.SetState(state) | |
448 step.SetSideEffectHandler(side_effect_handler) | |
449 steps.append(step) | |
450 number += 1 | |
451 | 452 |
452 for step in steps[options.s:]: | 453 for step in steps[options.s:]: |
453 step.Run() | 454 step.Run() |
OLD | NEW |