• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Provide helpers for running Fuchsia's `ffx emu`."""
5
6import argparse
7import logging
8import os
9import random
10
11from contextlib import AbstractContextManager
12
13from common import run_ffx_command, IMAGES_ROOT, INTERNAL_IMAGES_ROOT, \
14                   DIR_SRC_ROOT
15from compatible_utils import get_host_arch
16
17
18class FfxEmulator(AbstractContextManager):
19    """A helper for managing emulators."""
20    # pylint: disable=too-many-branches
21    def __init__(self, args: argparse.Namespace) -> None:
22        if args.product:
23            self._product = args.product
24        else:
25            if get_host_arch() == 'x64':
26                self._product = 'terminal.x64'
27            else:
28                self._product = 'terminal.qemu-arm64'
29
30        self._enable_graphics = args.enable_graphics
31        self._hardware_gpu = args.hardware_gpu
32        self._logs_dir = args.logs_dir
33        self._with_network = args.with_network
34        if args.everlasting:
35            # Do not change the name, it will break the logic.
36            # ffx has a prefix-matching logic, so 'fuchsia-emulator' is not
37            # usable to avoid breaking local development workflow. I.e.
38            # developers can create an everlasting emulator and an ephemeral one
39            # without interfering each other.
40            self._node_name = 'fuchsia-everlasting-emulator'
41            assert self._everlasting()
42        else:
43            self._node_name = 'fuchsia-emulator-' + str(random.randint(
44                1, 9999))
45        self._device_spec = args.device_spec
46
47    def _everlasting(self) -> bool:
48        return self._node_name == 'fuchsia-everlasting-emulator'
49
50    def __enter__(self) -> str:
51        """Start the emulator.
52
53        Returns:
54            The node name of the emulator.
55        """
56        logging.info('Starting emulator %s', self._node_name)
57        prod, board = self._product.split('.', 1)
58        image_dir = os.path.join(IMAGES_ROOT, prod, board)
59        if not os.path.isdir(image_dir):
60            image_dir = os.path.join(INTERNAL_IMAGES_ROOT, prod, board)
61        emu_command = ['emu', 'start', image_dir, '--name', self._node_name]
62        configs = ['emu.start.timeout=300']
63        if not self._enable_graphics:
64            emu_command.append('-H')
65        if self._hardware_gpu:
66            emu_command.append('--gpu')
67        if self._logs_dir:
68            emu_command.extend(
69                ('-l', os.path.join(self._logs_dir, 'emulator_log')))
70        if self._with_network:
71            emu_command.extend(['--net', 'tap'])
72        else:
73            emu_command.extend(['--net', 'user'])
74        if self._everlasting():
75            emu_command.extend(['--reuse-with-check'])
76        if self._device_spec:
77            emu_command.extend(['--device', self._device_spec])
78
79        # fuchsia-sdk does not carry arm64 qemu binaries, so use overrides to
80        # allow it using the qemu-arm64 being downloaded separately.
81        if get_host_arch() == 'arm64':
82            configs.append(
83                'sdk.overrides.qemu_internal=' +
84                os.path.join(DIR_SRC_ROOT, 'third_party', 'qemu-linux-arm64',
85                             'bin', 'qemu-system-aarch64'))
86
87        # Always use qemu for arm64 images, no matter it runs on arm64 hosts or
88        # x64 hosts with simulation.
89        if self._product.endswith('arm64'):
90            emu_command.extend(['--engine', 'qemu'])
91
92        run_ffx_command(cmd=emu_command, timeout=310, configs=configs)
93
94        return self._node_name
95
96    def __exit__(self, exc_type, exc_value, traceback) -> bool:
97        """Shutdown the emulator."""
98
99        logging.info('Stopping the emulator %s', self._node_name)
100        cmd = ['emu', 'stop', self._node_name]
101        if self._everlasting():
102            cmd.extend(['--persist'])
103        # The emulator might have shut down unexpectedly, so this command
104        # might fail.
105        run_ffx_command(cmd=cmd, check=False)
106        # Do not suppress exceptions.
107        return False
108