/* * Copyright (C) 2013 RoboVM AB * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. */ package org.robovm.compiler.target.ios; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.robovm.compiler.config.Arch; import org.robovm.compiler.log.Logger; import org.robovm.compiler.util.Executor; /** * Simulator device types, consisting of the device type id and SDK version as * listed by ios-sim. * * @author badlogic * */ public class DeviceType implements Comparable<DeviceType> { public static final String PREFIX = "com.apple.CoreSimulator.SimDeviceType."; public static final String PREFERRED_IPHONE_SIM_NAME = PREFIX + "iPhone-6"; public static final String PREFERRED_IPAD_SIM_NAME = PREFIX + "iPad-Air"; public static enum DeviceFamily { iPhone, iPad } private final String deviceName; private final SDK sdk; private final Set<Arch> archs; DeviceType(String deviceName, SDK sdk, Set<Arch> archs) { this.deviceName = deviceName; this.sdk = sdk; this.archs = archs; } public String getDeviceName() { return deviceName; } public SDK getSdk() { return sdk; } public Set<Arch> getArchs() { return Collections.unmodifiableSet(archs); } /** * @return id as understood by ios-sim, concatentation of type and sdk * version */ public String getDeviceTypeId() { return deviceName + ", " + sdk.getVersion(); } /** * @return id as understood by the AppCompiler -simdevicetype flag */ public String getSimpleDeviceTypeId() { return getSimpleDeviceName() + ", " + sdk.getVersion(); } public String getSimpleDeviceName() { return deviceName.substring(PREFIX.length()); } public DeviceFamily getFamily() { if (deviceName.contains("iPhone")) { return DeviceFamily.iPhone; } else { return DeviceFamily.iPad; } } public static List<DeviceType> listDeviceTypes() { try { String capture = new Executor(Logger.NULL_LOGGER, IOSTarget.getIosSimPath()).args( "showdevicetypes").execCapture(); List<DeviceType> types = new ArrayList<DeviceType>(); String[] deviceTypeIds = capture.split("\n"); List<SDK> sdks = SDK.listSimulatorSDKs(); Map<String, SDK> sdkMap = new HashMap<>(); for (SDK sdk : sdks) { sdkMap.put(sdk.getVersion(), sdk); } for (String deviceTypeId : deviceTypeIds) { String[] tokens = deviceTypeId.split(","); tokens[0] = tokens[0].trim(); tokens[1] = tokens[1].trim(); tokens[2] = tokens[2].trim(); SDK sdk = sdkMap.get(tokens[1]); if (sdk != null) { Set<Arch> archs = new HashSet<>(); for (String s : tokens[2].replaceAll("[\\(\\)]", "").split(" ")) { switch (s) { case "i386": archs.add(Arch.x86); break; case "x86_64": archs.add(Arch.x86_64); break; } } types.add(new DeviceType(tokens[0], sdk, archs)); } } // Sort. Make sure that devices that have an id which is a prefix of // another id comes before in the list. Collections.sort(types); return types; } catch (IOException e) { throw new RuntimeException(e); } } @Override public int compareTo(DeviceType that) { int c = this.sdk.compareTo(that.sdk); if (c == 0) { c = this.getFamily().compareTo(that.getFamily()); if (c == 0) { c = this.deviceName.compareToIgnoreCase(that.deviceName); } } return c; } private static List<DeviceType> filter(List<DeviceType> deviceTypes, Arch arch, DeviceFamily family, String deviceName, String sdkVersion) { deviceName = deviceName == null ? null : deviceName.toLowerCase(); List<DeviceType> result = new ArrayList<>(); for (DeviceType type : deviceTypes) { if (arch == null || type.getArchs().contains(arch)) { if (family == null || family == type.getFamily()) { if (deviceName == null || type.getSimpleDeviceName().toLowerCase().contains(deviceName)) { if (sdkVersion == null || type.getSdk().getVersion().equals(sdkVersion)) { result.add(type); } } } } } return result; } public static List<String> getSimpleDeviceTypeIds() { List<String> result = new ArrayList<>(); for (DeviceType type : listDeviceTypes()) { result.add(type.getSimpleDeviceTypeId()); } return result; } public static DeviceType getDeviceType(String deviceTypeId) { List<DeviceType> types = listDeviceTypes(); if (deviceTypeId == null) { return null; } if (!deviceTypeId.startsWith(DeviceType.PREFIX)) { deviceTypeId = DeviceType.PREFIX + deviceTypeId; } for (DeviceType type : types) { if (deviceTypeId.equals(type.getDeviceTypeId())) { return type; } } return null; } public static DeviceType getBestDeviceType() { return getBestDeviceType(null, null, null, null); } public static DeviceType getBestDeviceType(DeviceFamily family) { return getBestDeviceType(null, family, null, null); } /** * Returns the best {@link DeviceType} matching the parameters. If multiple * device types match the parameters the first one with the highest SDK * number will be returned. If no device name and no {@link DeviceFamily} is * specified this method will default to {@link DeviceFamily#iPhone}. */ public static DeviceType getBestDeviceType(Arch arch, DeviceFamily family, String deviceName, String sdkVersion) { if (deviceName == null && family == null) { family = DeviceFamily.iPhone; } String preferredDeciveName = PREFERRED_IPHONE_SIM_NAME; if (family == DeviceFamily.iPad) { preferredDeciveName = PREFERRED_IPAD_SIM_NAME; } DeviceType best = null; for (DeviceType type : filter(listDeviceTypes(), arch, family, deviceName, sdkVersion)) { if (best == null) { best = type; } else if (type.getSdk().compareTo(best.getSdk()) > 0 || type.getSdk().compareTo(best.getSdk()) == 0 && type.getDeviceName().equals(preferredDeciveName)) { best = type; } } if (best == null) { throw new IllegalArgumentException("Unable to find a matching device " + "[arch=" + arch + ", family=" + family + ", name=" + deviceName + ", sdk=" + sdkVersion + "]"); } return best; } @Override public String toString() { return "DeviceType [deviceName=" + deviceName + ", sdk=" + sdk + ", archs=" + archs + "]"; } }