Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Common python commands used by various build scripts.""" | 5 """Common python commands used by various build scripts.""" |
| 6 | 6 |
| 7 import inspect | 7 import inspect |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import shlex | |
|
davidjames
2011/04/14 21:59:05
This is unused now, right?
Peter Mayo
2011/04/19 03:59:39
Done.
| |
| 10 import signal | 11 import signal |
| 11 import subprocess | 12 import subprocess |
| 12 import sys | 13 import sys |
| 13 from terminal import Color | 14 from terminal import Color |
| 14 | 15 |
| 15 | 16 |
| 16 _STDOUT_IS_TTY = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() | 17 _STDOUT_IS_TTY = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() |
| 17 | 18 |
| 18 | 19 |
| 19 class CommandResult(object): | 20 class CommandResult(object): |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 36 return (type(self) == type(other) and | 37 return (type(self) == type(other) and |
| 37 str(self) == str(other) and | 38 str(self) == str(other) and |
| 38 self.cmd == other.cmd) | 39 self.cmd == other.cmd) |
| 39 | 40 |
| 40 def __ne__(self, other): | 41 def __ne__(self, other): |
| 41 return not self.__eq__(other) | 42 return not self.__eq__(other) |
| 42 | 43 |
| 43 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, | 44 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, |
| 44 exit_code=False, redirect_stdout=False, redirect_stderr=False, | 45 exit_code=False, redirect_stdout=False, redirect_stderr=False, |
| 45 cwd=None, input=None, enter_chroot=False, shell=False, | 46 cwd=None, input=None, enter_chroot=False, shell=False, |
| 46 env=None, ignore_sigint=False, combine_stdout_stderr=False): | 47 env=None, extra_env=None, ignore_sigint=False, |
| 48 combine_stdout_stderr=False): | |
| 47 """Runs a command. | 49 """Runs a command. |
| 48 | 50 |
| 49 Args: | 51 Args: |
| 50 cmd: cmd to run. Should be input to subprocess.Popen. | 52 cmd: cmd to run. Should be input to subprocess.Popen. If a string, it will |
| 53 be converted to an array by shlex.split() | |
|
sosa
2011/04/14 21:22:18
Indentation is inconsistent here.
davidjames
2011/04/14 21:59:05
Document that if shell is False, cmd must be an ar
Peter Mayo
2011/04/19 03:59:39
Done.
| |
| 51 print_cmd: prints the command before running it. | 54 print_cmd: prints the command before running it. |
| 52 error_ok: does not raise an exception on error. | 55 error_ok: does not raise an exception on error. |
| 53 error_message: prints out this message when an error occurrs. | 56 error_message: prints out this message when an error occurrs. |
| 54 exit_code: returns the return code of the shell command. | 57 exit_code: returns the return code of the shell command. |
| 55 redirect_stdout: returns the stdout. | 58 redirect_stdout: returns the stdout. |
| 56 redirect_stderr: holds stderr output until input is communicated. | 59 redirect_stderr: holds stderr output until input is communicated. |
| 57 cwd: the working directory to run this cmd. | 60 cwd: the working directory to run this cmd. |
| 58 input: input to pipe into this command through stdin. | 61 input: input to pipe into this command through stdin. |
| 59 enter_chroot: this command should be run from within the chroot. If set, | 62 enter_chroot: this command should be run from within the chroot. If set, |
| 60 cwd must point to the scripts directory. | 63 cwd must point to the scripts directory. |
| 61 shell: If shell is True, the specified command will be executed through | 64 shell: If shell is True, the specified command will be executed through |
| 62 the shell. | 65 the shell. |
|
davidjames
2011/04/14 21:59:05
Document that if shell is True then we expect a st
Peter Mayo
2011/04/19 03:59:39
Done.
| |
| 63 env: If non-None, this is the environment for the new process. | 66 env: If non-None, this is the environment for the new process. |
|
davidjames
2011/04/14 21:59:05
Document that this is not compatible with enter_ch
Peter Mayo
2011/04/19 03:59:39
It's not incompatible, just mostly useless - docum
| |
| 67 extra_env: If set, this is added to the environment for the new process. | |
| 64 ignore_sigint: If True, we'll ignore signal.SIGINT before calling the | 68 ignore_sigint: If True, we'll ignore signal.SIGINT before calling the |
| 65 child. This is the desired behavior if we know our child will handle | 69 child. This is the desired behavior if we know our child will handle |
| 66 Ctrl-C. If we don't do this, I think we and the child will both get | 70 Ctrl-C. If we don't do this, I think we and the child will both get |
| 67 Ctrl-C at the same time, which means we'll forcefully kill the child. | 71 Ctrl-C at the same time, which means we'll forcefully kill the child. |
| 68 combine_stdout_stderr: Combines stdout and stdin streams into stdout. | 72 combine_stdout_stderr: Combines stdout and stdin streams into stdout. |
| 69 | 73 |
| 70 Returns: | 74 Returns: |
| 71 A CommandResult object. | 75 A CommandResult object. |
| 72 | 76 |
| 73 Raises: | 77 Raises: |
| 74 Exception: Raises generic exception on error with optional error_message. | 78 Exception: Raises generic exception on error with optional error_message. |
| 75 """ | 79 """ |
| 76 # Set default for variables. | 80 # Set default for variables. |
| 77 stdout = None | 81 stdout = None |
| 78 stderr = None | 82 stderr = None |
| 79 stdin = None | 83 stdin = None |
| 80 cmd_result = CommandResult() | 84 cmd_result = CommandResult() |
| 81 | 85 |
| 82 # Modify defaults based on parameters. | 86 # Modify defaults based on parameters. |
| 83 if redirect_stdout: stdout = subprocess.PIPE | 87 if redirect_stdout: stdout = subprocess.PIPE |
| 84 if redirect_stderr: stderr = subprocess.PIPE | 88 if redirect_stderr: stderr = subprocess.PIPE |
| 85 if combine_stdout_stderr: stderr = subprocess.STDOUT | 89 if combine_stdout_stderr: stderr = subprocess.STDOUT |
| 86 # TODO(sosa): gpylint complains about redefining built-in 'input'. | 90 # TODO(sosa): gpylint complains about redefining built-in 'input'. |
| 87 # Can we rename this variable? | 91 # Can we rename this variable? |
| 88 if input: stdin = subprocess.PIPE | 92 if input: stdin = subprocess.PIPE |
| 89 if isinstance(cmd, basestring): | 93 |
| 90 if enter_chroot: cmd = './enter_chroot.sh -- ' + cmd | 94 if isinstance(cmd, basestring) or shell: |
|
davidjames
2011/04/14 21:59:05
Can we report errors when the shell variable is no
Peter Mayo
2011/04/19 03:59:39
Done.
| |
| 91 cmd_str = cmd | 95 cmd = ['sh', '-c', cmd] |
|
sosa
2011/04/14 21:22:18
Does this work for piped commands?
davidjames
2011/04/14 21:59:05
It should work. We should probably use /bin/sh for
Peter Mayo
2011/04/19 03:59:39
Done. But I didn't change the isinstance that way
| |
| 92 else: | 96 shell = False |
| 93 if enter_chroot: cmd = ['./enter_chroot.sh', '--'] + cmd | 97 |
| 94 cmd_str = ' '.join(cmd) | 98 # If we are using enter_chroot we need to use enterchroot pass env through |
| 99 # to the final command. | |
| 100 if enter_chroot: | |
| 101 cmd = ['./enter_chroot.sh', '--'] + cmd | |
| 102 if extra_env: | |
|
sosa
2011/04/14 21:22:18
So env is incompatible with extra_env ... should t
davidjames
2011/04/14 21:59:05
env is just plain ignored in enter_chroot mode. Ye
Peter Mayo
2011/04/19 03:59:39
I don't think so ... I have documented the probabl
| |
| 103 for (key, value) in extra_env.items(): | |
| 104 cmd.insert(1, '%s=%s' % (key, value)) | |
| 105 elif extra_env: | |
| 106 if env is not None: | |
|
sosa
2011/04/14 21:22:18
if env:
davidjames
2011/04/14 21:59:05
What if env is the empty array? It's useful to be
Peter Mayo
2011/04/19 03:59:39
I agree with davidjames here.
| |
| 107 env = env.copy() | |
| 108 else: | |
| 109 env = os.environ.copy() | |
|
sosa
2011/04/14 21:22:18
Add extra line
Peter Mayo
2011/04/19 03:59:39
OK, Done... it reads worse to my eye the new way
| |
| 110 env.update(extra_env) | |
| 95 | 111 |
| 96 # Print out the command before running. | 112 # Print out the command before running. |
| 97 if print_cmd: | 113 if print_cmd: |
| 98 if cwd: | 114 if cwd: |
| 99 Info('RunCommand: %s in %s' % (cmd_str, cwd)) | 115 Info('RunCommand: %r in %s' % (cmd, cwd)) |
| 100 else: | 116 else: |
| 101 Info('RunCommand: %s' % cmd_str) | 117 Info('RunCommand: %r' % cmd) |
| 102 cmd_result.cmd = cmd | 118 cmd_result.cmd = cmd |
| 103 | 119 |
| 104 try: | 120 try: |
| 105 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, stdout=stdout, | 121 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, stdout=stdout, |
| 106 stderr=stderr, shell=shell, env=env) | 122 stderr=stderr, shell=False, env=env) |
| 107 if ignore_sigint: | 123 if ignore_sigint: |
| 108 old_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) | 124 old_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) |
| 109 try: | 125 try: |
| 110 (cmd_result.output, cmd_result.error) = proc.communicate(input) | 126 (cmd_result.output, cmd_result.error) = proc.communicate(input) |
| 111 finally: | 127 finally: |
| 112 if ignore_sigint: | 128 if ignore_sigint: |
| 113 signal.signal(signal.SIGINT, old_sigint) | 129 signal.signal(signal.SIGINT, old_sigint) |
| 114 | 130 |
| 115 if exit_code: | 131 if exit_code: |
| 116 cmd_result.returncode = proc.returncode | 132 cmd_result.returncode = proc.returncode |
| 117 | 133 |
| 118 if not error_ok and proc.returncode: | 134 if not error_ok and proc.returncode: |
| 119 msg = ('Command "%s" failed.\n' % cmd_str + | 135 msg = ('Command "%r" failed.\n' % cmd + |
| 120 (error_message or cmd_result.error or cmd_result.output or '')) | 136 (error_message or cmd_result.error or cmd_result.output or '')) |
| 121 raise RunCommandError(msg, cmd) | 137 raise RunCommandError(msg, cmd) |
| 122 # TODO(sosa): is it possible not to use the catch-all Exception here? | 138 # TODO(sosa): is it possible not to use the catch-all Exception here? |
| 123 except Exception, e: | 139 except Exception, e: |
| 124 if not error_ok: | 140 if not error_ok: |
| 125 raise | 141 raise |
| 126 else: | 142 else: |
| 127 Warning(str(e)) | 143 Warning(str(e)) |
| 128 | 144 |
| 129 return cmd_result | 145 return cmd_result |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 377 except RunCommandException as e: | 393 except RunCommandException as e: |
| 378 if not error_ok and retry_count == num_retries: | 394 if not error_ok and retry_count == num_retries: |
| 379 raise e | 395 raise e |
| 380 else: | 396 else: |
| 381 Warning(str(e)) | 397 Warning(str(e)) |
| 382 if print_cmd: | 398 if print_cmd: |
| 383 Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % | 399 Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % |
| 384 (GetCallerName(), cmd, cwd)) | 400 (GetCallerName(), cmd, cwd)) |
| 385 | 401 |
| 386 return output | 402 return output |
| OLD | NEW |