OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 """Client-side script to send a try job to the try server. It communicates to | 5 """Client-side script to send a try job to the try server. It communicates to |
6 the try server by either writting to a svn repository or by directly connecting | 6 the try server by either writting to a svn repository or by directly connecting |
7 to the server by HTTP. | 7 to the server by HTTP. |
8 """ | 8 """ |
9 | 9 |
10 import datetime | 10 import datetime |
11 import getpass | 11 import getpass |
12 import logging | 12 import logging |
13 import optparse | 13 import optparse |
14 import os | 14 import os |
| 15 import posixpath |
15 import shutil | 16 import shutil |
16 import socket | 17 import socket |
17 import subprocess | 18 import subprocess |
18 import sys | 19 import sys |
19 import tempfile | 20 import tempfile |
20 import urllib | 21 import urllib |
21 | 22 |
22 try: | 23 try: |
23 import breakpad | 24 import breakpad |
24 except ImportError: | 25 except ImportError: |
25 pass | 26 pass |
26 | 27 |
27 import gclient_utils | 28 import gclient_utils |
28 import scm | 29 import scm |
29 | 30 |
30 __version__ = '1.2' | 31 __version__ = '1.2' |
31 | 32 |
32 | 33 |
33 # Constants | 34 # Constants |
34 HELP_STRING = "Sorry, Tryserver is not available." | 35 HELP_STRING = "Sorry, Tryserver is not available." |
35 USAGE = r"""%prog [change_name] [options] | 36 USAGE = r"""%prog [options] |
36 | 37 |
37 Client-side script to send a try job to the try server. It communicates to | 38 Client-side script to send a try job to the try server. It communicates to |
38 the try server by either writting to a svn repository or by directly connecting | 39 the try server by either writting to a svn repository or by directly connecting |
39 to the server by HTTP. | 40 to the server by HTTP. |
40 | 41 |
41 | |
42 Examples: | 42 Examples: |
43 Try a change against a particular revision: | 43 Try a change against a particular revision: |
44 %prog change_name -r 123 | 44 %prog change_name -r 123 |
45 | 45 |
46 A git patch off a web site (git inserts a/ and b/) and fix the base dir: | 46 A git patch off a web site (git inserts a/ and b/) and fix the base dir: |
47 %prog --url http://url/to/patch.diff --patchlevel 1 --root src | 47 %prog --url http://url/to/patch.diff --patchlevel 1 --root src |
48 | 48 |
49 Use svn to store the try job, specify an alternate email address and use a | 49 Use svn to store the try job, specify an alternate email address and use a |
50 premade diff file on the local drive: | 50 premade diff file on the local drive: |
51 %prog --email user@example.com | 51 %prog --email user@example.com |
52 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff | 52 --svn_repo svn://svn.chromium.org/chrome-try/try --diff foo.diff |
53 | 53 |
54 Running only on a 'mac' slave with revision 123 and clobber first; specify | 54 Running only on a 'mac' slave with revision 123 and clobber first; specify |
55 manually the 3 source files to use for the try job: | 55 manually the 3 source files to use for the try job: |
56 %prog --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h | 56 %prog --bot mac --revision 123 --clobber -f src/a.cc -f src/a.h |
57 -f include/b.h""" | 57 -f include/b.h |
| 58 |
| 59 When called from gcl, use the format gcl try <change_name>. |
| 60 """ |
58 | 61 |
59 class InvalidScript(Exception): | 62 class InvalidScript(Exception): |
60 def __str__(self): | 63 def __str__(self): |
61 return self.args[0] + '\n' + HELP_STRING | 64 return self.args[0] + '\n' + HELP_STRING |
62 | 65 |
63 | 66 |
64 class NoTryServerAccess(Exception): | 67 class NoTryServerAccess(Exception): |
65 def __str__(self): | 68 def __str__(self): |
66 return self.args[0] + '\n' + HELP_STRING | 69 return self.args[0] + '\n' + HELP_STRING |
67 | 70 |
68 | 71 |
69 def EscapeDot(name): | 72 def EscapeDot(name): |
70 return name.replace('.', '-') | 73 return name.replace('.', '-') |
71 | 74 |
72 | 75 |
73 class SCM(object): | 76 class SCM(object): |
74 """Simplistic base class to implement one function: ProcessOptions.""" | 77 """Simplistic base class to implement one function: ProcessOptions.""" |
75 def __init__(self, options, cwd): | 78 def __init__(self, options, cwd): |
76 self.checkout_root = cwd | 79 self.checkout_root = cwd |
77 self.options = options | 80 self.options = options |
78 self.files = self.options.files | 81 self.files = self.options.files |
79 self.options.files = None | 82 self.options.files = None |
| 83 self.codereview_settings = None |
| 84 self.codereview_settings_file = 'codereview.settings' |
80 | 85 |
81 def GetFileNames(self): | 86 def GetFileNames(self): |
82 """Return the list of files in the diff.""" | 87 """Return the list of files in the diff.""" |
83 return self.files | 88 return self.files |
84 | 89 |
| 90 def GetCodeReviewSetting(self, key): |
| 91 """Returns a value for the given key for this repository. |
| 92 |
| 93 Uses gcl-style settings from the repository.""" |
| 94 if self.codereview_settings is None: |
| 95 self.codereview_settings = {} |
| 96 settings_file = self.ReadRootFile(self.codereview_settings_file) |
| 97 if settings_file: |
| 98 for line in settings_file.splitlines(): |
| 99 if not line or line.lstrip().startswith('#'): |
| 100 continue |
| 101 k, v = line.split(":", 1) |
| 102 self.codereview_settings[k.strip()] = v.strip() |
| 103 return self.codereview_settings.get(key, '') |
| 104 |
| 105 def GclStyleSettings(self): |
| 106 """Set default settings based on the gcl-style settings from the |
| 107 repository.""" |
| 108 settings = { |
| 109 'port': self.GetCodeReviewSetting('TRYSERVER_HTTP_PORT'), |
| 110 'host': self.GetCodeReviewSetting('TRYSERVER_HTTP_HOST'), |
| 111 'svn_repo': self.GetCodeReviewSetting('TRYSERVER_SVN_URL'), |
| 112 'project': self.GetCodeReviewSetting('TRYSERVER_PROJECT'), |
| 113 'root': self.GetCodeReviewSetting('TRYSERVER_ROOT'), |
| 114 'patchlevel': self.GetCodeReviewSetting('TRYSERVER_PATCHLEVEL'), |
| 115 } |
| 116 for (k, v) in settings.iteritems(): |
| 117 if v and getattr(self.options, k) is None: |
| 118 setattr(self.options, k, v) |
| 119 |
| 120 def GclientStyleSettings(self): |
| 121 """Find the root, assuming a gclient-style checkout.""" |
| 122 if not self.options.no_gclient and not self.options.root: |
| 123 root = self.GetLocalRoot() |
| 124 gclient_root = gclient_utils.FindGclientRoot(root) |
| 125 if gclient_root: |
| 126 self.options.root = gclient_utils.PathDifference(gclient_root, root) |
| 127 |
| 128 def AutomagicalSettings(self): |
| 129 """Determines settings based on supported code review and checkout tools. |
| 130 """ |
| 131 self.GclStyleSettings() |
| 132 self.GclientStyleSettings() |
| 133 |
85 | 134 |
86 class SVN(SCM): | 135 class SVN(SCM): |
87 """Gathers the options and diff for a subversion checkout.""" | 136 """Gathers the options and diff for a subversion checkout.""" |
88 def __init__(self, *args, **kwargs): | 137 def __init__(self, *args, **kwargs): |
89 SCM.__init__(self, *args, **kwargs) | 138 SCM.__init__(self, *args, **kwargs) |
90 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) | 139 self.checkout_root = scm.SVN.GetCheckoutRoot(self.checkout_root) |
91 if not self.options.email: | 140 if not self.options.email: |
92 # Assumes the svn credential is an email address. | 141 # Assumes the svn credential is an email address. |
93 self.options.email = scm.SVN.GetEmail(self.checkout_root) | 142 self.options.email = scm.SVN.GetEmail(self.checkout_root) |
94 | 143 |
| 144 def ReadRootFile(self, filename): |
| 145 try: |
| 146 # Try to search on the subversion repository for the file. |
| 147 import gcl |
| 148 data = gcl.GetCachedFile(filename, use_root=True) |
| 149 logging.debug('%s:\n%s' % (filename, data)) |
| 150 return data |
| 151 except ImportError: |
| 152 try: |
| 153 data = gclient_utils.FileRead(os.path.join(self.checkout_root, |
| 154 filename)) |
| 155 logging.debug('%s:\n%s' % (filename, data)) |
| 156 return data |
| 157 except (IOError, OSError): |
| 158 logging.debug('%s:\nNone' % filename) |
| 159 return None |
| 160 |
95 def GenerateDiff(self): | 161 def GenerateDiff(self): |
96 """Returns a string containing the diff for the given file list. | 162 """Returns a string containing the diff for the given file list. |
97 | 163 |
98 The files in the list should either be absolute paths or relative to the | 164 The files in the list should either be absolute paths or relative to the |
99 given root. | 165 given root. |
100 """ | 166 """ |
101 if not self.files: | 167 if not self.files: |
102 previous_cwd = os.getcwd() | 168 previous_cwd = os.getcwd() |
103 os.chdir(self.checkout_root) | 169 os.chdir(self.checkout_root) |
104 excluded = ['!', '?', 'X', ' ', '~'] | 170 excluded = ['!', '?', 'X', ' ', '~'] |
105 self.files = [ | 171 self.files = [ |
106 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) | 172 f[1] for f in scm.SVN.CaptureStatus(self.checkout_root) |
107 if f[0][0] not in excluded | 173 if f[0][0] not in excluded |
108 ] | 174 ] |
109 os.chdir(previous_cwd) | 175 os.chdir(previous_cwd) |
110 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True) | 176 return scm.SVN.GenerateDiff(self.files, self.checkout_root, full_move=True) |
111 | 177 |
112 def GetLocalRoot(self): | 178 def GetLocalRoot(self): |
113 """Return the path of the repository root.""" | 179 """Return the path of the repository root.""" |
114 return self.checkout_root | 180 return self.checkout_root |
115 | 181 |
116 def GetBots(self): | |
117 try: | |
118 # Try to search on the subversion repository for the file. | |
119 import gcl | |
120 return gcl.GetCachedFile('PRESUBMIT.py', use_root=True) | |
121 except ImportError: | |
122 try: | |
123 return gclient_utils.FileRead(os.path.join(self.checkout_root, | |
124 'PRESUBMIT.py')) | |
125 except (IOError, OSError): | |
126 return None | |
127 | |
128 | 182 |
129 class GIT(SCM): | 183 class GIT(SCM): |
130 """Gathers the options and diff for a git checkout.""" | 184 """Gathers the options and diff for a git checkout.""" |
131 def __init__(self, *args, **kwargs): | 185 def __init__(self, *args, **kwargs): |
132 SCM.__init__(self, *args, **kwargs) | 186 SCM.__init__(self, *args, **kwargs) |
133 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) | 187 self.checkout_root = scm.GIT.GetCheckoutRoot(self.checkout_root) |
134 if not self.options.name: | 188 if not self.options.name: |
135 self.options.name = scm.GIT.GetPatchName(self.checkout_root) | 189 self.options.name = scm.GIT.GetPatchName(self.checkout_root) |
136 if not self.options.email: | 190 if not self.options.email: |
137 self.options.email = scm.GIT.GetEmail(self.checkout_root) | 191 self.options.email = scm.GIT.GetEmail(self.checkout_root) |
138 | 192 |
| 193 def ReadRootFile(self, filename): |
| 194 try: |
| 195 # A git checkout is always a full checkout. |
| 196 data = gclient_utils.FileRead(os.path.join(self.checkout_root, filename)) |
| 197 logging.debug('%s:\n%s' % (filename, data)) |
| 198 return data |
| 199 except (IOError, OSError): |
| 200 logging.debug('%s:\nNone' % filename) |
| 201 return None |
| 202 |
139 def GetLocalRoot(self): | 203 def GetLocalRoot(self): |
140 """Return the path of the repository root.""" | 204 """Return the path of the repository root.""" |
141 return self.checkout_root | 205 return self.checkout_root |
142 | 206 |
143 def GenerateDiff(self): | 207 def GenerateDiff(self): |
144 # For now, ignores self.files | 208 # For now, ignores self.files |
145 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True) | 209 return scm.GIT.GenerateDiff(self.checkout_root, full_move=True) |
146 | 210 |
147 def GetBots(self): | |
148 try: | |
149 # A git checkout is always a full checkout. | |
150 return gclient_utils.FileRead(os.path.join(self.checkout_root, | |
151 'PRESUBMIT.py')) | |
152 except (IOError, OSError): | |
153 return None | |
154 | |
155 | 211 |
156 def _ParseSendChangeOptions(options): | 212 def _ParseSendChangeOptions(options): |
157 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" | 213 """Parse common options passed to _SendChangeHTTP and _SendChangeSVN.""" |
158 values = {} | 214 values = {} |
159 if options.email: | 215 if options.email: |
160 values['email'] = options.email | 216 values['email'] = options.email |
161 values['user'] = options.user | 217 values['user'] = options.user |
162 values['name'] = options.name | 218 values['name'] = options.name |
163 if options.bot: | 219 if options.bot: |
164 values['bot'] = ','.join(options.bot) | 220 values['bot'] = ','.join(options.bot) |
(...skipping 21 matching lines...) Expand all Loading... |
186 def _SendChangeHTTP(options): | 242 def _SendChangeHTTP(options): |
187 """Send a change to the try server using the HTTP protocol.""" | 243 """Send a change to the try server using the HTTP protocol.""" |
188 if not options.host: | 244 if not options.host: |
189 raise NoTryServerAccess('Please use the --host option to specify the try ' | 245 raise NoTryServerAccess('Please use the --host option to specify the try ' |
190 'server host to connect to.') | 246 'server host to connect to.') |
191 if not options.port: | 247 if not options.port: |
192 raise NoTryServerAccess('Please use the --port option to specify the try ' | 248 raise NoTryServerAccess('Please use the --port option to specify the try ' |
193 'server port to connect to.') | 249 'server port to connect to.') |
194 | 250 |
195 values = _ParseSendChangeOptions(options) | 251 values = _ParseSendChangeOptions(options) |
| 252 description = ''.join("%s=%s\n" % (k,v) for (k,v) in values.iteritems()) |
196 values['patch'] = options.diff | 253 values['patch'] = options.diff |
197 | 254 |
198 url = 'http://%s:%s/send_try_patch' % (options.host, options.port) | 255 url = 'http://%s:%s/send_try_patch' % (options.host, options.port) |
199 proxies = None | 256 proxies = None |
200 if options.proxy: | 257 if options.proxy: |
201 if options.proxy.lower() == 'none': | 258 if options.proxy.lower() == 'none': |
202 # Effectively disable HTTP_PROXY or Internet settings proxy setup. | 259 # Effectively disable HTTP_PROXY or Internet settings proxy setup. |
203 proxies = {} | 260 proxies = {} |
204 else: | 261 else: |
205 proxies = {'http': options.proxy, 'https': options.proxy} | 262 proxies = {'http': options.proxy, 'https': options.proxy} |
206 | 263 |
| 264 logging.info(description) |
| 265 logging.info(url) |
| 266 logging.info(options.diff) |
207 if options.dry_run: | 267 if options.dry_run: |
208 # Last minute fake. | |
209 for (k,v) in values.iteritems(): | |
210 if k != 'patch': | |
211 print("%s=%s" % (k,v)) | |
212 print values['patch'] | |
213 return | 268 return |
214 | 269 |
215 try: | 270 try: |
216 connection = urllib.urlopen(url, urllib.urlencode(values), proxies=proxies) | 271 connection = urllib.urlopen(url, urllib.urlencode(values), proxies=proxies) |
217 except IOError, e: | 272 except IOError, e: |
| 273 logging.warning(str(e)) |
218 if (values.get('bot') and len(e.args) > 2 and | 274 if (values.get('bot') and len(e.args) > 2 and |
219 e.args[2] == 'got a bad status line'): | 275 e.args[2] == 'got a bad status line'): |
220 raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url) | 276 raise NoTryServerAccess('%s is unaccessible. Bad --bot argument?' % url) |
221 else: | 277 else: |
222 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url, | 278 raise NoTryServerAccess('%s is unaccessible. Reason: %s' % (url, |
223 str(e.args))) | 279 str(e.args))) |
224 if not connection: | 280 if not connection: |
225 raise NoTryServerAccess('%s is unaccessible.' % url) | 281 raise NoTryServerAccess('%s is unaccessible.' % url) |
226 response = connection.read() | 282 response = connection.read() |
227 if response != 'OK': | 283 if response != 'OK': |
228 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response)) | 284 raise NoTryServerAccess('%s is unaccessible. Got:\n%s' % (url, response)) |
229 | 285 |
230 | 286 |
231 def _SendChangeSVN(options): | 287 def _SendChangeSVN(options): |
232 """Send a change to the try server by committing a diff file on a subversion | 288 """Send a change to the try server by committing a diff file on a subversion |
233 server.""" | 289 server.""" |
234 if not options.svn_repo: | 290 if not options.svn_repo: |
235 raise NoTryServerAccess('Please use the --svn_repo option to specify the' | 291 raise NoTryServerAccess('Please use the --svn_repo option to specify the' |
236 ' try server svn repository to connect to.') | 292 ' try server svn repository to connect to.') |
237 | 293 |
238 values = _ParseSendChangeOptions(options) | 294 values = _ParseSendChangeOptions(options) |
239 description = '' | 295 description = ''.join("%s=%s\n" % (k,v) for (k,v) in values.iteritems()) |
240 for (k,v) in values.iteritems(): | 296 logging.info(description) |
241 description += "%s=%s\n" % (k,v) | 297 logging.info(options.svn_repo) |
242 | 298 logging.info(options.diff) |
243 if options.dry_run: | 299 if options.dry_run: |
244 # Last minute fake. | |
245 print str(descriptions) | |
246 print diff | |
247 return | 300 return |
248 | 301 |
249 # Do an empty checkout. | 302 # Do an empty checkout. |
250 temp_dir = tempfile.mkdtemp() | 303 temp_dir = tempfile.mkdtemp() |
251 temp_file = tempfile.NamedTemporaryFile() | 304 temp_file = tempfile.NamedTemporaryFile() |
252 try: | 305 try: |
253 try: | 306 try: |
254 command = ['svn', 'checkout', '--depth', 'empty', '-q', | 307 command = ['svn', 'checkout', '--depth', 'empty', '-q', |
255 options.svn_repo, temp_dir] | 308 options.svn_repo, temp_dir] |
256 if options.email: | 309 if options.email: |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 This examines the current directory, guesses which SCM we're using, and | 354 This examines the current directory, guesses which SCM we're using, and |
302 returns an instance of the appropriate class. Exit with an error if we can't | 355 returns an instance of the appropriate class. Exit with an error if we can't |
303 figure it out. | 356 figure it out. |
304 | 357 |
305 Returns: | 358 Returns: |
306 A SCM instance. Exits if the SCM can't be guessed. | 359 A SCM instance. Exits if the SCM can't be guessed. |
307 """ | 360 """ |
308 __pychecker__ = 'no-returnvalues' | 361 __pychecker__ = 'no-returnvalues' |
309 # Subversion has a .svn in all working directories. | 362 # Subversion has a .svn in all working directories. |
310 if os.path.isdir(os.path.join(cwd, '.svn')): | 363 if os.path.isdir(os.path.join(cwd, '.svn')): |
311 logging.info("Guessed VCS = Subversion") | 364 logging.info("GuessVCS(%s) = Subversion" % cwd) |
312 return SVN(options, cwd) | 365 return SVN(options, cwd) |
313 | 366 |
314 # Git has a command to test if you're in a git tree. | 367 # Git has a command to test if you're in a git tree. |
315 # Try running it, but don't die if we don't have git installed. | 368 # Try running it, but don't die if we don't have git installed. |
316 try: | 369 try: |
317 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd) | 370 gclient_utils.CheckCall(["git", "rev-parse", "--is-inside-work-tree"], cwd) |
318 logging.info("Guessed VCS = Git") | 371 logging.info("GuessVCS(%s) = Git" % cwd) |
319 return GIT(options, cwd) | 372 return GIT(options, cwd) |
320 except gclient_utils.CheckCallError, e: | 373 except gclient_utils.CheckCallError, e: |
321 if e.retcode != 2: # ENOENT -- they don't have git installed. | 374 if e.retcode != 2: # ENOENT -- they don't have git installed. |
322 raise | 375 raise |
323 raise NoTryServerAccess("Could not guess version control system. " | 376 raise NoTryServerAccess("Could not guess version control system. " |
324 "Are you in a working copy directory?") | 377 "Are you in a working copy directory?") |
325 | 378 |
326 | 379 |
327 def TryChange(argv, | 380 def TryChange(argv, |
328 file_list, | 381 file_list, |
329 swallow_exception, | 382 swallow_exception, |
330 prog=None): | 383 prog=None): |
331 """ | 384 """ |
332 Args: | 385 Args: |
333 argv: Arguments and options. | 386 argv: Arguments and options. |
334 file_list: Default value to pass to --file. | 387 file_list: Default value to pass to --file. |
335 swallow_exception: Whether we raise or swallow exceptions. | 388 swallow_exception: Whether we raise or swallow exceptions. |
336 """ | 389 """ |
337 # Parse argv | 390 # Parse argv |
338 parser = optparse.OptionParser(usage=USAGE, | 391 parser = optparse.OptionParser(usage=USAGE, |
339 version=__version__, | 392 version=__version__, |
340 prog=prog) | 393 prog=prog) |
341 | 394 parser.add_option("-v", "--verbose", action="count", default=0, |
| 395 help="Prints debugging infos") |
342 group = optparse.OptionGroup(parser, "Result and status") | 396 group = optparse.OptionGroup(parser, "Result and status") |
343 group.add_option("-u", "--user", default=getpass.getuser(), | 397 group.add_option("-u", "--user", default=getpass.getuser(), |
344 help="Owner user name [default: %default]") | 398 help="Owner user name [default: %default]") |
345 group.add_option("-e", "--email", | 399 group.add_option("-e", "--email", |
346 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS', | 400 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS', |
347 os.environ.get('EMAIL_ADDRESS')), | 401 os.environ.get('EMAIL_ADDRESS')), |
348 help="Email address where to send the results. Use either " | 402 help="Email address where to send the results. Use either " |
349 "the TRYBOT_RESULTS_EMAIL_ADDRESS environment " | 403 "the TRYBOT_RESULTS_EMAIL_ADDRESS environment " |
350 "variable or EMAIL_ADDRESS to set the email address " | 404 "variable or EMAIL_ADDRESS to set the email address " |
351 "the try bots report results to [default: %default]") | 405 "the try bots report results to [default: %default]") |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 "try, relative to the repository root") | 446 "try, relative to the repository root") |
393 group.add_option("--diff", | 447 group.add_option("--diff", |
394 help="File containing the diff to try") | 448 help="File containing the diff to try") |
395 group.add_option("--url", | 449 group.add_option("--url", |
396 help="Url where to grab a patch") | 450 help="Url where to grab a patch") |
397 group.add_option("--root", | 451 group.add_option("--root", |
398 help="Root to use for the patch; base subdirectory for " | 452 help="Root to use for the patch; base subdirectory for " |
399 "patch created in a subdirectory") | 453 "patch created in a subdirectory") |
400 group.add_option("--patchlevel", type='int', metavar="LEVEL", | 454 group.add_option("--patchlevel", type='int', metavar="LEVEL", |
401 help="Used as -pN parameter to patch") | 455 help="Used as -pN parameter to patch") |
402 group.add_option("--sub_rep", action="append", default=["."], | 456 group.add_option("--sub_rep", action="append", default=[], |
403 help="Subcheckout to use in addition. This is mainly " | 457 help="Subcheckout to use in addition. This is mainly " |
404 "useful for gclient-style checkouts.") | 458 "useful for gclient-style checkouts.") |
| 459 group.add_option("--no_gclient", action="store_true", |
| 460 help="Disable automatic search for gclient checkout.") |
405 parser.add_option_group(group) | 461 parser.add_option_group(group) |
406 | 462 |
407 group = optparse.OptionGroup(parser, "Access the try server by HTTP") | 463 group = optparse.OptionGroup(parser, "Access the try server by HTTP") |
408 group.add_option("--use_http", | 464 group.add_option("--use_http", |
409 action="store_const", | 465 action="store_const", |
410 const=_SendChangeHTTP, | 466 const=_SendChangeHTTP, |
411 dest="send_patch", | 467 dest="send_patch", |
412 help="Use HTTP to talk to the try server [default]") | 468 help="Use HTTP to talk to the try server [default]") |
413 group.add_option("--host", | 469 group.add_option("--host", |
414 help="Host address") | 470 help="Host address") |
(...skipping 12 matching lines...) Expand all Loading... |
427 group.add_option("--svn_repo", | 483 group.add_option("--svn_repo", |
428 metavar="SVN_URL", | 484 metavar="SVN_URL", |
429 help="SVN url to use to write the changes in; --use_svn is " | 485 help="SVN url to use to write the changes in; --use_svn is " |
430 "implied when using --svn_repo") | 486 "implied when using --svn_repo") |
431 parser.add_option_group(group) | 487 parser.add_option_group(group) |
432 | 488 |
433 options, args = parser.parse_args(argv) | 489 options, args = parser.parse_args(argv) |
434 if len(args) == 1 and args[0] == 'help': | 490 if len(args) == 1 and args[0] == 'help': |
435 parser.print_help() | 491 parser.print_help() |
436 | 492 |
437 # Switch the default accordingly if there was no default send_patch. | 493 if options.verbose == 0: |
438 if not options.send_patch: | 494 logging.basicConfig(level=logging.ERROR) |
439 if options.port and options.host: | 495 elif options.verbose == 1: |
440 options.send_patch = _SendChangeHTTP | 496 logging.basicConfig(level=logging.WARNING) |
441 elif options.svn_repo: | 497 elif options.verbose == 2: |
442 options.send_patch = _SendChangeSVN | 498 logging.basicConfig(level=logging.INFO) |
443 else: | 499 elif options.verbose > 2: |
444 parser.error('Please specify an access method.') | 500 logging.basicConfig(level=logging.DEBUG) |
445 | 501 |
446 try: | 502 try: |
447 # Process the VCS in any case at least to retrieve the email address. | 503 # Always include os.getcwd() in the checkout settings. |
448 checkouts = [] | 504 checkouts = [] |
| 505 checkouts.append(GuessVCS(options, os.getcwd())) |
| 506 checkouts[0].AutomagicalSettings() |
449 for item in options.sub_rep: | 507 for item in options.sub_rep: |
450 checkout = GuessVCS(options, item) | 508 checkout = GuessVCS(options, item) |
451 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]: | 509 if checkout.GetLocalRoot() in [c.GetLocalRoot() for c in checkouts]: |
452 parser.error('Specified the root %s two times.' % | 510 parser.error('Specified the root %s two times.' % |
453 checkout.GetLocalRoot()) | 511 checkout.GetLocalRoot()) |
454 checkouts.append(checkout) | 512 checkouts.append(checkout) |
455 | 513 |
| 514 # If there was no transport selected yet, now we must have enough data to |
| 515 # select one. |
| 516 if not options.send_patch: |
| 517 if options.port and options.host: |
| 518 options.send_patch = _SendChangeHTTP |
| 519 elif options.svn_repo: |
| 520 options.send_patch = _SendChangeSVN |
| 521 else: |
| 522 parser.error('Please specify an access method.') |
| 523 |
456 # Convert options.diff into the content of the diff. | 524 # Convert options.diff into the content of the diff. |
457 if options.url: | 525 if options.url: |
458 if options.files: | 526 if options.files: |
459 parser.error('You cannot specify files and --url at the same time.') | 527 parser.error('You cannot specify files and --url at the same time.') |
460 options.diff = urllib.urlopen(options.url).read() | 528 options.diff = urllib.urlopen(options.url).read() |
461 elif options.diff: | 529 elif options.diff: |
462 if options.files: | 530 if options.files: |
463 parser.error('You cannot specify files and --diff at the same time.') | 531 parser.error('You cannot specify files and --diff at the same time.') |
464 options.diff = gclient_utils.FileRead(options.diff, 'rb') | 532 options.diff = gclient_utils.FileRead(options.diff, 'rb') |
465 else: | 533 else: |
466 # Use this as the base. | 534 # Use this as the base. |
467 root = checkouts[0].GetLocalRoot() | 535 root = checkouts[0].GetLocalRoot() |
468 diffs = [] | 536 diffs = [] |
469 for checkout in checkouts: | 537 for checkout in checkouts: |
470 diff = checkout.GenerateDiff().splitlines(True) | 538 diff = checkout.GenerateDiff().splitlines(True) |
471 # Munge it. | 539 # Munge it. |
472 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot()) | 540 path_diff = gclient_utils.PathDifference(root, checkout.GetLocalRoot()) |
473 for i in range(len(diff)): | 541 for i in range(len(diff)): |
474 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): | 542 if diff[i].startswith('--- ') or diff[i].startswith('+++ '): |
475 diff[i] = diff[i][0:3] + path_diff + diff[i][4:] | 543 diff[i] = diff[i][0:4] + posixpath.join(path_diff, diff[i][4:]) |
476 diffs.extend(diff) | 544 diffs.extend(diff) |
477 options.diff = ''.join(diffs) | 545 options.diff = ''.join(diffs) |
478 | 546 |
479 if not options.bot: | 547 if not options.bot: |
480 # Get try slaves from PRESUBMIT.py files if not specified. | 548 # Get try slaves from PRESUBMIT.py files if not specified. |
481 # Even if the diff comes from options.url, use the local checkout for bot | 549 # Even if the diff comes from options.url, use the local checkout for bot |
482 # selection. | 550 # selection. |
483 try: | 551 try: |
484 import presubmit_support | 552 import presubmit_support |
485 root_presubmit = checkouts[0].GetBots() | 553 root_presubmit = checkouts[0].ReadRootFile('PRESUBMIT.py') |
486 options.bot = presubmit_support.DoGetTrySlaves( | 554 options.bot = presubmit_support.DoGetTrySlaves( |
487 checkouts[0].GetFileNames(), | 555 checkouts[0].GetFileNames(), |
488 checkouts[0].GetLocalRoot(), | 556 checkouts[0].GetLocalRoot(), |
489 root_presubmit, | 557 root_presubmit, |
490 False, | 558 False, |
491 sys.stdout) | 559 sys.stdout) |
492 except ImportError: | 560 except ImportError: |
493 pass | 561 pass |
494 # If no bot is specified, either the default pool will be selected or the | 562 # If no bot is specified, either the default pool will be selected or the |
495 # try server will refuse the job. Either case we don't need to interfere. | 563 # try server will refuse the job. Either case we don't need to interfere. |
(...skipping 20 matching lines...) Expand all Loading... |
516 except (InvalidScript, NoTryServerAccess), e: | 584 except (InvalidScript, NoTryServerAccess), e: |
517 if swallow_exception: | 585 if swallow_exception: |
518 return 1 | 586 return 1 |
519 print e | 587 print e |
520 return 1 | 588 return 1 |
521 return 0 | 589 return 0 |
522 | 590 |
523 | 591 |
524 if __name__ == "__main__": | 592 if __name__ == "__main__": |
525 sys.exit(TryChange(None, [], False)) | 593 sys.exit(TryChange(None, [], False)) |
OLD | NEW |