Merge patch series "tools: Minor clean-ups for the command library"

Simon Glass <sjg@chromium.org> says:

This series adds comments and fixes pylint warnings in the command
library. It also introduces a new, simpler way of running a single
command.

Link: https://lore.kernel.org/r/20250203162704.627469-1-sjg@chromium.org
This commit is contained in:
Tom Rini 2025-03-04 13:31:54 -06:00
commit 986ab810fa
11 changed files with 204 additions and 127 deletions

View file

@ -303,7 +303,7 @@ class TestFunctional(unittest.TestCase):
def setUp(self): def setUp(self):
# Enable this to turn on debugging output # Enable this to turn on debugging output
# tout.init(tout.DEBUG) # tout.init(tout.DEBUG)
command.test_result = None command.TEST_RESULT = None
def tearDown(self): def tearDown(self):
"""Remove the temporary output directory""" """Remove the temporary output directory"""
@ -345,8 +345,9 @@ class TestFunctional(unittest.TestCase):
Arguments to pass, as a list of strings Arguments to pass, as a list of strings
kwargs: Arguments to pass to Command.RunPipe() kwargs: Arguments to pass to Command.RunPipe()
""" """
result = command.run_pipe([[self._binman_pathname] + list(args)], all_args = [self._binman_pathname] + list(args)
capture=True, capture_stderr=True, raise_on_error=False) result = command.run_one(*all_args, capture=True, capture_stderr=True,
raise_on_error=False)
if result.return_code and kwargs.get('raise_on_error', True): if result.return_code and kwargs.get('raise_on_error', True):
raise Exception("Error running '%s': %s" % (' '.join(args), raise Exception("Error running '%s': %s" % (' '.join(args),
result.stdout + result.stderr)) result.stdout + result.stderr))
@ -790,11 +791,11 @@ class TestFunctional(unittest.TestCase):
def testFullHelpInternal(self): def testFullHelpInternal(self):
"""Test that the full help is displayed with -H""" """Test that the full help is displayed with -H"""
try: try:
command.test_result = command.CommandResult() command.TEST_RESULT = command.CommandResult()
result = self._DoBinman('-H') result = self._DoBinman('-H')
help_file = os.path.join(self._binman_dir, 'README.rst') help_file = os.path.join(self._binman_dir, 'README.rst')
finally: finally:
command.test_result = None command.TEST_RESULT = None
def testHelp(self): def testHelp(self):
"""Test that the basic help is displayed with -h""" """Test that the basic help is displayed with -h"""
@ -1882,7 +1883,7 @@ class TestFunctional(unittest.TestCase):
def testGbb(self): def testGbb(self):
"""Test for the Chromium OS Google Binary Block""" """Test for the Chromium OS Google Binary Block"""
command.test_result = self._HandleGbbCommand command.TEST_RESULT = self._HandleGbbCommand
entry_args = { entry_args = {
'keydir': 'devkeys', 'keydir': 'devkeys',
'bmpblk': 'bmpblk.bin', 'bmpblk': 'bmpblk.bin',
@ -1951,7 +1952,7 @@ class TestFunctional(unittest.TestCase):
def testVblock(self): def testVblock(self):
"""Test for the Chromium OS Verified Boot Block""" """Test for the Chromium OS Verified Boot Block"""
self._hash_data = False self._hash_data = False
command.test_result = self._HandleVblockCommand command.TEST_RESULT = self._HandleVblockCommand
entry_args = { entry_args = {
'keydir': 'devkeys', 'keydir': 'devkeys',
} }
@ -1984,7 +1985,7 @@ class TestFunctional(unittest.TestCase):
def testVblockContent(self): def testVblockContent(self):
"""Test that the vblock signs the right data""" """Test that the vblock signs the right data"""
self._hash_data = True self._hash_data = True
command.test_result = self._HandleVblockCommand command.TEST_RESULT = self._HandleVblockCommand
entry_args = { entry_args = {
'keydir': 'devkeys', 'keydir': 'devkeys',
} }
@ -5506,7 +5507,7 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
def testFitSubentryUsesBintool(self): def testFitSubentryUsesBintool(self):
"""Test that binman FIT subentries can use bintools""" """Test that binman FIT subentries can use bintools"""
command.test_result = self._HandleGbbCommand command.TEST_RESULT = self._HandleGbbCommand
entry_args = { entry_args = {
'keydir': 'devkeys', 'keydir': 'devkeys',
'bmpblk': 'bmpblk.bin', 'bmpblk': 'bmpblk.bin',

View file

@ -251,9 +251,9 @@ class KconfigScanner:
'-undef', '-undef',
'-x', 'assembler-with-cpp', '-x', 'assembler-with-cpp',
defconfig] defconfig]
result = command.run_pipe([cmd], capture=True, capture_stderr=True) stdout = command.output(*cmd, capture_stderr=True)
temp = tempfile.NamedTemporaryFile(prefix='buildman-') temp = tempfile.NamedTemporaryFile(prefix='buildman-')
tools.write_file(temp.name, result.stdout, False) tools.write_file(temp.name, stdout, False)
fname = temp.name fname = temp.name
tout.info(f'Processing #include to produce {defconfig}') tout.info(f'Processing #include to produce {defconfig}')
else: else:

View file

@ -510,7 +510,7 @@ class Builder:
stage: Stage that we are at (mrproper, config, oldconfig, build) stage: Stage that we are at (mrproper, config, oldconfig, build)
cwd: Directory where make should be run cwd: Directory where make should be run
args: Arguments to pass to make args: Arguments to pass to make
kwargs: Arguments to pass to command.run_pipe() kwargs: Arguments to pass to command.run_one()
""" """
def check_output(stream, data): def check_output(stream, data):
@ -533,9 +533,10 @@ class Builder:
self._restarting_config = False self._restarting_config = False
self._terminated = False self._terminated = False
cmd = [self.gnu_make] + list(args) cmd = [self.gnu_make] + list(args)
result = command.run_pipe([cmd], capture=True, capture_stderr=True, result = command.run_one(*cmd, capture=True, capture_stderr=True,
cwd=cwd, raise_on_error=False, infile='/dev/null', cwd=cwd, raise_on_error=False,
output_func=check_output, **kwargs) infile='/dev/null', output_func=check_output,
**kwargs)
if self._terminated: if self._terminated:
# Try to be helpful # Try to be helpful

View file

@ -179,13 +179,12 @@ class BuilderThread(threading.Thread):
cwd (str): Working directory to set, or None to leave it alone cwd (str): Working directory to set, or None to leave it alone
*args (list of str): Arguments to pass to 'make' *args (list of str): Arguments to pass to 'make'
**kwargs (dict): A list of keyword arguments to pass to **kwargs (dict): A list of keyword arguments to pass to
command.run_pipe() command.run_one()
Returns: Returns:
CommandResult object CommandResult object
""" """
return self.builder.do_make(commit, brd, stage, cwd, *args, return self.builder.do_make(commit, brd, stage, cwd, *args, **kwargs)
**kwargs)
def _build_args(self, brd, out_dir, out_rel_dir, work_dir, commit_upto): def _build_args(self, brd, out_dir, out_rel_dir, work_dir, commit_upto):
"""Set up arguments to the args list based on the settings """Set up arguments to the args list based on the settings
@ -588,8 +587,9 @@ class BuilderThread(threading.Thread):
lines = [] lines = []
for fname in BASE_ELF_FILENAMES: for fname in BASE_ELF_FILENAMES:
cmd = [f'{self.toolchain.cross}nm', '--size-sort', fname] cmd = [f'{self.toolchain.cross}nm', '--size-sort', fname]
nm_result = command.run_pipe([cmd], capture=True, nm_result = command.run_one(*cmd, capture=True,
capture_stderr=True, cwd=result.out_dir, capture_stderr=True,
cwd=result.out_dir,
raise_on_error=False, env=env) raise_on_error=False, env=env)
if nm_result.stdout: if nm_result.stdout:
nm_fname = self.builder.get_func_sizes_file( nm_fname = self.builder.get_func_sizes_file(
@ -598,8 +598,9 @@ class BuilderThread(threading.Thread):
print(nm_result.stdout, end=' ', file=outf) print(nm_result.stdout, end=' ', file=outf)
cmd = [f'{self.toolchain.cross}objdump', '-h', fname] cmd = [f'{self.toolchain.cross}objdump', '-h', fname]
dump_result = command.run_pipe([cmd], capture=True, dump_result = command.run_one(*cmd, capture=True,
capture_stderr=True, cwd=result.out_dir, capture_stderr=True,
cwd=result.out_dir,
raise_on_error=False, env=env) raise_on_error=False, env=env)
rodata_size = '' rodata_size = ''
if dump_result.stdout: if dump_result.stdout:
@ -613,8 +614,9 @@ class BuilderThread(threading.Thread):
rodata_size = fields[2] rodata_size = fields[2]
cmd = [f'{self.toolchain.cross}size', fname] cmd = [f'{self.toolchain.cross}size', fname]
size_result = command.run_pipe([cmd], capture=True, size_result = command.run_one(*cmd, capture=True,
capture_stderr=True, cwd=result.out_dir, capture_stderr=True,
cwd=result.out_dir,
raise_on_error=False, env=env) raise_on_error=False, env=env)
if size_result.stdout: if size_result.stdout:
lines.append(size_result.stdout.splitlines()[1] + ' ' + lines.append(size_result.stdout.splitlines()[1] + ' ' +
@ -624,9 +626,8 @@ class BuilderThread(threading.Thread):
cmd = [f'{self.toolchain.cross}objcopy', '-O', 'binary', cmd = [f'{self.toolchain.cross}objcopy', '-O', 'binary',
'-j', '.rodata.default_environment', '-j', '.rodata.default_environment',
'env/built-in.o', 'uboot.env'] 'env/built-in.o', 'uboot.env']
command.run_pipe([cmd], capture=True, command.run_one(*cmd, capture=True, capture_stderr=True,
capture_stderr=True, cwd=result.out_dir, cwd=result.out_dir, raise_on_error=False, env=env)
raise_on_error=False, env=env)
if not work_in_output: if not work_in_output:
copy_files(result.out_dir, build_dir, '', ['uboot.env']) copy_files(result.out_dir, build_dir, '', ['uboot.env'])

View file

@ -187,7 +187,7 @@ class TestFunctional(unittest.TestCase):
self._git_dir = os.path.join(self._base_dir, 'src') self._git_dir = os.path.join(self._base_dir, 'src')
self._buildman_pathname = sys.argv[0] self._buildman_pathname = sys.argv[0]
self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
command.test_result = self._HandleCommand command.TEST_RESULT = self._HandleCommand
bsettings.setup(None) bsettings.setup(None)
bsettings.add_file(settings_data) bsettings.add_file(settings_data)
self.setupToolchains() self.setupToolchains()
@ -232,8 +232,8 @@ class TestFunctional(unittest.TestCase):
self._toolchains.Add('gcc', test=False) self._toolchains.Add('gcc', test=False)
def _RunBuildman(self, *args): def _RunBuildman(self, *args):
return command.run_pipe([[self._buildman_pathname] + list(args)], all_args = [self._buildman_pathname] + list(args)
capture=True, capture_stderr=True) return command.run_one(*all_args, capture=True, capture_stderr=True)
def _RunControl(self, *args, brds=False, clean_dir=False, def _RunControl(self, *args, brds=False, clean_dir=False,
test_thread_exceptions=False, get_builder=True): test_thread_exceptions=False, get_builder=True):
@ -266,7 +266,7 @@ class TestFunctional(unittest.TestCase):
return result return result
def testFullHelp(self): def testFullHelp(self):
command.test_result = None command.TEST_RESULT = None
result = self._RunBuildman('-H') result = self._RunBuildman('-H')
help_file = os.path.join(self._buildman_dir, 'README.rst') help_file = os.path.join(self._buildman_dir, 'README.rst')
# Remove possible extraneous strings # Remove possible extraneous strings
@ -277,7 +277,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(0, result.return_code) self.assertEqual(0, result.return_code)
def testHelp(self): def testHelp(self):
command.test_result = None command.TEST_RESULT = None
result = self._RunBuildman('-h') result = self._RunBuildman('-h')
help_file = os.path.join(self._buildman_dir, 'README.rst') help_file = os.path.join(self._buildman_dir, 'README.rst')
self.assertTrue(len(result.stdout) > 1000) self.assertTrue(len(result.stdout) > 1000)
@ -286,11 +286,11 @@ class TestFunctional(unittest.TestCase):
def testGitSetup(self): def testGitSetup(self):
"""Test gitutils.Setup(), from outside the module itself""" """Test gitutils.Setup(), from outside the module itself"""
command.test_result = command.CommandResult(return_code=1) command.TEST_RESULT = command.CommandResult(return_code=1)
gitutil.setup() gitutil.setup()
self.assertEqual(gitutil.use_no_decorate, False) self.assertEqual(gitutil.use_no_decorate, False)
command.test_result = command.CommandResult(return_code=0) command.TEST_RESULT = command.CommandResult(return_code=0)
gitutil.setup() gitutil.setup()
self.assertEqual(gitutil.use_no_decorate, True) self.assertEqual(gitutil.use_no_decorate, True)
@ -445,7 +445,7 @@ class TestFunctional(unittest.TestCase):
stage: Stage that we are at (mrproper, config, build) stage: Stage that we are at (mrproper, config, build)
cwd: Directory where make should be run cwd: Directory where make should be run
args: Arguments to pass to make args: Arguments to pass to make
kwargs: Arguments to pass to command.run_pipe() kwargs: Arguments to pass to command.run_one()
""" """
self._make_calls += 1 self._make_calls += 1
out_dir = '' out_dir = ''

View file

@ -100,7 +100,7 @@ class Toolchain:
else: else:
self.priority = priority self.priority = priority
if test: if test:
result = command.run_pipe([cmd], capture=True, env=env, result = command.run_one(*cmd, capture=True, env=env,
raise_on_error=False) raise_on_error=False)
self.ok = result.return_code == 0 self.ok = result.return_code == 0
if verbose: if verbose:

View file

@ -711,7 +711,7 @@ def get_list(commit_range, git_dir=None, count=None):
""" """
params = gitutil.log_cmd(commit_range, reverse=True, count=count, params = gitutil.log_cmd(commit_range, reverse=True, count=count,
git_dir=git_dir) git_dir=git_dir)
return command.run_pipe([params], capture=True).stdout return command.run_one(*params, capture=True).stdout
def get_metadata_for_list(commit_range, git_dir=None, count=None, def get_metadata_for_list(commit_range, git_dir=None, count=None,
series=None, allow_overwrite=False): series=None, allow_overwrite=False):

View file

@ -43,18 +43,16 @@ def rm_kconfig_include(path):
Args: Args:
path: Path to search for and remove path: Path to search for and remove
""" """
cmd = ['git', 'grep', path] stdout = command.output('git', 'grep', path, raise_on_error=False)
stdout = command.run_pipe([cmd], capture=True, raise_on_error=False).stdout
if not stdout: if not stdout:
return return
fname = stdout.split(':')[0] fname = stdout.split(':')[0]
print("Fixing up '%s' to remove reference to '%s'" % (fname, path)) print("Fixing up '%s' to remove reference to '%s'" % (fname, path))
cmd = ['sed', '-i', '\|%s|d' % path, fname] stdout = command.run_one('sed', '-i', rf'\|{path}|d', fname,
stdout = command.run_pipe([cmd], capture=True).stdout capture=True).stdout
cmd = ['git', 'add', fname] stdout = command.output('git', 'add', fname)
stdout = command.run_pipe([cmd], capture=True).stdout
def rm_board(board): def rm_board(board):
"""Create a commit which removes a single board """Create a commit which removes a single board
@ -68,8 +66,7 @@ def rm_board(board):
""" """
# Find all MAINTAINERS and Kconfig files which mention the board # Find all MAINTAINERS and Kconfig files which mention the board
cmd = ['git', 'grep', '-l', board] stdout = command.output('git', 'grep', '-l', board)
stdout = command.run_pipe([cmd], capture=True).stdout
maintain = [] maintain = []
kconfig = [] kconfig = []
for line in stdout.splitlines(): for line in stdout.splitlines():
@ -109,16 +106,14 @@ def rm_board(board):
# Search for Kconfig files in the resulting list. Remove any 'source' lines # Search for Kconfig files in the resulting list. Remove any 'source' lines
# which reference Kconfig files we want to remove # which reference Kconfig files we want to remove
for path in real: for path in real:
cmd = ['find', path] stdout = command.output('find', path, raise_on_error=False)
stdout = (command.run_pipe([cmd], capture=True, raise_on_error=False).
stdout)
for fname in stdout.splitlines(): for fname in stdout.splitlines():
if fname.endswith('Kconfig'): if fname.endswith('Kconfig'):
rm_kconfig_include(fname) rm_kconfig_include(fname)
# Remove unwanted files # Remove unwanted files
cmd = ['git', 'rm', '-r'] + real cmd = ['git', 'rm', '-r'] + real
stdout = command.run_pipe([cmd], capture=True).stdout stdout = command.output(*cmd, capture=True)
## Change the messages as needed ## Change the messages as needed
msg = '''arm: Remove %s board msg = '''arm: Remove %s board
@ -131,13 +126,11 @@ Remove it.
msg += 'Patch-cc: %s\n' % name msg += 'Patch-cc: %s\n' % name
# Create the commit # Create the commit
cmd = ['git', 'commit', '-s', '-m', msg] stdout = command.output('git', 'commit', '-s', '-m', msg)
stdout = command.run_pipe([cmd], capture=True).stdout
# Check if the board is mentioned anywhere else. The user will need to deal # Check if the board is mentioned anywhere else. The user will need to deal
# with this # with this
cmd = ['git', 'grep', '-il', board] print(command.output('git', 'grep', '-il', board, raise_on_error=False))
print(command.run_pipe([cmd], capture=True, raise_on_error=False).stdout)
print(' '.join(cmd)) print(' '.join(cmd))
for board in sys.argv[1:]: for board in sys.argv[1:]:

View file

@ -1,21 +1,44 @@
# SPDX-License-Identifier: GPL-2.0+ # SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2011 The Chromium OS Authors. """
# Shell command ease-ups for Python
import os Copyright (c) 2011 The Chromium OS Authors.
"""
import subprocess
from u_boot_pylib import cros_subprocess from u_boot_pylib import cros_subprocess
"""Shell command ease-ups for Python.""" # This permits interception of RunPipe for test purposes. If it is set to
# a function, then that function is called with the pipe list being
# executed. Otherwise, it is assumed to be a CommandResult object, and is
# returned as the result for every run_pipe() call.
# When this value is None, commands are executed as normal.
TEST_RESULT = None
class CommandExc(Exception):
"""Reports an exception to the caller"""
def __init__(self, msg, result):
"""Set up a new exception object
Args:
result (CommandResult): Execution result so far
"""
super().__init__(msg)
self.result = result
class CommandResult: class CommandResult:
"""A class which captures the result of executing a command. """A class which captures the result of executing a command.
Members: Members:
stdout: stdout obtained from command, as a string stdout (bytes): stdout obtained from command, as a string
stderr: stderr obtained from command, as a string stderr (bytes): stderr obtained from command, as a string
return_code: Return code from command combined (bytes): stdout and stderr interleaved
exception: Exception received, or None if all ok return_code (int): Return code from command
exception (Exception): Exception received, or None if all ok
output (str or None): Returns output as a single line if requested
""" """
def __init__(self, stdout='', stderr='', combined='', return_code=0, def __init__(self, stdout='', stderr='', combined='', return_code=0,
exception=None): exception=None):
@ -24,8 +47,16 @@ class CommandResult:
self.combined = combined self.combined = combined
self.return_code = return_code self.return_code = return_code
self.exception = exception self.exception = exception
self.output = None
def to_output(self, binary): def to_output(self, binary):
"""Converts binary output to its final form
Args:
binary (bool): True to report binary output, False to use strings
Returns:
self
"""
if not binary: if not binary:
self.stdout = self.stdout.decode('utf-8') self.stdout = self.stdout.decode('utf-8')
self.stderr = self.stderr.decode('utf-8') self.stderr = self.stderr.decode('utf-8')
@ -33,44 +64,42 @@ class CommandResult:
return self return self
# This permits interception of RunPipe for test purposes. If it is set to def run_pipe(pipe_list, infile=None, outfile=None, capture=False,
# a function, then that function is called with the pipe list being capture_stderr=False, oneline=False, raise_on_error=True, cwd=None,
# executed. Otherwise, it is assumed to be a CommandResult object, and is binary=False, output_func=None, **kwargs):
# returned as the result for every run_pipe() call.
# When this value is None, commands are executed as normal.
test_result = None
def run_pipe(pipe_list, infile=None, outfile=None,
capture=False, capture_stderr=False, oneline=False,
raise_on_error=True, cwd=None, binary=False,
output_func=None, **kwargs):
""" """
Perform a command pipeline, with optional input/output filenames. Perform a command pipeline, with optional input/output filenames.
Args: Args:
pipe_list: List of command lines to execute. Each command line is pipe_list (list of list): List of command lines to execute. Each command
piped into the next, and is itself a list of strings. For line is piped into the next, and is itself a list of strings. For
example [ ['ls', '.git'] ['wc'] ] will pipe the output of example [ ['ls', '.git'] ['wc'] ] will pipe the output of
'ls .git' into 'wc'. 'ls .git' into 'wc'.
infile: File to provide stdin to the pipeline infile (str): File to provide stdin to the pipeline
outfile: File to store stdout outfile (str): File to store stdout
capture: True to capture output capture (bool): True to capture output
capture_stderr: True to capture stderr capture_stderr (bool): True to capture stderr
oneline: True to strip newline chars from output oneline (bool): True to strip newline chars from output
output_func: Output function to call with each output fragment raise_on_error (bool): True to raise on an error, False to return it in
(if it returns True the function terminates) the CommandResult
kwargs: Additional keyword arguments to cros_subprocess.Popen() cwd (str or None): Directory to run the command in
binary (bool): True to report binary output, False to use strings
output_func (function): Output function to call with each output
fragment (if it returns True the function terminates)
**kwargs: Additional keyword arguments to cros_subprocess.Popen()
Returns: Returns:
CommandResult object CommandResult object
Raises:
CommandExc if an exception happens
""" """
if test_result: if TEST_RESULT:
if hasattr(test_result, '__call__'): if hasattr(TEST_RESULT, '__call__'):
# pylint: disable=E1102 # pylint: disable=E1102
result = test_result(pipe_list=pipe_list) result = TEST_RESULT(pipe_list=pipe_list)
if result: if result:
return result return result
else: else:
return test_result return TEST_RESULT
# No result: fall through to normal processing # No result: fall through to normal processing
result = CommandResult(b'', b'', b'') result = CommandResult(b'', b'', b'')
last_pipe = None last_pipe = None
@ -96,7 +125,8 @@ def run_pipe(pipe_list, infile=None, outfile=None,
except Exception as err: except Exception as err:
result.exception = err result.exception = err
if raise_on_error: if raise_on_error:
raise Exception("Error running '%s': %s" % (user_pipestr, str)) raise CommandExc(f"Error running '{user_pipestr}': {err}",
result) from err
result.return_code = 255 result.return_code = 255
return result.to_output(binary) return result.to_output(binary)
@ -107,31 +137,84 @@ def run_pipe(pipe_list, infile=None, outfile=None,
result.output = result.stdout.rstrip(b'\r\n') result.output = result.stdout.rstrip(b'\r\n')
result.return_code = last_pipe.wait() result.return_code = last_pipe.wait()
if raise_on_error and result.return_code: if raise_on_error and result.return_code:
raise Exception("Error running '%s'" % user_pipestr) raise CommandExc(f"Error running '{user_pipestr}'", result)
return result.to_output(binary) return result.to_output(binary)
def output(*cmd, **kwargs): def output(*cmd, **kwargs):
"""Run a command and return its output
Args:
*cmd (list of str): Command to run
**kwargs (dict of args): Extra arguments to pass in
Returns:
str: command output
"""
kwargs['raise_on_error'] = kwargs.get('raise_on_error', True) kwargs['raise_on_error'] = kwargs.get('raise_on_error', True)
return run_pipe([cmd], capture=True, **kwargs).stdout return run_pipe([cmd], capture=True, **kwargs).stdout
def output_one_line(*cmd, **kwargs): def output_one_line(*cmd, **kwargs):
"""Run a command and output it as a single-line string """Run a command and output it as a single-line string
The command us expected to produce a single line of output The command is expected to produce a single line of output
Args:
*cmd (list of str): Command to run
**kwargs (dict of args): Extra arguments to pass in
Returns: Returns:
String containing output of command str: output of command with all newlines removed
""" """
raise_on_error = kwargs.pop('raise_on_error', True) raise_on_error = kwargs.pop('raise_on_error', True)
result = run_pipe([cmd], capture=True, oneline=True, result = run_pipe([cmd], capture=True, oneline=True,
raise_on_error=raise_on_error, **kwargs).stdout.strip() raise_on_error=raise_on_error, **kwargs).stdout.strip()
return result return result
def run(*cmd, **kwargs): def run(*cmd, **kwargs):
"""Run a command
Note that you must add 'capture' to kwargs to obtain non-empty output
Args:
*cmd (list of str): Command to run
**kwargs (dict of args): Extra arguments to pass in
Returns:
str: output of command
"""
return run_pipe([cmd], **kwargs).stdout return run_pipe([cmd], **kwargs).stdout
def run_one(*cmd, **kwargs):
"""Run a single command
Note that you must add 'capture' to kwargs to obtain non-empty output
Args:
*cmd (list of str): Command to run
**kwargs (dict of args): Extra arguments to pass in
Returns:
CommandResult: output of command
"""
return run_pipe([cmd], **kwargs)
def run_list(cmd): def run_list(cmd):
"""Run a command and return its output
Args:
cmd (list of str): Command to run
Returns:
str: output of command
"""
return run_pipe([cmd], capture=True).stdout return run_pipe([cmd], capture=True).stdout
def stop_all(): def stop_all():
"""Stop all subprocesses initiated with cros_subprocess"""
cros_subprocess.stay_alive = False cros_subprocess.stay_alive = False

View file

@ -65,8 +65,8 @@ def count_commits_to_branch(branch):
rev_range = '%s..%s' % (us, branch) rev_range = '%s..%s' % (us, branch)
else: else:
rev_range = '@{upstream}..' rev_range = '@{upstream}..'
pipe = [log_cmd(rev_range, oneline=True)] cmd = log_cmd(rev_range, oneline=True)
result = command.run_pipe(pipe, capture=True, capture_stderr=True, result = command.run_one(*cmd, capture=True, capture_stderr=True,
oneline=True, raise_on_error=False) oneline=True, raise_on_error=False)
if result.return_code: if result.return_code:
raise ValueError('Failed to determine upstream: %s' % raise ValueError('Failed to determine upstream: %s' %
@ -84,8 +84,7 @@ def name_revision(commit_hash):
Return: Return:
Name of revision, if any, else None Name of revision, if any, else None
""" """
pipe = ['git', 'name-rev', commit_hash] stdout = command.output_one_line('git', 'name-rev', commit_hash)
stdout = command.run_pipe([pipe], capture=True, oneline=True).stdout
# We expect a commit, a space, then a revision name # We expect a commit, a space, then a revision name
name = stdout.split(' ')[1].strip() name = stdout.split(' ')[1].strip()
@ -108,8 +107,8 @@ def guess_upstream(git_dir, branch):
Name of upstream branch (e.g. 'upstream/master') or None if none Name of upstream branch (e.g. 'upstream/master') or None if none
Warning/error message, or None if none Warning/error message, or None if none
""" """
pipe = [log_cmd(branch, git_dir=git_dir, oneline=True, count=100)] cmd = log_cmd(branch, git_dir=git_dir, oneline=True, count=100)
result = command.run_pipe(pipe, capture=True, capture_stderr=True, result = command.run_one(*cmd, capture=True, capture_stderr=True,
raise_on_error=False) raise_on_error=False)
if result.return_code: if result.return_code:
return None, "Branch '%s' not found" % branch return None, "Branch '%s' not found" % branch
@ -140,7 +139,7 @@ def get_upstream(git_dir, branch):
'branch.%s.remote' % branch) 'branch.%s.remote' % branch)
merge = command.output_one_line('git', '--git-dir', git_dir, 'config', merge = command.output_one_line('git', '--git-dir', git_dir, 'config',
'branch.%s.merge' % branch) 'branch.%s.merge' % branch)
except Exception: except command.CommandExc:
upstream, msg = guess_upstream(git_dir, branch) upstream, msg = guess_upstream(git_dir, branch)
return upstream, msg return upstream, msg
@ -183,8 +182,8 @@ def count_commits_in_range(git_dir, range_expr):
Number of patches that exist in the supplied range or None if none Number of patches that exist in the supplied range or None if none
were found were found
""" """
pipe = [log_cmd(range_expr, git_dir=git_dir, oneline=True)] cmd = log_cmd(range_expr, git_dir=git_dir, oneline=True)
result = command.run_pipe(pipe, capture=True, capture_stderr=True, result = command.run_one(*cmd, capture=True, capture_stderr=True,
raise_on_error=False) raise_on_error=False)
if result.return_code: if result.return_code:
return None, "Range '%s' not found or is invalid" % range_expr return None, "Range '%s' not found or is invalid" % range_expr
@ -250,9 +249,8 @@ def clone(git_dir, output_dir):
Args: Args:
commit_hash: Commit hash to check out commit_hash: Commit hash to check out
""" """
pipe = ['git', 'clone', git_dir, '.'] result = command.run_one('git', 'clone', git_dir, '.', capture=True,
result = command.run_pipe([pipe], capture=True, cwd=output_dir, cwd=output_dir, capture_stderr=True)
capture_stderr=True)
if result.return_code != 0: if result.return_code != 0:
raise OSError('git clone: %s' % result.stderr) raise OSError('git clone: %s' % result.stderr)
@ -263,13 +261,13 @@ def fetch(git_dir=None, work_tree=None):
Args: Args:
commit_hash: Commit hash to check out commit_hash: Commit hash to check out
""" """
pipe = ['git'] cmd = ['git']
if git_dir: if git_dir:
pipe.extend(['--git-dir', git_dir]) cmd.extend(['--git-dir', git_dir])
if work_tree: if work_tree:
pipe.extend(['--work-tree', work_tree]) cmd.extend(['--work-tree', work_tree])
pipe.append('fetch') cmd.append('fetch')
result = command.run_pipe([pipe], capture=True, capture_stderr=True) result = command.run_one(*cmd, capture=True, capture_stderr=True)
if result.return_code != 0: if result.return_code != 0:
raise OSError('git fetch: %s' % result.stderr) raise OSError('git fetch: %s' % result.stderr)
@ -283,8 +281,8 @@ def check_worktree_is_available(git_dir):
Returns: Returns:
True if git-worktree commands will work, False otherwise. True if git-worktree commands will work, False otherwise.
""" """
pipe = ['git', '--git-dir', git_dir, 'worktree', 'list'] result = command.run_one('git', '--git-dir', git_dir, 'worktree', 'list',
result = command.run_pipe([pipe], capture=True, capture_stderr=True, capture=True, capture_stderr=True,
raise_on_error=False) raise_on_error=False)
return result.return_code == 0 return result.return_code == 0
@ -298,10 +296,10 @@ def add_worktree(git_dir, output_dir, commit_hash=None):
commit_hash: Commit hash to checkout commit_hash: Commit hash to checkout
""" """
# We need to pass --detach to avoid creating a new branch # We need to pass --detach to avoid creating a new branch
pipe = ['git', '--git-dir', git_dir, 'worktree', 'add', '.', '--detach'] cmd = ['git', '--git-dir', git_dir, 'worktree', 'add', '.', '--detach']
if commit_hash: if commit_hash:
pipe.append(commit_hash) cmd.append(commit_hash)
result = command.run_pipe([pipe], capture=True, cwd=output_dir, result = command.run_one(*cmd, capture=True, cwd=output_dir,
capture_stderr=True) capture_stderr=True)
if result.return_code != 0: if result.return_code != 0:
raise OSError('git worktree add: %s' % result.stderr) raise OSError('git worktree add: %s' % result.stderr)
@ -313,8 +311,8 @@ def prune_worktrees(git_dir):
Args: Args:
git_dir: The repository whose deleted worktrees should be pruned git_dir: The repository whose deleted worktrees should be pruned
""" """
pipe = ['git', '--git-dir', git_dir, 'worktree', 'prune'] result = command.run_one('git', '--git-dir', git_dir, 'worktree', 'prune',
result = command.run_pipe([pipe], capture=True, capture_stderr=True) capture=True, capture_stderr=True)
if result.return_code != 0: if result.return_code != 0:
raise OSError('git worktree prune: %s' % result.stderr) raise OSError('git worktree prune: %s' % result.stderr)
@ -687,7 +685,7 @@ def setup():
if alias_fname: if alias_fname:
settings.ReadGitAliases(alias_fname) settings.ReadGitAliases(alias_fname)
cmd = log_cmd(None, count=0) cmd = log_cmd(None, count=0)
use_no_decorate = (command.run_pipe([cmd], raise_on_error=False) use_no_decorate = (command.run_one(*cmd, raise_on_error=False)
.return_code == 0) .return_code == 0)

View file

@ -376,7 +376,7 @@ def run_result(name, *args, **kwargs):
args = tuple(extra_args) + args args = tuple(extra_args) + args
name = os.path.expanduser(name) # Expand paths containing ~ name = os.path.expanduser(name) # Expand paths containing ~
all_args = (name,) + args all_args = (name,) + args
result = command.run_pipe([all_args], capture=True, capture_stderr=True, result = command.run_one(*all_args, capture=True, capture_stderr=True,
env=env, raise_on_error=False, binary=binary) env=env, raise_on_error=False, binary=binary)
if result.return_code: if result.return_code:
if raise_on_error: if raise_on_error: