blob: 7c8e6c99678327cf1c213cea6325fcbb71c7e1d6 [file] [log] [blame]
#!/usr/bin/env vpython3
#
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A script to run a command on all available emulators."""
import argparse
import logging
import os
import re
import subprocess
import sys
import time
_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.utils import logging_common
sys.path.append(os.path.join(_SRC_ROOT, 'tools', 'android', 'avd'))
from avd import get_avd_configs
def get_emulators(all_emulators):
"""Gets the list of emulators, optionally filtering by API level."""
emulators = []
for config in get_avd_configs():
if not all_emulators and not config.IsAvailable():
continue
path = config.avd_proto_path
basename = os.path.basename(path)
api_level = 0
# Try to extract API level like android_31_...
match = re.search(r'android_(\d+)', basename)
if match:
api_level = int(match.group(1))
else:
# Try to extract API level like generic_android27.textpb
match = re.search(r'generic_android(\d+)', basename)
if match:
api_level = int(match.group(1))
# If we found an API level and it's 28 or less, skip it.
if api_level and api_level <= 28:
logging.info('Skipping emulator %s due to old API level (%d).', path,
api_level)
continue
emulators.append(path)
return emulators
def wait_for_emulator(serial):
"""Waits for the emulator to be online."""
logging.info('Waiting for emulator %s to be online...', serial)
for _ in range(60):
try:
output = subprocess.check_output(['adb', 'devices'], encoding='utf-8')
if re.search(r'^%s\s+device$' % re.escape(serial), output, re.MULTILINE):
logging.info('Emulator %s is online.', serial)
return True
except subprocess.CalledProcessError:
pass
time.sleep(1)
logging.error('Emulator %s did not come online.', serial)
return False
def main():
"""Starts each emulator, runs a command, and stops it."""
parser = argparse.ArgumentParser(
description='Run a command on all available emulators.')
parser.add_argument('--cmd',
required=True,
help='The command to run on the emulator.')
parser.add_argument('--all',
action='store_true',
help='Run on all emulators, not just installed ones.')
parser.add_argument('--output-file', help='File to write the output to.')
logging_common.AddLoggingArguments(parser)
args = parser.parse_args()
logging_common.InitializeLogging(args)
if args.output_file:
output_dir = os.path.dirname(args.output_file)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Clear the file if it exists
open(args.output_file, 'w').close()
emulators = get_emulators(args.all)
if not emulators:
logging.warning('No available emulators found.')
return
for emulator_config in emulators:
logging.info('Starting emulator: %s', emulator_config)
try:
start_process = subprocess.Popen([
'tools/android/avd/avd.py', 'start', '--avd-config', emulator_config
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding='utf-8')
stdout, stderr = start_process.communicate()
if start_process.returncode != 0:
logging.error('Failed to start emulator %s: %s', emulator_config,
stderr)
continue
# The serial number is expected to be in the output of the start command.
match = re.search(r'emulator-\d+', stdout)
if not match:
logging.error('Could not determine serial for emulator %s',
emulator_config)
continue
serial = match.group(0)
if wait_for_emulator(serial):
logging.info('Running command on %s: %s', serial, args.cmd)
try:
cmd_output = subprocess.check_output(args.cmd,
shell=True,
encoding='utf-8')
if args.output_file:
with open(args.output_file, 'a') as f:
f.write(f'{emulator_config}: {cmd_output.strip()}\n')
else:
print(f'Output for {emulator_config}:\n{cmd_output}')
except subprocess.CalledProcessError as e:
logging.error('Command failed on %s: %s', emulator_config, e)
logging.info('Stopping emulator: %s', emulator_config)
subprocess.run(
['tools/android/avd/avd.py', 'stop', '--avd-config', emulator_config],
check=False)
except Exception as e:
logging.error('An error occurred with emulator %s: %s', emulator_config,
e)
# Ensure the emulator is stopped even if an error occurs.
subprocess.run(
['tools/android/avd/avd.py', 'stop', '--avd-config', emulator_config],
check=False)
if __name__ == '__main__':
main()