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

Side by Side Diff: build/android/pylib/device/device_utils.py

Issue 787813002: Reland of Allow RunShellCommand to work even with very large commands (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: log large commands run, fix 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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 """Restarts the adb server. 67 """Restarts the adb server.
68 68
69 Raises: 69 Raises:
70 CommandFailedError if we fail to kill or restart the server. 70 CommandFailedError if we fail to kill or restart the server.
71 """ 71 """
72 pylib.android_commands.AndroidCommands().RestartAdbServer() 72 pylib.android_commands.AndroidCommands().RestartAdbServer()
73 73
74 74
75 class DeviceUtils(object): 75 class DeviceUtils(object):
76 76
77 _MAX_ADB_COMMAND_LENGTH = 512
77 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 78 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
78 79
79 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, 80 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
80 default_retries=_DEFAULT_RETRIES): 81 default_retries=_DEFAULT_RETRIES):
81 """DeviceUtils constructor. 82 """DeviceUtils constructor.
82 83
83 Args: 84 Args:
84 device: Either a device serial, an existing AdbWrapper instance, or an 85 device: Either a device serial, an existing AdbWrapper instance, or an
85 an existing AndroidCommands instance. 86 an existing AndroidCommands instance.
86 default_timeout: An integer containing the default number of seconds to 87 default_timeout: An integer containing the default number of seconds to
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 more lines. 429 more lines.
429 CommandTimeoutError on timeout. 430 CommandTimeoutError on timeout.
430 DeviceUnreachableError on missing device. 431 DeviceUnreachableError on missing device.
431 """ 432 """
432 def env_quote(key, value): 433 def env_quote(key, value):
433 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): 434 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
434 raise KeyError('Invalid shell variable name %r' % key) 435 raise KeyError('Invalid shell variable name %r' % key)
435 # using double quotes here to allow interpolation of shell variables 436 # using double quotes here to allow interpolation of shell variables
436 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) 437 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
437 438
439 def do_run(cmd):
440 try:
441 return self.adb.Shell(cmd)
442 except device_errors.AdbCommandFailedError as exc:
443 if check_return:
444 raise
445 else:
446 return exc.output
447
438 if not isinstance(cmd, basestring): 448 if not isinstance(cmd, basestring):
439 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 449 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
440 if env: 450 if env:
441 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) 451 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
442 cmd = '%s %s' % (env, cmd) 452 cmd = '%s %s' % (env, cmd)
443 if cwd: 453 if cwd:
444 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) 454 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
445 if as_root and self.NeedsSU(): 455 if as_root and self.NeedsSU():
446 # "su -c sh -c" allows using shell features in |cmd| 456 # "su -c sh -c" allows using shell features in |cmd|
447 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) 457 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
448 if timeout is None: 458 if timeout is None:
449 timeout = self._default_timeout 459 timeout = self._default_timeout
450 460
451 try: 461 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
452 output = self.adb.Shell(cmd) 462 output = do_run(cmd)
453 except device_errors.AdbCommandFailedError as e: 463 else:
454 if check_return: 464 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
455 raise 465 self._WriteFileWithPush(script.name, cmd)
456 else: 466 logging.info('Large shell command will be run from file: %s ...',
457 output = e.output 467 cmd[:100])
perezju 2014/12/15 15:48:59 I've noted that running large shell commands in th
jbudorick 2014/12/15 23:18:28 sgtm
468 output = do_run('sh %s' % script.name_quoted)
458 469
459 output = output.splitlines() 470 output = output.splitlines()
460 if single_line: 471 if single_line:
461 if not output: 472 if not output:
462 return '' 473 return ''
463 elif len(output) == 1: 474 elif len(output) == 1:
464 return output[0] 475 return output[0]
465 else: 476 else:
466 msg = 'one line of output was expected, but got: %s' 477 msg = 'one line of output was expected, but got: %s'
467 raise device_errors.CommandFailedError(msg % output, str(self)) 478 raise device_errors.CommandFailedError(msg % output, str(self))
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after
893 # TODO(jbudorick) Evaluate whether we want to return a list of lines after 904 # TODO(jbudorick) Evaluate whether we want to return a list of lines after
894 # the implementation switch, and if file not found should raise exception. 905 # the implementation switch, and if file not found should raise exception.
895 if as_root: 906 if as_root:
896 if not self.old_interface.CanAccessProtectedFileContents(): 907 if not self.old_interface.CanAccessProtectedFileContents():
897 raise device_errors.CommandFailedError( 908 raise device_errors.CommandFailedError(
898 'Cannot read from %s with root privileges.' % device_path) 909 'Cannot read from %s with root privileges.' % device_path)
899 return self.old_interface.GetProtectedFileContents(device_path) 910 return self.old_interface.GetProtectedFileContents(device_path)
900 else: 911 else:
901 return self.old_interface.GetFileContents(device_path) 912 return self.old_interface.GetFileContents(device_path)
902 913
914 def _WriteFileWithPush(self, device_path, contents):
915 with tempfile.NamedTemporaryFile() as host_temp:
916 host_temp.write(contents)
917 host_temp.flush()
918 self.adb.Push(host_temp.name, device_path)
919
903 @decorators.WithTimeoutAndRetriesFromInstance() 920 @decorators.WithTimeoutAndRetriesFromInstance()
904 def WriteFile(self, device_path, contents, as_root=False, force_push=False, 921 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
905 timeout=None, retries=None): 922 timeout=None, retries=None):
906 """Writes |contents| to a file on the device. 923 """Writes |contents| to a file on the device.
907 924
908 Args: 925 Args:
909 device_path: A string containing the absolute path to the file to write 926 device_path: A string containing the absolute path to the file to write
910 on the device. 927 on the device.
911 contents: A string containing the data to write to the device. 928 contents: A string containing the data to write to the device.
912 as_root: A boolean indicating whether the write should be executed with 929 as_root: A boolean indicating whether the write should be executed with
913 root privileges (if available). 930 root privileges (if available).
914 force_push: A boolean indicating whether to force the operation to be 931 force_push: A boolean indicating whether to force the operation to be
915 performed by pushing a file to the device. The default is, when the 932 performed by pushing a file to the device. The default is, when the
916 contents are short, to pass the contents using a shell script instead. 933 contents are short, to pass the contents using a shell script instead.
917 timeout: timeout in seconds 934 timeout: timeout in seconds
918 retries: number of retries 935 retries: number of retries
919 936
920 Raises: 937 Raises:
921 CommandFailedError if the file could not be written on the device. 938 CommandFailedError if the file could not be written on the device.
922 CommandTimeoutError on timeout. 939 CommandTimeoutError on timeout.
923 DeviceUnreachableError on missing device. 940 DeviceUnreachableError on missing device.
924 """ 941 """
925 if len(contents) < 512 and not force_push: 942 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
943 # If the contents are small, for efficieny we write the contents with
944 # a shell command rather than pushing a file.
926 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), 945 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
927 cmd_helper.SingleQuote(device_path)) 946 cmd_helper.SingleQuote(device_path))
928 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 947 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
948 elif as_root and self.NeedsSU():
949 # Adb does not allow to "push with su", so we first push to a temp file
950 # on a safe location, and then copy it to the desired location with su.
951 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
952 self._WriteFileWithPush(device_temp.name, contents)
953 # Here we need 'cp' rather than 'mv' because the temp and
954 # destination files might be on different file systems (e.g.
955 # on internal storage and an external sd card).
956 self.RunShellCommand(['cp', device_temp.name, device_path],
957 as_root=True, check_return=True)
929 else: 958 else:
930 with tempfile.NamedTemporaryFile() as host_temp: 959 # If root is not needed, we can push directly to the desired location.
931 host_temp.write(contents) 960 self._WriteFileWithPush(device_path, contents)
932 host_temp.flush()
933 if as_root and self.NeedsSU():
934 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
935 self.adb.Push(host_temp.name, device_temp.name)
936 # Here we need 'cp' rather than 'mv' because the temp and
937 # destination files might be on different file systems (e.g.
938 # on internal storage and an external sd card)
939 self.RunShellCommand(['cp', device_temp.name, device_path],
940 as_root=True, check_return=True)
941 else:
942 self.adb.Push(host_temp.name, device_path)
943 961
944 @decorators.WithTimeoutAndRetriesFromInstance() 962 @decorators.WithTimeoutAndRetriesFromInstance()
945 def Ls(self, device_path, timeout=None, retries=None): 963 def Ls(self, device_path, timeout=None, retries=None):
946 """Lists the contents of a directory on the device. 964 """Lists the contents of a directory on the device.
947 965
948 Args: 966 Args:
949 device_path: A string containing the path of the directory on the device 967 device_path: A string containing the path of the directory on the device
950 to list. 968 to list.
951 timeout: timeout in seconds 969 timeout: timeout in seconds
952 retries: number of retries 970 retries: number of retries
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after
1269 Returns: 1287 Returns:
1270 A Parallelizer operating over |devices|. 1288 A Parallelizer operating over |devices|.
1271 """ 1289 """
1272 if not devices: 1290 if not devices:
1273 devices = adb_wrapper.AdbWrapper.GetDevices() 1291 devices = adb_wrapper.AdbWrapper.GetDevices()
1274 devices = [d if isinstance(d, cls) else cls(d) for d in devices] 1292 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1275 if async: 1293 if async:
1276 return parallelizer.Parallelizer(devices) 1294 return parallelizer.Parallelizer(devices)
1277 else: 1295 else:
1278 return parallelizer.SyncParallelizer(devices) 1296 return parallelizer.SyncParallelizer(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