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

Side by Side 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: fixes w.r.t. latest comments Created 6 years 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 unified diff | Download patch
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium 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 """Provides a variety of device interactions based on adb. 5 """Provides a variety of device interactions based on adb.
6 6
7 Eventually, this will be based on adb_wrapper. 7 Eventually, this will be based on adb_wrapper.
8 """ 8 """
9 # pylint: disable=W0613 9 # pylint: disable=W0613
10 10
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
51 """Restarts the adb server. 51 """Restarts the adb server.
52 52
53 Raises: 53 Raises:
54 CommandFailedError if we fail to kill or restart the server. 54 CommandFailedError if we fail to kill or restart the server.
55 """ 55 """
56 pylib.android_commands.AndroidCommands().RestartAdbServer() 56 pylib.android_commands.AndroidCommands().RestartAdbServer()
57 57
58 58
59 class DeviceUtils(object): 59 class DeviceUtils(object):
60 60
61 _MAX_ADB_COMMAND_LENGTH = 512
61 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 62 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
62 63
63 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, 64 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
64 default_retries=_DEFAULT_RETRIES): 65 default_retries=_DEFAULT_RETRIES):
65 """DeviceUtils constructor. 66 """DeviceUtils constructor.
66 67
67 Args: 68 Args:
68 device: Either a device serial, an existing AdbWrapper instance, an 69 device: Either a device serial, an existing AdbWrapper instance, an
69 an existing AndroidCommands instance, or nothing. 70 an existing AndroidCommands instance, or nothing.
70 default_timeout: An integer containing the default number of seconds to 71 default_timeout: An integer containing the default number of seconds to
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 more lines. 421 more lines.
421 CommandTimeoutError on timeout. 422 CommandTimeoutError on timeout.
422 DeviceUnreachableError on missing device. 423 DeviceUnreachableError on missing device.
423 """ 424 """
424 def env_quote(key, value): 425 def env_quote(key, value):
425 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): 426 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
426 raise KeyError('Invalid shell variable name %r' % key) 427 raise KeyError('Invalid shell variable name %r' % key)
427 # using double quotes here to allow interpolation of shell variables 428 # using double quotes here to allow interpolation of shell variables
428 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) 429 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
429 430
431 def do_run(cmd):
432 try:
433 return self.adb.Shell(cmd)
434 except device_errors.AdbCommandFailedError as exc:
435 if check_return:
436 raise
437 else:
438 return exc.output
439
430 if not isinstance(cmd, basestring): 440 if not isinstance(cmd, basestring):
431 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 441 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
432 if env: 442 if env:
433 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) 443 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
434 cmd = '%s %s' % (env, cmd) 444 cmd = '%s %s' % (env, cmd)
435 if cwd: 445 if cwd:
436 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) 446 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
437 if as_root and self.NeedsSU(): 447 if as_root and self.NeedsSU():
438 # "su -c sh -c" allows using shell features in |cmd| 448 # "su -c sh -c" allows using shell features in |cmd|
439 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) 449 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
440 if timeout is None: 450 if timeout is None:
441 timeout = self._default_timeout 451 timeout = self._default_timeout
442 452
443 try: 453 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
444 output = self.adb.Shell(cmd) 454 output = do_run(cmd)
445 except device_errors.AdbCommandFailedError as e: 455 else:
446 if check_return: 456 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
447 raise 457 self._WriteFileWithPush(script.name, cmd)
448 else: 458 output = do_run('sh %s' % script.name_quoted)
449 output = e.output
450 459
451 output = output.splitlines() 460 output = output.splitlines()
452 if single_line: 461 if single_line:
453 if not output: 462 if not output:
454 return '' 463 return ''
455 elif len(output) == 1: 464 elif len(output) == 1:
456 return output[0] 465 return output[0]
457 else: 466 else:
458 msg = 'one line of output was expected, but got: %s' 467 msg = 'one line of output was expected, but got: %s'
459 raise device_errors.CommandFailedError(msg % output, str(self)) 468 raise device_errors.CommandFailedError(msg % output, str(self))
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after
880 # TODO(jbudorick) Evaluate whether we want to return a list of lines after 889 # TODO(jbudorick) Evaluate whether we want to return a list of lines after
881 # the implementation switch, and if file not found should raise exception. 890 # the implementation switch, and if file not found should raise exception.
882 if as_root: 891 if as_root:
883 if not self.old_interface.CanAccessProtectedFileContents(): 892 if not self.old_interface.CanAccessProtectedFileContents():
884 raise device_errors.CommandFailedError( 893 raise device_errors.CommandFailedError(
885 'Cannot read from %s with root privileges.' % device_path) 894 'Cannot read from %s with root privileges.' % device_path)
886 return self.old_interface.GetProtectedFileContents(device_path) 895 return self.old_interface.GetProtectedFileContents(device_path)
887 else: 896 else:
888 return self.old_interface.GetFileContents(device_path) 897 return self.old_interface.GetFileContents(device_path)
889 898
899 def _WriteFileWithPush(self, device_path, contents):
900 with tempfile.NamedTemporaryFile() as host_temp:
901 host_temp.write(contents)
902 host_temp.flush()
903 self.adb.Push(host_temp.name, device_path)
904
890 @decorators.WithTimeoutAndRetriesFromInstance() 905 @decorators.WithTimeoutAndRetriesFromInstance()
891 def WriteFile(self, device_path, contents, as_root=False, force_push=False, 906 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
892 timeout=None, retries=None): 907 timeout=None, retries=None):
893 """Writes |contents| to a file on the device. 908 """Writes |contents| to a file on the device.
894 909
895 Args: 910 Args:
896 device_path: A string containing the absolute path to the file to write 911 device_path: A string containing the absolute path to the file to write
897 on the device. 912 on the device.
898 contents: A string containing the data to write to the device. 913 contents: A string containing the data to write to the device.
899 as_root: A boolean indicating whether the write should be executed with 914 as_root: A boolean indicating whether the write should be executed with
900 root privileges (if available). 915 root privileges (if available).
901 force_push: A boolean indicating whether to force the operation to be 916 force_push: A boolean indicating whether to force the operation to be
902 performed by pushing a file to the device. The default is, when the 917 performed by pushing a file to the device. The default is, when the
903 contents are short, to pass the contents using a shell script instead. 918 contents are short, to pass the contents using a shell script instead.
904 timeout: timeout in seconds 919 timeout: timeout in seconds
905 retries: number of retries 920 retries: number of retries
906 921
907 Raises: 922 Raises:
908 CommandFailedError if the file could not be written on the device. 923 CommandFailedError if the file could not be written on the device.
909 CommandTimeoutError on timeout. 924 CommandTimeoutError on timeout.
910 DeviceUnreachableError on missing device. 925 DeviceUnreachableError on missing device.
911 """ 926 """
912 if len(contents) < 512 and not force_push: 927 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
jbudorick 2014/12/04 15:34:01 nit: Can we comment the reason for the 3-way split
913 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), 928 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
914 cmd_helper.SingleQuote(device_path)) 929 cmd_helper.SingleQuote(device_path))
915 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 930 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
931 elif as_root and self.NeedsSU():
932 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
933 self._WriteFileWithPush(device_temp.name, contents)
934 # Here we need 'cp' rather than 'mv' because the temp and
935 # destination files might be on different file systems (e.g.
936 # on internal storage and an external sd card)
937 self.RunShellCommand(['cp', device_temp.name, device_path],
938 as_root=True, check_return=True)
916 else: 939 else:
917 with tempfile.NamedTemporaryFile() as host_temp: 940 self._WriteFileWithPush(device_path, contents)
918 host_temp.write(contents)
919 host_temp.flush()
920 if as_root and self.NeedsSU():
921 with device_temp_file.DeviceTempFile(self) as device_temp:
922 self.adb.Push(host_temp.name, device_temp.name)
923 # Here we need 'cp' rather than 'mv' because the temp and
924 # destination files might be on different file systems (e.g.
925 # on internal storage and an external sd card)
926 self.RunShellCommand(['cp', device_temp.name, device_path],
927 as_root=True, check_return=True)
928 else:
929 self.adb.Push(host_temp.name, device_path)
930 941
931 @decorators.WithTimeoutAndRetriesFromInstance() 942 @decorators.WithTimeoutAndRetriesFromInstance()
932 def Ls(self, device_path, timeout=None, retries=None): 943 def Ls(self, device_path, timeout=None, retries=None):
933 """Lists the contents of a directory on the device. 944 """Lists the contents of a directory on the device.
934 945
935 Args: 946 Args:
936 device_path: A string containing the path of the directory on the device 947 device_path: A string containing the path of the directory on the device
937 to list. 948 to list.
938 timeout: timeout in seconds 949 timeout: timeout in seconds
939 retries: number of retries 950 retries: number of retries
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after
1167 Returns: 1178 Returns:
1168 A Parallelizer operating over |devices|. 1179 A Parallelizer operating over |devices|.
1169 """ 1180 """
1170 if not devices or len(devices) == 0: 1181 if not devices or len(devices) == 0:
1171 devices = pylib.android_commands.GetAttachedDevices() 1182 devices = pylib.android_commands.GetAttachedDevices()
1172 parallelizer_type = (parallelizer.Parallelizer if async 1183 parallelizer_type = (parallelizer.Parallelizer if async
1173 else parallelizer.SyncParallelizer) 1184 else parallelizer.SyncParallelizer)
1174 return parallelizer_type([ 1185 return parallelizer_type([
1175 d if isinstance(d, DeviceUtils) else DeviceUtils(d) 1186 d if isinstance(d, DeviceUtils) else DeviceUtils(d)
1176 for d in devices]) 1187 for d in devices])
OLDNEW
« 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