| #! /usr/bin/env vpython3 |
| # Copyright 2019 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import os |
| import logging |
| import json |
| import pathlib |
| import sys |
| |
| _SRC_ROOT = os.path.abspath( |
| os.path.join(os.path.dirname(__file__), '..', '..', '..')) |
| |
| sys.path.append( |
| os.path.join(_SRC_ROOT, 'third_party', 'catapult', 'devil')) |
| from devil.android.tools import script_common |
| from devil.utils import logging_common |
| |
| sys.path.append( |
| os.path.join(_SRC_ROOT, 'build', 'android')) |
| import devil_chromium |
| from pylib.local.emulator import avd |
| |
| |
| def _add_avd_config_argument(parser, required=True): |
| parser.add_argument('--avd-config', |
| type=os.path.realpath, |
| metavar='PATH', |
| required=required, |
| help='Path to an AVD config text protobuf.') |
| |
| |
| def _add_common_arguments(parser): |
| logging_common.AddLoggingArguments(parser) |
| script_common.AddEnvironmentArguments(parser) |
| |
| |
| def main(raw_args): |
| |
| parser = argparse.ArgumentParser() |
| |
| subparsers = parser.add_subparsers() |
| subparser = subparsers.add_parser( |
| 'install', |
| help='Install the CIPD packages specified in the given config.') |
| _add_common_arguments(subparser) |
| _add_avd_config_argument(subparser) |
| |
| def install_cmd(args): |
| avd.AvdConfig(args.avd_config).Install() |
| return 0 |
| |
| subparser.set_defaults(func=install_cmd) |
| |
| subparser = subparsers.add_parser( |
| 'uninstall', |
| help='Uninstall all the artifacts associated with the given config.') |
| _add_common_arguments(subparser) |
| _add_avd_config_argument(subparser) |
| |
| def uninstall_cmd(args): |
| avd.AvdConfig(args.avd_config).Uninstall() |
| return 0 |
| |
| subparser.set_defaults(func=uninstall_cmd) |
| |
| subparser = subparsers.add_parser( |
| 'create', |
| help='Create an AVD CIPD package according to the given config.') |
| _add_common_arguments(subparser) |
| _add_avd_config_argument(subparser) |
| subparser.add_argument( |
| '--avd-variant', |
| help='The name of the AVD variant to use during creation. Will error out ' |
| 'if the name is set but avd config has no variants or the name is not ' |
| 'found in the avd config.') |
| subparser.add_argument( |
| '--snapshot', |
| action='store_true', |
| help='Snapshot the AVD before creating the CIPD package.') |
| subparser.add_argument( |
| '--force', |
| action='store_true', |
| help='Pass --force to AVD creation. This is useful when an AVD with ' |
| 'the same name already exists.') |
| subparser.add_argument('--keep', |
| action='store_true', |
| help='Keep the AVD after creating the CIPD package.') |
| subparser.add_argument( |
| '--privileged-apk', |
| action='append', |
| default=[], |
| dest='privileged_apk_pairs', |
| nargs=2, |
| metavar=('APK_PATH', 'DEVICE_PARTITION'), |
| help='Privileged apks to be installed during AVD launching. Expecting ' |
| 'two strings where the first element being the path to the APK, and the ' |
| 'second element being the system image partition on device where the APK ' |
| 'will be pushed to. Example: --privileged-apk path/to/some.apk /system') |
| subparser.add_argument( |
| '--additional-apk', |
| action='append', |
| default=[], |
| dest='additional_apks', |
| metavar='APK_PATH', |
| type=os.path.realpath, |
| help='Additional apk to be installed during AVD launching') |
| subparser.add_argument( |
| '--cipd-json-output', |
| type=os.path.realpath, |
| metavar='PATH', |
| help='Path to which `cipd create` should dump json output ' |
| 'via -json-output.') |
| subparser.add_argument( |
| '--dry-run', |
| action='store_true', |
| help='Skip the CIPD package creation after creating the AVD.') |
| |
| def create_cmd(args): |
| avd_config = avd.AvdConfig(args.avd_config) |
| avd_config.Create( |
| avd_variant_name=args.avd_variant, |
| force=args.force, |
| snapshot=args.snapshot, |
| keep=args.keep, |
| additional_apks=args.additional_apks, |
| privileged_apk_tuples=[tuple(p) for p in args.privileged_apk_pairs], |
| cipd_json_output=args.cipd_json_output, |
| dry_run=args.dry_run) |
| return 0 |
| |
| subparser.set_defaults(func=create_cmd) |
| |
| subparser = subparsers.add_parser( |
| 'start', |
| help='Start an AVD instance with the given config.', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| _add_common_arguments(subparser) |
| _add_avd_config_argument(subparser) |
| subparser.add_argument( |
| '--wipe-data', |
| action='store_true', |
| default=False, |
| help='Reset user data image for this emulator. Note that when set, all ' |
| 'the customization, e.g. wifi, additional apks, privileged apks will be ' |
| 'gone') |
| subparser.add_argument( |
| '--read-only', |
| action='store_true', |
| help='Allowing running multiple instances of emulators on the same AVD, ' |
| 'but cannot save snapshot. This will be forced to False if emulator ' |
| 'has a system snapshot.') |
| subparser.add_argument('--no-read-only', |
| action='store_false', |
| dest='read_only') |
| # TODO(crbug.com/40208043): Default to False when AVDs with sideloaded |
| # system apks are rolled. |
| subparser.set_defaults(read_only=True) |
| subparser.add_argument( |
| '--writable-system', |
| action='store_true', |
| default=False, |
| help='Makes system & vendor image writable after adb remount. This will ' |
| 'be forced to True, if emulator has a system snapshot.') |
| subparser.add_argument( |
| '--emulator-window', |
| action='store_true', |
| default=False, |
| help='Enable graphical window display on the emulator.') |
| subparser.add_argument( |
| '--gpu-mode', |
| help='Override the mode of hardware OpenGL ES emulation indicated by the ' |
| 'AVD. See "emulator -help-gpu" for a full list of modes. Note when set ' |
| 'to "host", it needs a valid DISPLAY env, even if "--emulator-window" is ' |
| 'false, and it will not work under remote sessions like chrome remote ' |
| 'desktop.') |
| subparser.add_argument( |
| '--debug-tags', |
| help='Comma-separated list of debug tags. This can be used to enable or ' |
| 'disable debug messages from specific parts of the emulator, e.g. ' |
| 'init,snapshot. See "emulator -help-debug-tags" ' |
| 'for a full list of tags.') |
| subparser.add_argument( |
| '--disk-size', |
| help='Override the default disk size for the emulator instance.') |
| subparser.add_argument( |
| '--enable-network', |
| action='store_true', |
| help='Enable the network (WiFi and mobile data) on the emulator.') |
| subparser.add_argument( |
| '--require-fast-start', |
| action='store_true', |
| help='Deprecated and will be removed soon. Please use ' |
| '"proto/*_local.textpb" avd config files for local development.') |
| |
| def start_cmd(args): |
| avd_config = avd.AvdConfig(args.avd_config) |
| if not avd_config.IsAvailable(): |
| logging.warning('Emulator not up-to-date, installing (takes a minute)...') |
| avd_config.Install() |
| logging.warning('Starting emulator...') |
| |
| debug_tags = args.debug_tags |
| if not debug_tags and args.verbose: |
| debug_tags = 'time,init' |
| |
| inst = avd_config.CreateInstance() |
| inst.Start(read_only=args.read_only, |
| window=args.emulator_window, |
| writable_system=args.writable_system, |
| gpu_mode=args.gpu_mode, |
| wipe_data=args.wipe_data, |
| debug_tags=debug_tags, |
| disk_size=args.disk_size, |
| enable_network=args.enable_network) |
| print('%s started (pid: %d)' % (str(inst), inst._emulator_proc.pid)) |
| return 0 |
| |
| subparser.set_defaults(func=start_cmd) |
| |
| subparser = subparsers.add_parser( |
| 'list', |
| help='Shows possible values for --avd-config.', |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| _add_common_arguments(subparser) |
| _add_avd_config_argument(subparser, required=False) |
| subparser.add_argument( |
| '--avd-config-dir', |
| type=os.path.realpath, |
| metavar='DIR_PATH', |
| help='Path to the dir that contains the avd config files. ' |
| 'Default to the sibling dir "proto" of this "avd.py" script, if neither ' |
| '"--avd-config-path" nor this argument is set.') |
| subparser.add_argument( |
| '--json-output', |
| type=os.path.realpath, |
| metavar='PATH', |
| help='Dump json output to the given path.') |
| |
| def list_cmd(args): |
| files = set() |
| if args.avd_config: |
| files.add(args.avd_config) |
| if args.avd_config_dir: |
| files.update(pathlib.Path(args.avd_config_dir).glob('*.textpb')) |
| if not args.avd_config and not args.avd_config_dir: |
| files.update(pathlib.Path(__file__).parent.glob('proto/*.textpb')) |
| |
| if not files: |
| print('No avd config files found.') |
| return 0 |
| |
| avd_configs = [avd.AvdConfig(os.path.relpath(f)) for f in sorted(files)] |
| metadata = [config.GetMetadata() for config in avd_configs] |
| if args.json_output: |
| with open(args.json_output, 'w') as json_file: |
| json.dump(metadata, json_file, indent=2) |
| else: |
| # Import tabulate only when needed, in case it is not listed in .vpython3. |
| tabulate = __import__('tabulate') |
| print(tabulate.tabulate(metadata, headers='keys')) |
| return 0 |
| |
| subparser.set_defaults(func=list_cmd) |
| |
| if len(sys.argv) == 1: |
| parser.print_help() |
| return 1 |
| |
| args = parser.parse_args(raw_args) |
| |
| logging_common.InitializeLogging(args) |
| devil_chromium.Initialize(adb_path=args.adb_path) |
| return args.func(args) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |