OLD | NEW |
1 # Copyright 2009 Google Inc. All Rights Reserved. | 1 # Copyright 2009 Google Inc. All Rights Reserved. |
2 # | 2 # |
3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
6 # | 6 # |
7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
8 # | 8 # |
9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 def GetNodeNamedAttributeText(node, node_name, attribute_name): | 111 def GetNodeNamedAttributeText(node, node_name, attribute_name): |
112 child_nodes = node.getElementsByTagName(node_name) | 112 child_nodes = node.getElementsByTagName(node_name) |
113 if not child_nodes: | 113 if not child_nodes: |
114 return None | 114 return None |
115 assert len(child_nodes) == 1 | 115 assert len(child_nodes) == 1 |
116 return child_nodes[0].getAttribute(attribute_name) | 116 return child_nodes[0].getAttribute(attribute_name) |
117 | 117 |
118 | 118 |
119 class Error(Exception): | 119 class Error(Exception): |
120 """gclient exception class.""" | 120 """gclient exception class.""" |
| 121 # TODO(maruel): Merge with CheckCallError. |
121 pass | 122 pass |
122 | 123 |
123 | 124 |
124 def SyntaxErrorToError(filename, e): | 125 def SyntaxErrorToError(filename, e): |
125 """Raises a gclient_utils.Error exception with the human readable message""" | 126 """Raises a gclient_utils.Error exception with the human readable message""" |
126 try: | 127 try: |
127 # Try to construct a human readable error message | 128 # Try to construct a human readable error message |
128 if filename: | 129 if filename: |
129 error_message = 'There is a syntax error in %s\n' % filename | 130 error_message = 'There is a syntax error in %s\n' % filename |
130 else: | 131 else: |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 try: | 245 try: |
245 os.rmdir(file_path) | 246 os.rmdir(file_path) |
246 except OSError, e: | 247 except OSError, e: |
247 if e.errno != errno.EACCES or sys.platform != 'win32': | 248 if e.errno != errno.EACCES or sys.platform != 'win32': |
248 raise | 249 raise |
249 print 'Failed to remove %s: trying again' % file_path | 250 print 'Failed to remove %s: trying again' % file_path |
250 time.sleep(0.1) | 251 time.sleep(0.1) |
251 os.rmdir(file_path) | 252 os.rmdir(file_path) |
252 | 253 |
253 | 254 |
254 def SubprocessCall(args, **kwargs): | 255 def CheckCallAndFilterAndHeader(args, always=False, **kwargs): |
255 """Wraps SubprocessCallAndFilter() with different default arguments. | 256 """Adds 'header' support to CheckCallAndFilter. |
256 | 257 |
257 Calls subprocess and capture nothing.""" | 258 If |always| is True, a message indicating what is being done |
258 kwargs['print_messages'] = True | 259 is printed to stdout all the time even if not output is generated. Otherwise |
| 260 the message header is printed only if the call generated any ouput. |
| 261 """ |
| 262 stdout = kwargs.get('stdout', None) or sys.stdout |
| 263 if always: |
| 264 stdout.write('\n________ running \'%s\' in \'%s\'\n' |
| 265 % (' '.join(args), kwargs.get('cwd', '.'))) |
| 266 else: |
| 267 filter_fn = kwargs.get('filter_fn', None) |
| 268 def filter_msg(line): |
| 269 if line is None: |
| 270 stdout.write('\n________ running \'%s\' in \'%s\'\n' |
| 271 % (' '.join(args), kwargs.get('cwd', '.'))) |
| 272 elif filter_fn: |
| 273 filter_fn(line) |
| 274 kwargs['filter_fn'] = filter_msg |
| 275 kwargs['call_filter_on_first_line'] = True |
| 276 # Obviously. |
259 kwargs['print_stdout'] = True | 277 kwargs['print_stdout'] = True |
260 return SubprocessCallAndFilter(args, **kwargs) | 278 return CheckCallAndFilter(args, **kwargs) |
261 | 279 |
262 | 280 |
263 def SubprocessCallAndFilter(args, **kwargs): | 281 def CheckCallAndFilter(args, stdout=None, filter_fn=None, |
264 """Runs a command and prints a header line if appropriate. | 282 print_stdout=None, call_filter_on_first_line=False, |
| 283 **kwargs): |
| 284 """Runs a command and calls back a filter function if needed. |
265 | 285 |
266 If |print_messages| is True, a message indicating what is being done | 286 Accepts all subprocess.Popen() parameters plus: |
267 is printed to stdout. Otherwise the message is printed only if the call | 287 print_stdout: If True, the command's stdout is forwarded to stdout. |
268 generated any ouput. If both |print_messages| and |print_stdout| are False, | 288 filter_fn: A function taking a single string argument called with each line |
269 no output at all is generated. | 289 of the subprocess's output. Each line has the trailing newline |
| 290 character trimmed. |
| 291 stdout: Can be any bufferable output. |
270 | 292 |
271 If |print_stdout| is True, the command's stdout is also forwarded to stdout. | 293 stderr is always redirected to stdout. |
272 | |
273 If |filter_fn| function is specified, it is expected to take a single | |
274 string argument, and it will be called with each line of the | |
275 subprocess's output. Each line has had the trailing newline character | |
276 trimmed. | |
277 | |
278 If the command fails, as indicated by a nonzero exit status, gclient will | |
279 exit with an exit status of fail_status. If fail_status is None (the | |
280 default), gclient will raise an Error exception. | |
281 | |
282 Other subprocess.Popen parameters can be specified. | |
283 """ | 294 """ |
284 stdout = kwargs.pop('stdout', sys.stdout) or sys.stdout | 295 assert print_stdout or filter_fn |
| 296 stdout = stdout or sys.stdout |
| 297 filter_fn = filter_fn or (lambda x: None) |
285 assert not 'stderr' in kwargs | 298 assert not 'stderr' in kwargs |
286 filter_fn = kwargs.pop('filter_fn', None) | |
287 print_messages = kwargs.pop('print_messages', False) | |
288 print_stdout = kwargs.pop('print_stdout', False) | |
289 fail_status = kwargs.pop('fail_status', None) | |
290 | |
291 logging.debug(args) | 299 logging.debug(args) |
292 if print_messages: | |
293 stdout.write('\n________ running \'%s\' in \'%s\'\n' | |
294 % (' '.join(args), kwargs['cwd'])) | |
295 | |
296 kid = Popen(args, bufsize=0, | 300 kid = Popen(args, bufsize=0, |
297 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, | 301 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
298 **kwargs) | 302 **kwargs) |
299 | 303 |
300 # Do a flush of sys.stdout before we begin reading from the subprocess's | 304 # Do a flush of stdout before we begin reading from the subprocess's stdout |
301 # stdout. | |
302 last_flushed_at = time.time() | 305 last_flushed_at = time.time() |
303 stdout.flush() | 306 stdout.flush() |
304 | 307 |
305 # Also, we need to forward stdout to prevent weird re-ordering of output. | 308 # Also, we need to forward stdout to prevent weird re-ordering of output. |
306 # This has to be done on a per byte basis to make sure it is not buffered: | 309 # This has to be done on a per byte basis to make sure it is not buffered: |
307 # normally buffering is done for each line, but if svn requests input, no | 310 # normally buffering is done for each line, but if svn requests input, no |
308 # end-of-line character is output after the prompt and it would not show up. | 311 # end-of-line character is output after the prompt and it would not show up. |
309 in_byte = kid.stdout.read(1) | 312 in_byte = kid.stdout.read(1) |
310 in_line = '' | 313 if in_byte: |
311 while in_byte: | 314 if call_filter_on_first_line: |
312 if in_byte != '\r': | 315 filter_fn(None) |
313 if print_stdout: | 316 in_line = '' |
314 if not print_messages: | 317 while in_byte: |
315 stdout.write('\n________ running \'%s\' in \'%s\'\n' | 318 if in_byte != '\r': |
316 % (' '.join(args), kwargs['cwd'])) | 319 if print_stdout: |
317 print_messages = True | 320 stdout.write(in_byte) |
318 stdout.write(in_byte) | 321 if in_byte != '\n': |
319 if in_byte != '\n': | 322 in_line += in_byte |
320 in_line += in_byte | 323 else: |
321 if in_byte == '\n': | 324 filter_fn(in_line) |
322 if filter_fn: | 325 in_line = '' |
323 filter_fn(in_line) | 326 # Flush at least 10 seconds between line writes. We wait at least 10 |
324 in_line = '' | 327 # seconds to avoid overloading the reader that called us with output, |
325 # Flush at least 10 seconds between line writes. We wait at least 10 | 328 # which can slow busy readers down. |
326 # seconds to avoid overloading the reader that called us with output, | 329 if (time.time() - last_flushed_at) > 10: |
327 # which can slow busy readers down. | 330 last_flushed_at = time.time() |
328 if (time.time() - last_flushed_at) > 10: | 331 stdout.flush() |
329 last_flushed_at = time.time() | 332 in_byte = kid.stdout.read(1) |
330 stdout.flush() | 333 # Flush the rest of buffered output. This is only an issue with |
331 in_byte = kid.stdout.read(1) | 334 # stdout/stderr not ending with a \n. |
332 # Flush the rest of buffered output. This is only an issue with files not | 335 if len(in_line): |
333 # ending with a \n. | 336 filter_fn(in_line) |
334 if len(in_line) and filter_fn: | |
335 filter_fn(in_line) | |
336 rv = kid.wait() | 337 rv = kid.wait() |
337 | |
338 if rv: | 338 if rv: |
339 msg = 'failed to run command: %s' % ' '.join(args) | 339 raise Error('failed to run command: %s' % ' '.join(args)) |
340 if fail_status != None: | 340 return 0 |
341 sys.stderr.write(msg + '\n') | |
342 sys.exit(fail_status) | |
343 raise Error(msg) | |
344 | 341 |
345 | 342 |
346 def FindGclientRoot(from_dir, filename='.gclient'): | 343 def FindGclientRoot(from_dir, filename='.gclient'): |
347 """Tries to find the gclient root.""" | 344 """Tries to find the gclient root.""" |
348 path = os.path.realpath(from_dir) | 345 path = os.path.realpath(from_dir) |
349 while not os.path.exists(os.path.join(path, filename)): | 346 while not os.path.exists(os.path.join(path, filename)): |
350 split_path = os.path.split(path) | 347 split_path = os.path.split(path) |
351 if not split_path[1]: | 348 if not split_path[1]: |
352 return None | 349 return None |
353 path = split_path[0] | 350 path = split_path[0] |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 if exception: | 531 if exception: |
535 self.parent.exceptions.append(exception) | 532 self.parent.exceptions.append(exception) |
536 if self.parent.progress: | 533 if self.parent.progress: |
537 self.parent.progress.update(1) | 534 self.parent.progress.update(1) |
538 assert not self.item.name in self.parent.ran | 535 assert not self.item.name in self.parent.ran |
539 if not self.item.name in self.parent.ran: | 536 if not self.item.name in self.parent.ran: |
540 self.parent.ran.append(self.item.name) | 537 self.parent.ran.append(self.item.name) |
541 finally: | 538 finally: |
542 self.parent.ready_cond.notifyAll() | 539 self.parent.ready_cond.notifyAll() |
543 self.parent.ready_cond.release() | 540 self.parent.ready_cond.release() |
OLD | NEW |