From 3fd43b5bf4850f187989d944ac39cd6b6fb5ed59 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Wed, 28 Oct 2015 11:56:13 -0700 Subject: [PATCH] Allow selection of adb command. This is useful for ndk-gdb.py on Windows. Change-Id: I2a2b6b50ae00bcfc530c555f06c945b4f53f9999 --- python-packages/adb/device.py | 47 +++++++++++------------ python-packages/gdbrunner/__init__.py | 54 +++++++++++++++------------ 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/python-packages/adb/device.py b/python-packages/adb/device.py index ceb263d69..fad920b3d 100644 --- a/python-packages/adb/device.py +++ b/python-packages/adb/device.py @@ -48,11 +48,11 @@ class ShellError(RuntimeError): self.exit_code = exit_code -def get_devices(): +def get_devices(adb_path='adb'): with open(os.devnull, 'wb') as devnull: - subprocess.check_call(['adb', 'start-server'], stdout=devnull, + subprocess.check_call([adb_path, 'start-server'], stdout=devnull, stderr=devnull) - out = subprocess.check_output(['adb', 'devices']).splitlines() + out = subprocess.check_output([adb_path, 'devices']).splitlines() # The first line of `adb devices` just says "List of attached devices", so # skip that. @@ -68,21 +68,21 @@ def get_devices(): return devices -def _get_unique_device(product=None): - devices = get_devices() +def _get_unique_device(product=None, adb_path='adb'): + devices = get_devices(adb_path=adb_path) if len(devices) != 1: raise NoUniqueDeviceError() - return AndroidDevice(devices[0], product) + return AndroidDevice(devices[0], product, adb_path) -def _get_device_by_serial(serial, product=None): - for device in get_devices(): +def _get_device_by_serial(serial, product=None, adb_path='adb'): + for device in get_devices(adb_path=adb_path): if device == serial: - return AndroidDevice(serial, product) + return AndroidDevice(serial, product, adb_path) raise DeviceNotFoundError(serial) -def get_device(serial=None, product=None): +def get_device(serial=None, product=None, adb_path='adb'): """Get a uniquely identified AndroidDevice if one is available. Raises: @@ -104,29 +104,29 @@ def get_device(serial=None, product=None): 3) The single device connnected to the system. """ if serial is not None: - return _get_device_by_serial(serial, product) + return _get_device_by_serial(serial, product, adb_path) android_serial = os.getenv('ANDROID_SERIAL') if android_serial is not None: - return _get_device_by_serial(android_serial, product) + return _get_device_by_serial(android_serial, product, adb_path) - return _get_unique_device(product) + return _get_unique_device(product, adb_path=adb_path) -def _get_device_by_type(flag): +def _get_device_by_type(flag, adb_path): with open(os.devnull, 'wb') as devnull: - subprocess.check_call(['adb', 'start-server'], stdout=devnull, + subprocess.check_call([adb_path, 'start-server'], stdout=devnull, stderr=devnull) try: - serial = subprocess.check_output(['adb', flag, 'get-serialno']).strip() + serial = subprocess.check_output([adb_path, flag, 'get-serialno']).strip() except subprocess.CalledProcessError: raise RuntimeError('adb unexpectedly returned nonzero') if serial == 'unknown': raise NoUniqueDeviceError() - return _get_device_by_serial(serial) + return _get_device_by_serial(serial, adb_path=adb_path) -def get_usb_device(): +def get_usb_device(adb_path='adb'): """Get the unique USB-connected AndroidDevice if it is available. Raises: @@ -136,10 +136,10 @@ def get_usb_device(): Returns: An AndroidDevice associated with the unique USB-connected device. """ - return _get_device_by_type('-d') + return _get_device_by_type('-d', adb_path=adb_path) -def get_emulator_device(): +def get_emulator_device(adb_path='adb'): """Get the unique emulator AndroidDevice if it is available. Raises: @@ -149,7 +149,7 @@ def get_emulator_device(): Returns: An AndroidDevice associated with the unique running emulator. """ - return _get_device_by_type('-e') + return _get_device_by_type('-e', adb_path=adb_path) @contextlib.contextmanager @@ -248,10 +248,11 @@ class AndroidDevice(object): # Feature name strings. SHELL_PROTOCOL_FEATURE = 'shell_v2' - def __init__(self, serial, product=None): + def __init__(self, serial, product=None, adb_path='adb'): self.serial = serial self.product = product - self.adb_cmd = ['adb'] + self.adb_cmd = [adb_path] + if self.serial is not None: self.adb_cmd.extend(['-s', serial]) if self.product is not None: diff --git a/python-packages/gdbrunner/__init__.py b/python-packages/gdbrunner/__init__.py index 379409e4e..b01ef6b91 100644 --- a/python-packages/gdbrunner/__init__.py +++ b/python-packages/gdbrunner/__init__.py @@ -21,49 +21,57 @@ import argparse import atexit import os import subprocess +import sys import tempfile class ArgumentParser(argparse.ArgumentParser): """ArgumentParser subclass that provides adb device selection.""" - class DeviceAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - if option_string is None: - raise RuntimeError("DeviceAction called without option_string") - elif option_string == "-a": - # Handled in parse_args - return - elif option_string == "-d": - namespace.device = adb.get_usb_device() - elif option_string == "-e": - namespace.device = adb.get_emulator_device() - elif option_string == "-s": - namespace.device = adb.get_device(values[0]) - else: - raise RuntimeError("Unexpected flag {}".format(option_string)) - def __init__(self): super(ArgumentParser, self).__init__() + self.add_argument( + "--adb", dest="adb_path", + help="Use specific adb command") + group = self.add_argument_group(title="device selection") group = group.add_mutually_exclusive_group() group.add_argument( - "-a", nargs=0, action=self.DeviceAction, + "-a", action="store_const", dest="device", const="-a", help="directs commands to all interfaces") group.add_argument( - "-d", nargs=0, action=self.DeviceAction, + "-d", action="store_const", dest="device", const="-d", help="directs commands to the only connected USB device") group.add_argument( - "-e", nargs=0, action=self.DeviceAction, + "-e", action="store_const", dest="device", const="-e", help="directs commands to the only connected emulator") group.add_argument( - "-s", nargs=1, metavar="SERIAL", action=self.DeviceAction, + "-s", metavar="SERIAL", action="store", dest="serial", help="directs commands to device/emulator with the given serial") def parse_args(self, args=None, namespace=None): result = super(ArgumentParser, self).parse_args(args, namespace) - # Default to -a behavior if no flags are given. - if "device" not in result: - result.device = adb.get_device() + + adb_path = result.adb_path or "adb" + + # Try to run the specified adb command + try: + subprocess.check_output([adb_path, "version"], + stderr=subprocess.STDOUT) + except (OSError, subprocess.CalledProcessError): + msg = "ERROR: Unable to run adb executable (tried '{}')." + if not result.adb_path: + msg += "\n Try specifying its location with --adb." + sys.exit(msg.format(adb_path)) + + if result.device == "-a": + result.device = adb.get_device(adb_path=adb_path) + elif result.device == "-d": + result.device = adb.get_usb_device(adb_path=adb_path) + elif result.device == "-e": + result.device = adb.get_emulator_device(adb_path=adb_path) + else: + result.device = adb.get_device(result.serial, adb_path=adb_path) + return result