blob: 97d983756080243f896b3ee210b9e13899f8e097 [file] [log] [blame]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00001"""Shell command runner function."""
TatWai Chong6a46b252024-01-12 13:13:22 -08002# Copyright (c) 2020-2024, ARM Limited.
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00003# SPDX-License-Identifier: Apache-2.0
4import shlex
5import subprocess
6
7
8class RunShCommandError(Exception):
9 """Exception raised for errors running the shell command.
10
11 Attributes:
12 return_code - non-zero return code from running command
13 full_cmd_esc - command and arguments list (pre-escaped)
14 stderr - (optional) - standard error output
15 """
16
17 def __init__(self, return_code, full_cmd_esc, stderr=None, stdout=None):
18 """Initialize run shell command error."""
19 self.return_code = return_code
20 self.full_cmd_esc = full_cmd_esc
21 self.stderr = stderr
22 self.stdout = stdout
23 self.message = "Error {} running command: {}".format(
24 self.return_code, " ".join(self.full_cmd_esc)
25 )
26 if stdout:
27 self.message = "{}\n{}".format(self.message, self.stdout)
28 if stderr:
29 self.message = "{}\n{}".format(self.message, self.stderr)
30 super().__init__(self.message)
31
32
33def run_sh_command(full_cmd, verbose=False, capture_output=False):
34 """Run an external shell command.
35
TatWai Chong6a46b252024-01-12 13:13:22 -080036 full_cmd: string, or array containing shell command and its arguments
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000037 verbose: optional flag that enables verbose output
38 capture_output: optional flag to return captured stdout/stderr
39 """
TatWai Chong6a46b252024-01-12 13:13:22 -080040
41 is_str = True if isinstance(full_cmd, str) else False
42 is_list = True if isinstance(full_cmd, list) else False
43
44 if is_list:
45 # Quote the command line for printing
46 full_cmd_esc = [shlex.quote(x) for x in full_cmd]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000047
48 if verbose:
TatWai Chong6a46b252024-01-12 13:13:22 -080049 if is_list:
50 print("### Running {}".format(" ".join(full_cmd_esc)))
51 if is_str:
52 print("### Running {}".format(full_cmd))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000053
54 if capture_output:
TatWai Chong6a46b252024-01-12 13:13:22 -080055 rc = subprocess.run(
56 full_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=is_str
57 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000058 stdout = rc.stdout.decode("utf-8")
59 stderr = rc.stderr.decode("utf-8")
60 if verbose:
61 if stdout:
62 print(stdout, end="")
63 if stderr:
64 print(stderr, end="")
65 else:
66 stdout, stderr = None, None
TatWai Chong6a46b252024-01-12 13:13:22 -080067 rc = subprocess.run(full_cmd, shell=is_str)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000068
69 if rc.returncode != 0:
TatWai Chong6a46b252024-01-12 13:13:22 -080070 raise RunShCommandError(
71 rc.returncode, full_cmd_esc if is_list else full_cmd, stderr, stdout
72 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000073 return (stdout, stderr)