in apple/internal/templates/ios_sim.template.py [0:0]
def discover_best_compatible_simulator(simctl_path, minimum_os, sim_device,
sim_os_version):
"""Discovers the best compatible simulator device type and device.
Args:
simctl_path: The path to the `simctl` binary.
minimum_os: The minimum OS version required by the ios_application() target.
sim_device: Optional name of the device (e.g. "iPhone 8 Plus").
sim_os_version: Optional version of the iOS runtime (e.g. "13.2").
Returns:
A tuple (device_type, device) containing the DeviceType and Device
of the best compatible simulator (might be None if no match was found).
Raises:
subprocess.SubprocessError: if `simctl list` fails or times out.
"""
# The `simctl list` CLI provides only very basic case-insensitive description
# matching search term functionality.
#
# This code needs to enforce a numeric floor on `minimum_os`, so it directly
# parses the JSON output by `simctl list` instead of repeatedly invoking
# `simctl list` with search terms.
cmd = [simctl_path, "list", "-j"]
with subprocess.Popen(cmd, stdout=subprocess.PIPE) as process:
simctl_data = json.load(process.stdout)
if process.wait() != os.EX_OK:
raise subprocess.CalledProcessError(process.returncode, cmd)
compatible_device_types = []
minimum_runtime_version = minimum_os_to_simctl_runtime_version(minimum_os)
# Prepare the device name for case-insensitive matching.
sim_device = sim_device and sim_device.casefold()
# `simctl list` orders device types from oldest to newest. Remember
# the index of each device type to preserve that ordering when
# sorting device types.
for (simctl_list_index, device_type) in enumerate(simctl_data["devicetypes"]):
device_type = DeviceType(device_type, simctl_list_index)
if not (device_type.is_iphone() or device_type.is_ipad()):
continue
# Some older simulators are missing `maxRuntimeVersion`. Assume those
# simulators support all OSes (even though it's not true).
max_runtime_version = device_type.get("maxRuntimeVersion")
if max_runtime_version and max_runtime_version < minimum_runtime_version:
continue
if sim_device and device_type["name"].casefold().find(sim_device) == -1:
continue
compatible_device_types.append(device_type)
compatible_device_types.sort()
logger.debug("Found %d compatible device types.",
len(compatible_device_types))
compatible_runtime_identifiers = set()
for runtime in simctl_data["runtimes"]:
if not runtime["isAvailable"]:
continue
if sim_os_version and runtime["version"] != sim_os_version:
continue
compatible_runtime_identifiers.add(runtime["identifier"])
compatible_devices = []
for runtime_identifier, devices in simctl_data["devices"].items():
if runtime_identifier not in compatible_runtime_identifiers:
continue
for device in devices:
if not device["isAvailable"]:
continue
compatible_device = None
for device_type in compatible_device_types:
if device["deviceTypeIdentifier"] == device_type["identifier"]:
compatible_device = Device(device, device_type)
break
if not compatible_device:
continue
compatible_devices.append(compatible_device)
compatible_devices.sort()
logger.debug("Found %d compatible devices.", len(compatible_devices))
if compatible_device_types:
best_compatible_device_type = compatible_device_types[-1]
else:
best_compatible_device_type = None
if compatible_devices:
best_compatible_device = compatible_devices[-1]
else:
best_compatible_device = None
return (best_compatible_device_type, best_compatible_device)