Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Unified Diff: build/android/pylib/device/device_utils.py

Issue 751063002: Allow RunShellCommand to work even with very large commands (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: no minds bent Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/pylib/device/device_utils.py
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index 272e613b6817f915a2cee039caf6ff53de13bea7..10f3be6f881e846a958bbfb2c43fe5ad7637df5c 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -58,6 +58,7 @@ def RestartServer():
class DeviceUtils(object):
+ _MAX_ADB_COMMAND_LENGTH = 512
_VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
@@ -369,6 +370,25 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError(
str(e), str(self)), None, sys.exc_info()[2]
+ def _PrepareShellCommand(self, cmd, cwd=None, env=None, as_root=False):
+ def env_quote(key, value):
+ if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
+ raise KeyError('Invalid shell variable name %r' % key)
+ # using double quotes here to allow interpolation of shell variables
+ return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
+
+ if not isinstance(cmd, basestring):
+ cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
+ if env:
+ env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
+ cmd = '%s %s' % (env, cmd)
+ if cwd:
+ cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
+ if as_root and self.NeedsSU():
+ # "su -c sh -c" allows using shell features in |cmd|
+ cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
+ return cmd
+
@decorators.WithTimeoutAndRetriesFromInstance()
def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
as_root=False, single_line=False,
@@ -421,32 +441,25 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- def env_quote(key, value):
- if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
- raise KeyError('Invalid shell variable name %r' % key)
- # using double quotes here to allow interpolation of shell variables
- return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
+ def do_run(cmd):
+ try:
+ return self.adb.Shell(cmd)
+ except device_errors.AdbCommandFailedError as exc:
+ if check_return:
+ raise
+ else:
+ return exc.output
- if not isinstance(cmd, basestring):
- cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
- if env:
- env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
- cmd = '%s %s' % (env, cmd)
- if cwd:
- cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
- if as_root and self.NeedsSU():
- # "su -c sh -c" allows using shell features in |cmd|
- cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
if timeout is None:
timeout = self._default_timeout
- try:
- output = self.adb.Shell(cmd)
- except device_errors.AdbCommandFailedError as e:
- if check_return:
- raise
- else:
- output = e.output
+ cmd = self._PrepareShellCommand(cmd, cwd=cwd, env=env, as_root=as_root)
+ if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
+ output = do_run(cmd)
+ else:
+ with device_temp_file.DeviceTempFile(self, suffix='.sh') as script:
+ self.PushContents(cmd, script.name)
+ output = do_run('sh %s' % script.name_quoted)
output = output.splitlines()
if single_line:
@@ -884,6 +897,30 @@ class DeviceUtils(object):
return self.old_interface.GetFileContents(device_path)
@decorators.WithTimeoutAndRetriesFromInstance()
+ def PushContents(self, contents, device_path, timeout=None, retries=None):
perezju 2014/11/26 10:33:29 Turns out we need to push contents in quite a few
+ """Pushes |contents| to a file on the device.
+
+ Note: If the contents are small, or root access is needed, then |WriteFile|
+ is a better alternative.
+
+ Args:
+ contents: A string containing the data to write to the device.
+ device_path: A string containing the absolute path to the file to write
+ on the device.
+ timeout: timeout in seconds
+ retries: number of retries
+
+ Raises:
+ CommandFailedError if the file could not be written on the device.
+ CommandTimeoutError on timeout.
+ DeviceUnreachableError on missing device.
+ """
+ with tempfile.NamedTemporaryFile() as host_temp:
+ host_temp.write(contents)
+ host_temp.flush()
+ self.adb.Push(host_temp.name, device_path)
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def WriteFile(self, device_path, contents, as_root=False, force_push=False,
timeout=None, retries=None):
"""Writes |contents| to a file on the device.
@@ -905,24 +942,24 @@ class DeviceUtils(object):
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- if len(contents) < 512 and not force_push:
- cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
- cmd_helper.SingleQuote(device_path))
- self.RunShellCommand(cmd, as_root=as_root, check_return=True)
+ if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
+ cmd = self._PrepareShellCommand(
+ 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
+ cmd_helper.SingleQuote(device_path)),
+ as_root=as_root)
+ if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
+ self.RunShellCommand(cmd, check_return=True)
+ return
perezju 2014/11/26 10:33:28 There was a small chance that the contents are sma
+ if as_root and self.NeedsSU():
+ with device_temp_file.DeviceTempFile(self) as device_temp:
+ self.PushContents(contents, device_temp.name)
+ # Here we need 'cp' rather than 'mv' because the temp and
+ # destination files might be on different file systems (e.g.
+ # on internal storage and an external sd card)
+ self.RunShellCommand(['cp', device_temp.name, device_path],
+ as_root=True, check_return=True)
else:
- with tempfile.NamedTemporaryFile() as host_temp:
- host_temp.write(contents)
- host_temp.flush()
- if as_root and self.NeedsSU():
- with device_temp_file.DeviceTempFile(self) as device_temp:
- self.adb.Push(host_temp.name, device_temp.name)
- # Here we need 'cp' rather than 'mv' because the temp and
- # destination files might be on different file systems (e.g.
- # on internal storage and an external sd card)
- self.RunShellCommand(['cp', device_temp.name, device_path],
- as_root=True, check_return=True)
- else:
- self.adb.Push(host_temp.name, device_path)
+ self.PushContents(contents, device_path)
@decorators.WithTimeoutAndRetriesFromInstance()
def Ls(self, device_path, timeout=None, retries=None):
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698