[Tizen] Add prelauncher
[platform/framework/web/crosswalk-tizen.git] / vendor / depot_tools / git_retry.py
1 #!/usr/bin/env python
2 # Copyright 2014 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.
5
6 import logging
7 import optparse
8 import subprocess
9 import sys
10 import threading
11 import time
12
13 from git_common import GIT_EXE, GIT_TRANSIENT_ERRORS_RE
14
15
16 class TeeThread(threading.Thread):
17
18   def __init__(self, fd, out_fd, name):
19     super(TeeThread, self).__init__(name='git-retry.tee.%s' % (name,))
20     self.data = None
21     self.fd = fd
22     self.out_fd = out_fd
23
24   def run(self):
25     chunks = []
26     for line in self.fd:
27       chunks.append(line)
28       self.out_fd.write(line)
29     self.data = ''.join(chunks)
30
31
32 class GitRetry(object):
33
34   logger = logging.getLogger('git-retry')
35   DEFAULT_DELAY_SECS = 3.0
36   DEFAULT_RETRY_COUNT = 5
37
38   def __init__(self, retry_count=None, delay=None, delay_factor=None):
39     self.retry_count = retry_count or self.DEFAULT_RETRY_COUNT
40     self.delay = max(delay, 0) if delay else 0
41     self.delay_factor = max(delay_factor, 0) if delay_factor else 0
42
43   def shouldRetry(self, stderr):
44     m = GIT_TRANSIENT_ERRORS_RE.search(stderr)
45     if not m:
46       return False
47     self.logger.info("Encountered known transient error: [%s]",
48                      stderr[m.start(): m.end()])
49     return True
50
51   @staticmethod
52   def execute(*args):
53     args = (GIT_EXE,) + args
54     proc = subprocess.Popen(
55         args,
56         stderr=subprocess.PIPE,
57     )
58     stderr_tee = TeeThread(proc.stderr, sys.stderr, 'stderr')
59
60     # Start our process. Collect/tee 'stdout' and 'stderr'.
61     stderr_tee.start()
62     try:
63       proc.wait()
64     except KeyboardInterrupt:
65       proc.kill()
66       raise
67     finally:
68       stderr_tee.join()
69     return proc.returncode, None, stderr_tee.data
70
71   def computeDelay(self, iteration):
72     """Returns: the delay (in seconds) for a given iteration
73
74     The first iteration has a delay of '0'.
75
76     Args:
77       iteration: (int) The iteration index (starting with zero as the first
78           iteration)
79     """
80     if (not self.delay) or (iteration == 0):
81       return 0
82     if self.delay_factor == 0:
83       # Linear delay
84       return iteration * self.delay
85     # Exponential delay
86     return (self.delay_factor ** (iteration - 1)) * self.delay
87
88   def __call__(self, *args):
89     returncode = 0
90     for i in xrange(self.retry_count):
91       # If the previous run failed and a delay is configured, delay before the
92       # next run.
93       delay = self.computeDelay(i)
94       if delay > 0:
95         self.logger.info("Delaying for [%s second(s)] until next retry", delay)
96         time.sleep(delay)
97
98       self.logger.debug("Executing subprocess (%d/%d) with arguments: %s",
99                         (i+1), self.retry_count, args)
100       returncode, _, stderr = self.execute(*args)
101
102       self.logger.debug("Process terminated with return code: %d", returncode)
103       if returncode == 0:
104         break
105
106       if not self.shouldRetry(stderr):
107         self.logger.error("Process failure was not known to be transient; "
108                           "terminating with return code %d", returncode)
109         break
110     return returncode
111
112
113 def main(args):
114   parser = optparse.OptionParser()
115   parser.disable_interspersed_args()
116   parser.add_option('-v', '--verbose',
117                     action='count', default=0,
118                     help="Increase verbosity; can be specified multiple times")
119   parser.add_option('-c', '--retry-count', metavar='COUNT',
120                     type=int, default=GitRetry.DEFAULT_RETRY_COUNT,
121                     help="Number of times to retry (default=%default)")
122   parser.add_option('-d', '--delay', metavar='SECONDS',
123                     type=float, default=GitRetry.DEFAULT_DELAY_SECS,
124                     help="Specifies the amount of time (in seconds) to wait "
125                          "between successive retries (default=%default). This "
126                          "can be zero.")
127   parser.add_option('-D', '--delay-factor', metavar='FACTOR',
128                     type=int, default=2,
129                     help="The exponential factor to apply to delays in between "
130                          "successive failures (default=%default). If this is "
131                          "zero, delays will increase linearly. Set this to "
132                          "one to have a constant (non-increasing) delay.")
133
134   opts, args = parser.parse_args(args)
135
136   # Configure logging verbosity
137   if opts.verbose == 0:
138     logging.getLogger().setLevel(logging.WARNING)
139   elif opts.verbose == 1:
140     logging.getLogger().setLevel(logging.INFO)
141   else:
142     logging.getLogger().setLevel(logging.DEBUG)
143
144   # Execute retries
145   retry = GitRetry(
146       retry_count=opts.retry_count,
147       delay=opts.delay,
148       delay_factor=opts.delay_factor,
149   )
150   return retry(*args)
151
152
153 if __name__ == '__main__':
154   logging.basicConfig()
155   logging.getLogger().setLevel(logging.WARNING)
156   sys.exit(main(sys.argv[2:]))