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 11 matching lines...) Expand all Loading... |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | 28 |
29 import argparse | 29 import argparse |
30 import datetime | 30 import datetime |
31 import httplib | 31 import httplib |
| 32 import glob |
32 import imp | 33 import imp |
33 import json | 34 import json |
34 import os | 35 import os |
35 import re | 36 import re |
| 37 import shutil |
36 import subprocess | 38 import subprocess |
37 import sys | 39 import sys |
38 import textwrap | 40 import textwrap |
39 import time | 41 import time |
40 import urllib | 42 import urllib |
41 import urllib2 | 43 import urllib2 |
42 | 44 |
43 from git_recipes import GitRecipesMixin | 45 from git_recipes import GitRecipesMixin |
44 from git_recipes import GitFailedException | 46 from git_recipes import GitFailedException |
45 | 47 |
46 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" | 48 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME" |
47 BRANCHNAME = "BRANCHNAME" | 49 BRANCHNAME = "BRANCHNAME" |
48 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" | 50 DOT_GIT_LOCATION = "DOT_GIT_LOCATION" |
49 VERSION_FILE = "VERSION_FILE" | 51 VERSION_FILE = "VERSION_FILE" |
50 CHANGELOG_FILE = "CHANGELOG_FILE" | 52 CHANGELOG_FILE = "CHANGELOG_FILE" |
51 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" | 53 CHANGELOG_ENTRY_FILE = "CHANGELOG_ENTRY_FILE" |
52 COMMITMSG_FILE = "COMMITMSG_FILE" | 54 COMMITMSG_FILE = "COMMITMSG_FILE" |
53 PATCH_FILE = "PATCH_FILE" | 55 PATCH_FILE = "PATCH_FILE" |
54 | 56 |
| 57 # V8 base directory. |
| 58 DEFAULT_CWD = os.path.dirname( |
| 59 os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 60 |
55 | 61 |
56 def TextToFile(text, file_name): | 62 def TextToFile(text, file_name): |
57 with open(file_name, "w") as f: | 63 with open(file_name, "w") as f: |
58 f.write(text) | 64 f.write(text) |
59 | 65 |
60 | 66 |
61 def AppendToFile(text, file_name): | 67 def AppendToFile(text, file_name): |
62 with open(file_name, "a") as f: | 68 with open(file_name, "a") as f: |
63 f.write(text) | 69 f.write(text) |
64 | 70 |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 version_keys = map(int, version.split(".")) | 182 version_keys = map(int, version.split(".")) |
177 # Fill up to full version numbers to normalize comparison. | 183 # Fill up to full version numbers to normalize comparison. |
178 while len(version_keys) < 4: # pragma: no cover | 184 while len(version_keys) < 4: # pragma: no cover |
179 version_keys.append(0) | 185 version_keys.append(0) |
180 # Fill digits. | 186 # Fill digits. |
181 return ".".join(map("{0:04d}".format, version_keys)) | 187 return ".".join(map("{0:04d}".format, version_keys)) |
182 | 188 |
183 | 189 |
184 # Some commands don't like the pipe, e.g. calling vi from within the script or | 190 # Some commands don't like the pipe, e.g. calling vi from within the script or |
185 # from subscripts like git cl upload. | 191 # from subscripts like git cl upload. |
186 def Command(cmd, args="", prefix="", pipe=True): | 192 def Command(cmd, args="", prefix="", pipe=True, cwd=None): |
| 193 cwd = cwd or os.getcwd() |
187 # TODO(machenbach): Use timeout. | 194 # TODO(machenbach): Use timeout. |
188 cmd_line = "%s %s %s" % (prefix, cmd, args) | 195 cmd_line = "%s %s %s" % (prefix, cmd, args) |
189 print "Command: %s" % cmd_line | 196 print "Command: %s" % cmd_line |
| 197 print "in %s" % cwd |
190 sys.stdout.flush() | 198 sys.stdout.flush() |
191 try: | 199 try: |
192 if pipe: | 200 if pipe: |
193 return subprocess.check_output(cmd_line, shell=True) | 201 return subprocess.check_output(cmd_line, shell=True, cwd=cwd) |
194 else: | 202 else: |
195 return subprocess.check_call(cmd_line, shell=True) | 203 return subprocess.check_call(cmd_line, shell=True, cwd=cwd) |
196 except subprocess.CalledProcessError: | 204 except subprocess.CalledProcessError: |
197 return None | 205 return None |
198 finally: | 206 finally: |
199 sys.stdout.flush() | 207 sys.stdout.flush() |
200 sys.stderr.flush() | 208 sys.stderr.flush() |
201 | 209 |
202 | 210 |
203 # Wrapper for side effects. | 211 # Wrapper for side effects. |
204 class SideEffectHandler(object): # pragma: no cover | 212 class SideEffectHandler(object): # pragma: no cover |
205 def Call(self, fun, *args, **kwargs): | 213 def Call(self, fun, *args, **kwargs): |
206 return fun(*args, **kwargs) | 214 return fun(*args, **kwargs) |
207 | 215 |
208 def Command(self, cmd, args="", prefix="", pipe=True): | 216 def Command(self, cmd, args="", prefix="", pipe=True, cwd=None): |
209 return Command(cmd, args, prefix, pipe) | 217 return Command(cmd, args, prefix, pipe, cwd=cwd) |
210 | 218 |
211 def ReadLine(self): | 219 def ReadLine(self): |
212 return sys.stdin.readline().strip() | 220 return sys.stdin.readline().strip() |
213 | 221 |
214 def ReadURL(self, url, params=None): | 222 def ReadURL(self, url, params=None): |
215 # pylint: disable=E1121 | 223 # pylint: disable=E1121 |
216 url_fh = urllib2.urlopen(url, params, 60) | 224 url_fh = urllib2.urlopen(url, params, 60) |
217 try: | 225 try: |
218 return url_fh.read() | 226 return url_fh.read() |
219 finally: | 227 finally: |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 | 264 |
257 class Step(GitRecipesMixin): | 265 class Step(GitRecipesMixin): |
258 def __init__(self, text, requires, number, config, state, options, handler): | 266 def __init__(self, text, requires, number, config, state, options, handler): |
259 self._text = text | 267 self._text = text |
260 self._requires = requires | 268 self._requires = requires |
261 self._number = number | 269 self._number = number |
262 self._config = config | 270 self._config = config |
263 self._state = state | 271 self._state = state |
264 self._options = options | 272 self._options = options |
265 self._side_effect_handler = handler | 273 self._side_effect_handler = handler |
| 274 |
| 275 # The testing configuration might set a different default cwd. |
| 276 self.default_cwd = self._config.get("DEFAULT_CWD") or DEFAULT_CWD |
| 277 |
266 assert self._number >= 0 | 278 assert self._number >= 0 |
267 assert self._config is not None | 279 assert self._config is not None |
268 assert self._state is not None | 280 assert self._state is not None |
269 assert self._side_effect_handler is not None | 281 assert self._side_effect_handler is not None |
270 | 282 |
271 def __getitem__(self, key): | 283 def __getitem__(self, key): |
272 # Convenience method to allow direct [] access on step classes for | 284 # Convenience method to allow direct [] access on step classes for |
273 # manipulating the backed state dict. | 285 # manipulating the backed state dict. |
274 return self._state[key] | 286 return self._state[key] |
275 | 287 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 return result | 346 return result |
335 | 347 |
336 def ReadLine(self, default=None): | 348 def ReadLine(self, default=None): |
337 # Don't prompt in forced mode. | 349 # Don't prompt in forced mode. |
338 if self._options.force_readline_defaults and default is not None: | 350 if self._options.force_readline_defaults and default is not None: |
339 print "%s (forced)" % default | 351 print "%s (forced)" % default |
340 return default | 352 return default |
341 else: | 353 else: |
342 return self._side_effect_handler.ReadLine() | 354 return self._side_effect_handler.ReadLine() |
343 | 355 |
344 def Git(self, args="", prefix="", pipe=True, retry_on=None): | 356 def Command(self, name, args, cwd=None): |
345 cmd = lambda: self._side_effect_handler.Command("git", args, prefix, pipe) | 357 cmd = lambda: self._side_effect_handler.Command( |
| 358 name, args, "", True, cwd=cwd or self.default_cwd) |
| 359 return self.Retry(cmd, None, [5]) |
| 360 |
| 361 def Git(self, args="", prefix="", pipe=True, retry_on=None, cwd=None): |
| 362 cmd = lambda: self._side_effect_handler.Command( |
| 363 "git", args, prefix, pipe, cwd=cwd or self.default_cwd) |
346 result = self.Retry(cmd, retry_on, [5, 30]) | 364 result = self.Retry(cmd, retry_on, [5, 30]) |
347 if result is None: | 365 if result is None: |
348 raise GitFailedException("'git %s' failed." % args) | 366 raise GitFailedException("'git %s' failed." % args) |
349 return result | 367 return result |
350 | 368 |
351 def SVN(self, args="", prefix="", pipe=True, retry_on=None): | 369 def SVN(self, args="", prefix="", pipe=True, retry_on=None, cwd=None): |
352 cmd = lambda: self._side_effect_handler.Command("svn", args, prefix, pipe) | 370 cmd = lambda: self._side_effect_handler.Command( |
| 371 "svn", args, prefix, pipe, cwd=cwd or self.default_cwd) |
353 return self.Retry(cmd, retry_on, [5, 30]) | 372 return self.Retry(cmd, retry_on, [5, 30]) |
354 | 373 |
355 def Editor(self, args): | 374 def Editor(self, args): |
356 if self._options.requires_editor: | 375 if self._options.requires_editor: |
357 return self._side_effect_handler.Command(os.environ["EDITOR"], args, | 376 return self._side_effect_handler.Command( |
358 pipe=False) | 377 os.environ["EDITOR"], |
| 378 args, |
| 379 pipe=False, |
| 380 cwd=self.default_cwd) |
359 | 381 |
360 def ReadURL(self, url, params=None, retry_on=None, wait_plan=None): | 382 def ReadURL(self, url, params=None, retry_on=None, wait_plan=None): |
361 wait_plan = wait_plan or [3, 60, 600] | 383 wait_plan = wait_plan or [3, 60, 600] |
362 cmd = lambda: self._side_effect_handler.ReadURL(url, params) | 384 cmd = lambda: self._side_effect_handler.ReadURL(url, params) |
363 return self.Retry(cmd, retry_on, wait_plan) | 385 return self.Retry(cmd, retry_on, wait_plan) |
364 | 386 |
365 def GetDate(self): | 387 def GetDate(self): |
366 return self._side_effect_handler.GetDate() | 388 return self._side_effect_handler.GetDate() |
367 | 389 |
368 def Die(self, msg=""): | 390 def Die(self, msg=""): |
(...skipping 23 matching lines...) Expand all Loading... |
392 msg = "Can't continue. Please delete branch %s and try again." % name | 414 msg = "Can't continue. Please delete branch %s and try again." % name |
393 self.Die(msg) | 415 self.Die(msg) |
394 | 416 |
395 def InitialEnvironmentChecks(self): | 417 def InitialEnvironmentChecks(self): |
396 # Cancel if this is not a git checkout. | 418 # Cancel if this is not a git checkout. |
397 if not os.path.exists(self._config[DOT_GIT_LOCATION]): # pragma: no cover | 419 if not os.path.exists(self._config[DOT_GIT_LOCATION]): # pragma: no cover |
398 self.Die("This is not a git checkout, this script won't work for you.") | 420 self.Die("This is not a git checkout, this script won't work for you.") |
399 | 421 |
400 # Cancel if EDITOR is unset or not executable. | 422 # Cancel if EDITOR is unset or not executable. |
401 if (self._options.requires_editor and (not os.environ.get("EDITOR") or | 423 if (self._options.requires_editor and (not os.environ.get("EDITOR") or |
402 Command("which", os.environ["EDITOR"]) is None)): # pragma: no cover | 424 self.Command( |
| 425 "which", os.environ["EDITOR"]) is None)): # pragma: no cover |
403 self.Die("Please set your EDITOR environment variable, you'll need it.") | 426 self.Die("Please set your EDITOR environment variable, you'll need it.") |
404 | 427 |
405 def CommonPrepare(self): | 428 def CommonPrepare(self): |
406 # Check for a clean workdir. | 429 # Check for a clean workdir. |
407 if not self.GitIsWorkdirClean(): # pragma: no cover | 430 if not self.GitIsWorkdirClean(): # pragma: no cover |
408 self.Die("Workspace is not clean. Please commit or undo your changes.") | 431 self.Die("Workspace is not clean. Please commit or undo your changes.") |
409 | 432 |
410 # Persist current branch. | 433 # Persist current branch. |
411 self["current_branch"] = self.GitCurrentBranch() | 434 self["current_branch"] = self.GitCurrentBranch() |
412 | 435 |
413 # Fetch unfetched revisions. | 436 # Fetch unfetched revisions. |
414 self.GitSVNFetch() | 437 self.GitSVNFetch() |
415 | 438 |
416 def PrepareBranch(self): | 439 def PrepareBranch(self): |
417 # Delete the branch that will be created later if it exists already. | 440 # Delete the branch that will be created later if it exists already. |
418 self.DeleteBranch(self._config[BRANCHNAME]) | 441 self.DeleteBranch(self._config[BRANCHNAME]) |
419 | 442 |
420 def CommonCleanup(self): | 443 def CommonCleanup(self): |
421 self.GitCheckout(self["current_branch"]) | 444 self.GitCheckout(self["current_branch"]) |
422 if self._config[BRANCHNAME] != self["current_branch"]: | 445 if self._config[BRANCHNAME] != self["current_branch"]: |
423 self.GitDeleteBranch(self._config[BRANCHNAME]) | 446 self.GitDeleteBranch(self._config[BRANCHNAME]) |
424 | 447 |
425 # Clean up all temporary files. | 448 # Clean up all temporary files. |
426 Command("rm", "-f %s*" % self._config[PERSISTFILE_BASENAME]) | 449 for f in glob.iglob("%s*" % self._config[PERSISTFILE_BASENAME]): |
| 450 if os.path.isfile(f): |
| 451 os.remove(f) |
| 452 if os.path.isdir(f): |
| 453 shutil.rmtree(f) |
427 | 454 |
428 def ReadAndPersistVersion(self, prefix=""): | 455 def ReadAndPersistVersion(self, prefix=""): |
429 def ReadAndPersist(var_name, def_name): | 456 def ReadAndPersist(var_name, def_name): |
430 match = re.match(r"^#define %s\s+(\d*)" % def_name, line) | 457 match = re.match(r"^#define %s\s+(\d*)" % def_name, line) |
431 if match: | 458 if match: |
432 value = match.group(1) | 459 value = match.group(1) |
433 self["%s%s" % (prefix, var_name)] = value | 460 self["%s%s" % (prefix, var_name)] = value |
434 for line in LinesInFile(self._config[VERSION_FILE]): | 461 for line in LinesInFile(self._config[VERSION_FILE]): |
435 for (var_name, def_name) in [("major", "MAJOR_VERSION"), | 462 for (var_name, def_name) in [("major", "MAJOR_VERSION"), |
436 ("minor", "MINOR_VERSION"), | 463 ("minor", "MINOR_VERSION"), |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 help="Path to the script mapping google accounts.") | 627 help="Path to the script mapping google accounts.") |
601 parser.add_argument("-r", "--reviewer", default="", | 628 parser.add_argument("-r", "--reviewer", default="", |
602 help="The account name to be used for reviews.") | 629 help="The account name to be used for reviews.") |
603 parser.add_argument("--sheriff", default=False, action="store_true", | 630 parser.add_argument("--sheriff", default=False, action="store_true", |
604 help=("Determine current sheriff to review CLs. On " | 631 help=("Determine current sheriff to review CLs. On " |
605 "success, this will overwrite the reviewer " | 632 "success, this will overwrite the reviewer " |
606 "option.")) | 633 "option.")) |
607 parser.add_argument("-s", "--step", | 634 parser.add_argument("-s", "--step", |
608 help="Specify the step where to start work. Default: 0.", | 635 help="Specify the step where to start work. Default: 0.", |
609 default=0, type=int) | 636 default=0, type=int) |
610 | |
611 self._PrepareOptions(parser) | 637 self._PrepareOptions(parser) |
612 | 638 |
613 if args is None: # pragma: no cover | 639 if args is None: # pragma: no cover |
614 options = parser.parse_args() | 640 options = parser.parse_args() |
615 else: | 641 else: |
616 options = parser.parse_args(args) | 642 options = parser.parse_args(args) |
617 | 643 |
618 # Process common options. | 644 # Process common options. |
619 if options.step < 0: # pragma: no cover | 645 if options.step < 0: # pragma: no cover |
620 print "Bad step number %d" % options.step | 646 print "Bad step number %d" % options.step |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
654 for (number, step_class) in enumerate(step_classes): | 680 for (number, step_class) in enumerate(step_classes): |
655 steps.append(MakeStep(step_class, number, self._state, self._config, | 681 steps.append(MakeStep(step_class, number, self._state, self._config, |
656 options, self._side_effect_handler)) | 682 options, self._side_effect_handler)) |
657 for step in steps[options.step:]: | 683 for step in steps[options.step:]: |
658 if step.Run(): | 684 if step.Run(): |
659 return 0 | 685 return 0 |
660 return 0 | 686 return 0 |
661 | 687 |
662 def Run(self, args=None): | 688 def Run(self, args=None): |
663 return self.RunSteps(self._Steps(), args) | 689 return self.RunSteps(self._Steps(), args) |
OLD | NEW |