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

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: comment implementation of WriteFile 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, or an 69 device: Either a device serial, an existing AdbWrapper instance, or an
69 an existing AndroidCommands instance. 70 an existing AndroidCommands instance.
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 348 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 more lines. 420 more lines.
420 CommandTimeoutError on timeout. 421 CommandTimeoutError on timeout.
421 DeviceUnreachableError on missing device. 422 DeviceUnreachableError on missing device.
422 """ 423 """
423 def env_quote(key, value): 424 def env_quote(key, value):
424 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): 425 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
425 raise KeyError('Invalid shell variable name %r' % key) 426 raise KeyError('Invalid shell variable name %r' % key)
426 # using double quotes here to allow interpolation of shell variables 427 # using double quotes here to allow interpolation of shell variables
427 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) 428 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
428 429
430 def do_run(cmd):
431 try:
432 return self.adb.Shell(cmd)
433 except device_errors.AdbCommandFailedError as exc:
434 if check_return:
435 raise
436 else:
437 return exc.output
438
429 if not isinstance(cmd, basestring): 439 if not isinstance(cmd, basestring):
430 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 440 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
431 if env: 441 if env:
432 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) 442 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
433 cmd = '%s %s' % (env, cmd) 443 cmd = '%s %s' % (env, cmd)
434 if cwd: 444 if cwd:
435 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) 445 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
436 if as_root and self.NeedsSU(): 446 if as_root and self.NeedsSU():
437 # "su -c sh -c" allows using shell features in |cmd| 447 # "su -c sh -c" allows using shell features in |cmd|
438 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) 448 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
439 if timeout is None: 449 if timeout is None:
440 timeout = self._default_timeout 450 timeout = self._default_timeout
441 451
442 try: 452 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
443 output = self.adb.Shell(cmd) 453 output = do_run(cmd)
444 except device_errors.AdbCommandFailedError as e: 454 else:
445 if check_return: 455 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
446 raise 456 self._WriteFileWithPush(script.name, cmd)
447 else: 457 output = do_run('sh %s' % script.name_quoted)
448 output = e.output
449 458
450 output = output.splitlines() 459 output = output.splitlines()
451 if single_line: 460 if single_line:
452 if not output: 461 if not output:
453 return '' 462 return ''
454 elif len(output) == 1: 463 elif len(output) == 1:
455 return output[0] 464 return output[0]
456 else: 465 else:
457 msg = 'one line of output was expected, but got: %s' 466 msg = 'one line of output was expected, but got: %s'
458 raise device_errors.CommandFailedError(msg % output, str(self)) 467 raise device_errors.CommandFailedError(msg % output, str(self))
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after
884 # TODO(jbudorick) Evaluate whether we want to return a list of lines after 893 # TODO(jbudorick) Evaluate whether we want to return a list of lines after
885 # the implementation switch, and if file not found should raise exception. 894 # the implementation switch, and if file not found should raise exception.
886 if as_root: 895 if as_root:
887 if not self.old_interface.CanAccessProtectedFileContents(): 896 if not self.old_interface.CanAccessProtectedFileContents():
888 raise device_errors.CommandFailedError( 897 raise device_errors.CommandFailedError(
889 'Cannot read from %s with root privileges.' % device_path) 898 'Cannot read from %s with root privileges.' % device_path)
890 return self.old_interface.GetProtectedFileContents(device_path) 899 return self.old_interface.GetProtectedFileContents(device_path)
891 else: 900 else:
892 return self.old_interface.GetFileContents(device_path) 901 return self.old_interface.GetFileContents(device_path)
893 902
903 def _WriteFileWithPush(self, device_path, contents):
904 with tempfile.NamedTemporaryFile() as host_temp:
905 host_temp.write(contents)
906 host_temp.flush()
907 self.adb.Push(host_temp.name, device_path)
908
894 @decorators.WithTimeoutAndRetriesFromInstance() 909 @decorators.WithTimeoutAndRetriesFromInstance()
895 def WriteFile(self, device_path, contents, as_root=False, force_push=False, 910 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
896 timeout=None, retries=None): 911 timeout=None, retries=None):
897 """Writes |contents| to a file on the device. 912 """Writes |contents| to a file on the device.
898 913
899 Args: 914 Args:
900 device_path: A string containing the absolute path to the file to write 915 device_path: A string containing the absolute path to the file to write
901 on the device. 916 on the device.
902 contents: A string containing the data to write to the device. 917 contents: A string containing the data to write to the device.
903 as_root: A boolean indicating whether the write should be executed with 918 as_root: A boolean indicating whether the write should be executed with
904 root privileges (if available). 919 root privileges (if available).
905 force_push: A boolean indicating whether to force the operation to be 920 force_push: A boolean indicating whether to force the operation to be
906 performed by pushing a file to the device. The default is, when the 921 performed by pushing a file to the device. The default is, when the
907 contents are short, to pass the contents using a shell script instead. 922 contents are short, to pass the contents using a shell script instead.
908 timeout: timeout in seconds 923 timeout: timeout in seconds
909 retries: number of retries 924 retries: number of retries
910 925
911 Raises: 926 Raises:
912 CommandFailedError if the file could not be written on the device. 927 CommandFailedError if the file could not be written on the device.
913 CommandTimeoutError on timeout. 928 CommandTimeoutError on timeout.
914 DeviceUnreachableError on missing device. 929 DeviceUnreachableError on missing device.
915 """ 930 """
916 if len(contents) < 512 and not force_push: 931 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
932 # If the contents are small, for efficieny we write the contents using
933 # using a shell command rather than push
917 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), 934 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
918 cmd_helper.SingleQuote(device_path)) 935 cmd_helper.SingleQuote(device_path))
919 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 936 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
937 elif as_root and self.NeedsSU():
938 # Adb does not allow to "push with su", so we first push to a temp file
939 # on a safe location, and then copy it to the desired location with su
940 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
941 self._WriteFileWithPush(device_temp.name, contents)
942 # Here we need 'cp' rather than 'mv' because the temp and
943 # destination files might be on different file systems (e.g.
944 # on internal storage and an external sd card)
945 self.RunShellCommand(['cp', device_temp.name, device_path],
946 as_root=True, check_return=True)
920 else: 947 else:
921 with tempfile.NamedTemporaryFile() as host_temp: 948 # If root is not needed, we can push directly to the desired location
922 host_temp.write(contents) 949 self._WriteFileWithPush(device_path, contents)
923 host_temp.flush()
924 if as_root and self.NeedsSU():
925 with device_temp_file.DeviceTempFile(self) as device_temp:
926 self.adb.Push(host_temp.name, device_temp.name)
927 # Here we need 'cp' rather than 'mv' because the temp and
928 # destination files might be on different file systems (e.g.
929 # on internal storage and an external sd card)
930 self.RunShellCommand(['cp', device_temp.name, device_path],
931 as_root=True, check_return=True)
932 else:
933 self.adb.Push(host_temp.name, device_path)
934 950
935 @decorators.WithTimeoutAndRetriesFromInstance() 951 @decorators.WithTimeoutAndRetriesFromInstance()
936 def Ls(self, device_path, timeout=None, retries=None): 952 def Ls(self, device_path, timeout=None, retries=None):
937 """Lists the contents of a directory on the device. 953 """Lists the contents of a directory on the device.
938 954
939 Args: 955 Args:
940 device_path: A string containing the path of the directory on the device 956 device_path: A string containing the path of the directory on the device
941 to list. 957 to list.
942 timeout: timeout in seconds 958 timeout: timeout in seconds
943 retries: number of retries 959 retries: number of retries
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
1194 Returns: 1210 Returns:
1195 A Parallelizer operating over |devices|. 1211 A Parallelizer operating over |devices|.
1196 """ 1212 """
1197 if not devices or len(devices) == 0: 1213 if not devices or len(devices) == 0:
1198 devices = pylib.android_commands.GetAttachedDevices() 1214 devices = pylib.android_commands.GetAttachedDevices()
1199 parallelizer_type = (parallelizer.Parallelizer if async 1215 parallelizer_type = (parallelizer.Parallelizer if async
1200 else parallelizer.SyncParallelizer) 1216 else parallelizer.SyncParallelizer)
1201 return parallelizer_type([ 1217 return parallelizer_type([
1202 d if isinstance(d, DeviceUtils) else DeviceUtils(d) 1218 d if isinstance(d, DeviceUtils) else DeviceUtils(d)
1203 for d in devices]) 1219 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