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 signal | 10 import signal |
11 import subprocess | 11 import subprocess |
(...skipping 24 matching lines...) Expand all Loading... | |
36 return (type(self) == type(other) and | 36 return (type(self) == type(other) and |
37 str(self) == str(other) and | 37 str(self) == str(other) and |
38 self.cmd == other.cmd) | 38 self.cmd == other.cmd) |
39 | 39 |
40 def __ne__(self, other): | 40 def __ne__(self, other): |
41 return not self.__eq__(other) | 41 return not self.__eq__(other) |
42 | 42 |
43 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, | 43 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, |
44 exit_code=False, redirect_stdout=False, redirect_stderr=False, | 44 exit_code=False, redirect_stdout=False, redirect_stderr=False, |
45 cwd=None, input=None, enter_chroot=False, shell=False, | 45 cwd=None, input=None, enter_chroot=False, shell=False, |
46 env=None, ignore_sigint=False, combine_stdout_stderr=False): | 46 env=None, extra_env=None, ignore_sigint=False, |
47 combine_stdout_stderr=False): | |
47 """Runs a command. | 48 """Runs a command. |
48 | 49 |
49 Args: | 50 Args: |
50 cmd: cmd to run. Should be input to subprocess.Popen. | 51 cmd: cmd to run. Should be input to subprocess.Popen. If a string, shell |
52 must be true. Otherwise the command must be an array of arguments, and | |
53 shell must be false. | |
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: Controls whether we add a shell as a command interpreter. See cmd |
62 the shell. | 65 since it has to agree as to the type. |
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. If |
67 enter_chroot is true then this is the environment of the enter_chroot, | |
68 most of which gets removed from the cmd run. | |
69 extra_env: If set, this is added to the environment for the new process. | |
70 In enter_chroot=True case, these are specified on the post-entry | |
71 side, and so are often more useful. This dictionary is not used to | |
72 clear any entries though. | |
64 ignore_sigint: If True, we'll ignore signal.SIGINT before calling the | 73 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 | 74 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 | 75 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. | 76 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. | 77 combine_stdout_stderr: Combines stdout and stdin streams into stdout. |
69 | 78 |
70 Returns: | 79 Returns: |
71 A CommandResult object. | 80 A CommandResult object. |
72 | 81 |
73 Raises: | 82 Raises: |
74 Exception: Raises generic exception on error with optional error_message. | 83 Exception: Raises generic exception on error with optional error_message. |
75 """ | 84 """ |
76 # Set default for variables. | 85 # Set default for variables. |
77 stdout = None | 86 stdout = None |
78 stderr = None | 87 stderr = None |
79 stdin = None | 88 stdin = None |
80 cmd_result = CommandResult() | 89 cmd_result = CommandResult() |
81 | 90 |
82 # Modify defaults based on parameters. | 91 # Modify defaults based on parameters. |
83 if redirect_stdout: stdout = subprocess.PIPE | 92 if redirect_stdout: stdout = subprocess.PIPE |
84 if redirect_stderr: stderr = subprocess.PIPE | 93 if redirect_stderr: stderr = subprocess.PIPE |
85 if combine_stdout_stderr: stderr = subprocess.STDOUT | 94 if combine_stdout_stderr: stderr = subprocess.STDOUT |
86 # TODO(sosa): gpylint complains about redefining built-in 'input'. | 95 # TODO(sosa): gpylint complains about redefining built-in 'input'. |
87 # Can we rename this variable? | 96 # Can we rename this variable? |
88 if input: stdin = subprocess.PIPE | 97 if input: stdin = subprocess.PIPE |
98 | |
89 if isinstance(cmd, basestring): | 99 if isinstance(cmd, basestring): |
90 if enter_chroot: cmd = './enter_chroot.sh -- ' + cmd | 100 if not shell: |
91 cmd_str = cmd | 101 raise Exception('Cannot run a string command without a shell') |
92 else: | 102 cmd = ['/bin/sh', '-c', cmd] |
93 if enter_chroot: cmd = ['./enter_chroot.sh', '--'] + cmd | 103 shell = False |
94 cmd_str = ' '.join(cmd) | 104 elif shell: |
105 raise Exception('Cannot run an array command with a shell') | |
106 | |
107 # If we are using enter_chroot we need to use enterchroot pass env through | |
108 # to the final command. | |
109 if enter_chroot: | |
110 cmd = ['./enter_chroot.sh', '--'] + cmd | |
111 if extra_env: | |
112 for (key, value) in extra_env.items(): | |
113 cmd.insert(1, '%s=%s' % (key, value)) | |
114 elif extra_env: | |
115 if env is not None: | |
116 env = env.copy() | |
117 else: | |
118 env = os.environ.copy() | |
119 | |
120 env.update(extra_env) | |
95 | 121 |
96 # Print out the command before running. | 122 # Print out the command before running. |
97 if print_cmd: | 123 if print_cmd: |
98 if cwd: | 124 if cwd: |
99 Info('RunCommand: %s in %s' % (cmd_str, cwd)) | 125 Info('RunCommand: %r in %s' % (cmd, cwd)) |
100 else: | 126 else: |
101 Info('RunCommand: %s' % cmd_str) | 127 Info('RunCommand: %r' % cmd) |
102 cmd_result.cmd = cmd | 128 cmd_result.cmd = cmd |
103 | 129 |
104 try: | 130 try: |
105 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, stdout=stdout, | 131 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, stdout=stdout, |
106 stderr=stderr, shell=shell, env=env) | 132 stderr=stderr, shell=False, env=env) |
107 if ignore_sigint: | 133 if ignore_sigint: |
108 old_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) | 134 old_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) |
109 try: | 135 try: |
110 (cmd_result.output, cmd_result.error) = proc.communicate(input) | 136 (cmd_result.output, cmd_result.error) = proc.communicate(input) |
111 finally: | 137 finally: |
112 if ignore_sigint: | 138 if ignore_sigint: |
113 signal.signal(signal.SIGINT, old_sigint) | 139 signal.signal(signal.SIGINT, old_sigint) |
114 | 140 |
115 if exit_code: | 141 if exit_code: |
116 cmd_result.returncode = proc.returncode | 142 cmd_result.returncode = proc.returncode |
117 | 143 |
118 if not error_ok and proc.returncode: | 144 if not error_ok and proc.returncode: |
119 msg = ('Command "%s" failed.\n' % cmd_str + | 145 msg = ('Command "%r" failed.\n' % cmd + |
120 (error_message or cmd_result.error or cmd_result.output or '')) | 146 (error_message or cmd_result.error or cmd_result.output or '')) |
121 raise RunCommandError(msg, cmd) | 147 raise RunCommandError(msg, cmd) |
122 # TODO(sosa): is it possible not to use the catch-all Exception here? | 148 # TODO(sosa): is it possible not to use the catch-all Exception here? |
149 except OSError, e: | |
150 if not error_ok: | |
151 raise RunCommandError(str(e), cmd) | |
152 else: | |
153 Warning(str(e)) | |
154 | |
davidjames
2011/04/19 22:42:37
no need for this newline
Peter Mayo
2011/04/19 23:14:58
Done.
| |
123 except Exception, e: | 155 except Exception, e: |
124 if not error_ok: | 156 if not error_ok: |
125 raise | 157 raise |
126 else: | 158 else: |
127 Warning(str(e)) | 159 Warning(str(e)) |
128 | 160 |
129 return cmd_result | 161 return cmd_result |
130 | 162 |
131 | 163 |
132 #TODO(sjg): Remove this in favor of operation.Die | 164 #TODO(sjg): Remove this in favor of operation.Die |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
377 except RunCommandException as e: | 409 except RunCommandException as e: |
378 if not error_ok and retry_count == num_retries: | 410 if not error_ok and retry_count == num_retries: |
379 raise e | 411 raise e |
380 else: | 412 else: |
381 Warning(str(e)) | 413 Warning(str(e)) |
382 if print_cmd: | 414 if print_cmd: |
383 Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % | 415 Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % |
384 (GetCallerName(), cmd, cwd)) | 416 (GetCallerName(), cmd, cwd)) |
385 | 417 |
386 return output | 418 return output |
OLD | NEW |