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

Side by Side Diff: tools/push-to-trunk/common_includes.py

Issue 196133017: Experimental parser: merge r19949 (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/parser
Patch Set: Created 6 years, 9 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
« no previous file with comments | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/git_recipes.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
11 # disclaimer in the documentation and/or other materials provided 11 # disclaimer in the documentation and/or other materials provided
12 # with the distribution. 12 # with the distribution.
13 # * Neither the name of Google Inc. nor the names of its 13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived 14 # contributors may be used to endorse or promote products derived
15 # from this software without specific prior written permission. 15 # from this software without specific prior written permission.
16 # 16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
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 datetime 30 import datetime
30 import json 31 import json
31 import os 32 import os
32 import re 33 import re
33 import subprocess 34 import subprocess
34 import sys 35 import sys
35 import textwrap 36 import textwrap
36 import time 37 import time
37 import urllib2 38 import urllib2
38 39
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 try: 185 try:
185 if pipe: 186 if pipe:
186 return subprocess.check_output(cmd_line, shell=True) 187 return subprocess.check_output(cmd_line, shell=True)
187 else: 188 else:
188 return subprocess.check_call(cmd_line, shell=True) 189 return subprocess.check_call(cmd_line, shell=True)
189 except subprocess.CalledProcessError: 190 except subprocess.CalledProcessError:
190 return None 191 return None
191 192
192 193
193 # Wrapper for side effects. 194 # Wrapper for side effects.
194 class SideEffectHandler(object): 195 class SideEffectHandler(object): # pragma: no cover
195 def Call(self, fun, *args, **kwargs): 196 def Call(self, fun, *args, **kwargs):
196 return fun(*args, **kwargs) 197 return fun(*args, **kwargs)
197 198
198 def Command(self, cmd, args="", prefix="", pipe=True): 199 def Command(self, cmd, args="", prefix="", pipe=True):
199 return Command(cmd, args, prefix, pipe) 200 return Command(cmd, args, prefix, pipe)
200 201
201 def ReadLine(self): 202 def ReadLine(self):
202 return sys.stdin.readline().strip() 203 return sys.stdin.readline().strip()
203 204
204 def ReadURL(self, url, params=None): 205 def ReadURL(self, url, params=None):
(...skipping 14 matching lines...) Expand all
219 220
220 221
221 class NoRetryException(Exception): 222 class NoRetryException(Exception):
222 pass 223 pass
223 224
224 225
225 class GitFailedException(Exception): 226 class GitFailedException(Exception):
226 pass 227 pass
227 228
228 229
229 class CommonOptions(object):
230 def __init__(self, options, manual=True):
231 self.requires_editor = True
232 self.wait_for_lgtm = True
233 self.step = options.step
234 self.force_readline_defaults = not manual
235 self.force_upload = not manual
236 self.manual = manual
237 self.reviewer = getattr(options, 'reviewer', "")
238 self.author = getattr(options, 'author', "")
239
240
241 class Step(GitRecipesMixin): 230 class Step(GitRecipesMixin):
242 def __init__(self, text, requires, number, config, state, options, handler): 231 def __init__(self, text, requires, number, config, state, options, handler):
243 self._text = text 232 self._text = text
244 self._requires = requires 233 self._requires = requires
245 self._number = number 234 self._number = number
246 self._config = config 235 self._config = config
247 self._state = state 236 self._state = state
248 self._options = options 237 self._options = options
249 self._side_effect_handler = handler 238 self._side_effect_handler = handler
250 assert self._number >= 0 239 assert self._number >= 0
251 assert self._config is not None 240 assert self._config is not None
252 assert self._state is not None 241 assert self._state is not None
253 assert self._side_effect_handler is not None 242 assert self._side_effect_handler is not None
254 assert isinstance(options, CommonOptions)
255 243
256 def __getitem__(self, key): 244 def __getitem__(self, key):
257 # Convenience method to allow direct [] access on step classes for 245 # Convenience method to allow direct [] access on step classes for
258 # manipulating the backed state dict. 246 # manipulating the backed state dict.
259 return self._state[key] 247 return self._state[key]
260 248
261 def __setitem__(self, key, value): 249 def __setitem__(self, key, value):
262 # Convenience method to allow direct [] access on step classes for 250 # Convenience method to allow direct [] access on step classes for
263 # manipulating the backed state dict. 251 # manipulating the backed state dict.
264 self._state[key] = value 252 self._state[key] = value
(...skipping 10 matching lines...) Expand all
275 # Skip step if requirement is not met. 263 # Skip step if requirement is not met.
276 if self._requires and not self._state.get(self._requires): 264 if self._requires and not self._state.get(self._requires):
277 return 265 return
278 266
279 print ">>> Step %d: %s" % (self._number, self._text) 267 print ">>> Step %d: %s" % (self._number, self._text)
280 self.RunStep() 268 self.RunStep()
281 269
282 # Persist state. 270 # Persist state.
283 TextToFile(json.dumps(self._state), state_file) 271 TextToFile(json.dumps(self._state), state_file)
284 272
285 def RunStep(self): 273 def RunStep(self): # pragma: no cover
286 raise NotImplementedError 274 raise NotImplementedError
287 275
288 def Retry(self, cb, retry_on=None, wait_plan=None): 276 def Retry(self, cb, retry_on=None, wait_plan=None):
289 """ Retry a function. 277 """ Retry a function.
290 Params: 278 Params:
291 cb: The function to retry. 279 cb: The function to retry.
292 retry_on: A callback that takes the result of the function and returns 280 retry_on: A callback that takes the result of the function and returns
293 True if the function should be retried. A function throwing an 281 True if the function should be retried. A function throwing an
294 exception is always retried. 282 exception is always retried.
295 wait_plan: A list of waiting delays between retries in seconds. The 283 wait_plan: A list of waiting delays between retries in seconds. The
296 maximum number of retries is len(wait_plan). 284 maximum number of retries is len(wait_plan).
297 """ 285 """
298 retry_on = retry_on or (lambda x: False) 286 retry_on = retry_on or (lambda x: False)
299 wait_plan = list(wait_plan or []) 287 wait_plan = list(wait_plan or [])
300 wait_plan.reverse() 288 wait_plan.reverse()
301 while True: 289 while True:
302 got_exception = False 290 got_exception = False
303 try: 291 try:
304 result = cb() 292 result = cb()
305 except NoRetryException, e: 293 except NoRetryException, e:
306 raise e 294 raise e
307 except Exception: 295 except Exception:
308 got_exception = True 296 got_exception = True
309 if got_exception or retry_on(result): 297 if got_exception or retry_on(result):
310 if not wait_plan: 298 if not wait_plan: # pragma: no cover
311 raise Exception("Retried too often. Giving up.") 299 raise Exception("Retried too often. Giving up.")
312 wait_time = wait_plan.pop() 300 wait_time = wait_plan.pop()
313 print "Waiting for %f seconds." % wait_time 301 print "Waiting for %f seconds." % wait_time
314 self._side_effect_handler.Sleep(wait_time) 302 self._side_effect_handler.Sleep(wait_time)
315 print "Retrying..." 303 print "Retrying..."
316 else: 304 else:
317 return result 305 return result
318 306
319 def ReadLine(self, default=None): 307 def ReadLine(self, default=None):
320 # Don't prompt in forced mode. 308 # Don't prompt in forced mode.
(...skipping 27 matching lines...) Expand all
348 def GetDate(self): 336 def GetDate(self):
349 return self._side_effect_handler.GetDate() 337 return self._side_effect_handler.GetDate()
350 338
351 def Die(self, msg=""): 339 def Die(self, msg=""):
352 if msg != "": 340 if msg != "":
353 print "Error: %s" % msg 341 print "Error: %s" % msg
354 print "Exiting" 342 print "Exiting"
355 raise Exception(msg) 343 raise Exception(msg)
356 344
357 def DieNoManualMode(self, msg=""): 345 def DieNoManualMode(self, msg=""):
358 if not self._options.manual: 346 if not self._options.manual: # pragma: no cover
359 msg = msg or "Only available in manual mode." 347 msg = msg or "Only available in manual mode."
360 self.Die(msg) 348 self.Die(msg)
361 349
362 def Confirm(self, msg): 350 def Confirm(self, msg):
363 print "%s [Y/n] " % msg, 351 print "%s [Y/n] " % msg,
364 answer = self.ReadLine(default="Y") 352 answer = self.ReadLine(default="Y")
365 return answer == "" or answer == "Y" or answer == "y" 353 return answer == "" or answer == "Y" or answer == "y"
366 354
367 def DeleteBranch(self, name): 355 def DeleteBranch(self, name):
368 for line in self.GitBranch().splitlines(): 356 for line in self.GitBranch().splitlines():
369 if re.match(r".*\s+%s$" % name, line): 357 if re.match(r".*\s+%s$" % name, line):
370 msg = "Branch %s exists, do you want to delete it?" % name 358 msg = "Branch %s exists, do you want to delete it?" % name
371 if self.Confirm(msg): 359 if self.Confirm(msg):
372 self.GitDeleteBranch(name) 360 self.GitDeleteBranch(name)
373 print "Branch %s deleted." % name 361 print "Branch %s deleted." % name
374 else: 362 else:
375 msg = "Can't continue. Please delete branch %s and try again." % name 363 msg = "Can't continue. Please delete branch %s and try again." % name
376 self.Die(msg) 364 self.Die(msg)
377 365
378 def InitialEnvironmentChecks(self): 366 def InitialEnvironmentChecks(self):
379 # Cancel if this is not a git checkout. 367 # Cancel if this is not a git checkout.
380 if not os.path.exists(self._config[DOT_GIT_LOCATION]): 368 if not os.path.exists(self._config[DOT_GIT_LOCATION]): # pragma: no cover
381 self.Die("This is not a git checkout, this script won't work for you.") 369 self.Die("This is not a git checkout, this script won't work for you.")
382 370
383 # Cancel if EDITOR is unset or not executable. 371 # Cancel if EDITOR is unset or not executable.
384 if (self._options.requires_editor and (not os.environ.get("EDITOR") or 372 if (self._options.requires_editor and (not os.environ.get("EDITOR") or
385 Command("which", os.environ["EDITOR"]) is None)): 373 Command("which", os.environ["EDITOR"]) is None)): # pragma: no cover
386 self.Die("Please set your EDITOR environment variable, you'll need it.") 374 self.Die("Please set your EDITOR environment variable, you'll need it.")
387 375
388 def CommonPrepare(self): 376 def CommonPrepare(self):
389 # Check for a clean workdir. 377 # Check for a clean workdir.
390 if not self.GitIsWorkdirClean(): 378 if not self.GitIsWorkdirClean(): # pragma: no cover
391 self.Die("Workspace is not clean. Please commit or undo your changes.") 379 self.Die("Workspace is not clean. Please commit or undo your changes.")
392 380
393 # Persist current branch. 381 # Persist current branch.
394 self["current_branch"] = self.GitCurrentBranch() 382 self["current_branch"] = self.GitCurrentBranch()
395 383
396 # Fetch unfetched revisions. 384 # Fetch unfetched revisions.
397 self.GitSVNFetch() 385 self.GitSVNFetch()
398 386
399 def PrepareBranch(self): 387 def PrepareBranch(self):
400 # Get ahold of a safe temporary branch and check it out. 388 # Get ahold of a safe temporary branch and check it out.
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 try: 483 try:
496 requires = step_class.REQUIRES 484 requires = step_class.REQUIRES
497 except AttributeError: 485 except AttributeError:
498 requires = None 486 requires = None
499 487
500 return step_class(message, requires, number=number, config=config, 488 return step_class(message, requires, number=number, config=config,
501 state=state, options=options, 489 state=state, options=options,
502 handler=side_effect_handler) 490 handler=side_effect_handler)
503 491
504 492
505 def RunScript(step_classes, 493 class ScriptsBase(object):
506 config, 494 # TODO(machenbach): Move static config here.
507 options, 495 def __init__(self, config, side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER,
508 side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER): 496 state=None):
509 state_file = "%s-state.json" % config[PERSISTFILE_BASENAME] 497 self._config = config
510 if options.step == 0 and os.path.exists(state_file): 498 self._side_effect_handler = side_effect_handler
511 os.remove(state_file) 499 self._state = state if state is not None else {}
512 state = {}
513 steps = []
514 for (number, step_class) in enumerate(step_classes):
515 steps.append(MakeStep(step_class, number, state, config,
516 options, side_effect_handler))
517 500
518 for step in steps[options.step:]: 501 def _Description(self):
519 step.Run() 502 return None
503
504 def _PrepareOptions(self, parser):
505 pass
506
507 def _ProcessOptions(self, options):
508 return True
509
510 def _Steps(self): # pragma: no cover
511 raise Exception("Not implemented.")
512
513 def MakeOptions(self, args=None):
514 parser = argparse.ArgumentParser(description=self._Description())
515 parser.add_argument("-a", "--author", default="",
516 help="The author email used for rietveld.")
517 parser.add_argument("-r", "--reviewer", default="",
518 help="The account name to be used for reviews.")
519 parser.add_argument("-s", "--step",
520 help="Specify the step where to start work. Default: 0.",
521 default=0, type=int)
522
523 self._PrepareOptions(parser)
524
525 if args is None: # pragma: no cover
526 options = parser.parse_args()
527 else:
528 options = parser.parse_args(args)
529
530 # Process common options.
531 if options.step < 0: # pragma: no cover
532 print "Bad step number %d" % options.step
533 parser.print_help()
534 return None
535
536 # Defaults for options, common to all scripts.
537 options.manual = getattr(options, "manual", True)
538 options.force = getattr(options, "force", False)
539
540 # Derived options.
541 options.requires_editor = not options.force
542 options.wait_for_lgtm = not options.force
543 options.force_readline_defaults = not options.manual
544 options.force_upload = not options.manual
545
546 # Process script specific options.
547 if not self._ProcessOptions(options):
548 parser.print_help()
549 return None
550 return options
551
552 def RunSteps(self, step_classes, args=None):
553 options = self.MakeOptions(args)
554 if not options:
555 return 1
556
557 state_file = "%s-state.json" % self._config[PERSISTFILE_BASENAME]
558 if options.step == 0 and os.path.exists(state_file):
559 os.remove(state_file)
560
561 steps = []
562 for (number, step_class) in enumerate(step_classes):
563 steps.append(MakeStep(step_class, number, self._state, self._config,
564 options, self._side_effect_handler))
565 for step in steps[options.step:]:
566 step.Run()
567 return 0
568
569 def Run(self, args=None):
570 return self.RunSteps(self._Steps(), args)
OLDNEW
« no previous file with comments | « tools/push-to-trunk/auto_roll.py ('k') | tools/push-to-trunk/git_recipes.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698