microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fix-timing-issues

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/ios/iOSTargetManager.ts

287lines · modeblame

4cd25962JiglioNero4 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
09f6024fHeniker4 years ago4import * as nls from "vscode-nls";
5import { QuickPickOptions, window } from "vscode";
4cd25962JiglioNero4 years ago6import { ChildProcess } from "../../common/node/childProcess";
ab0238b7RedMickey4 years ago7import { PromiseUtil } from "../../common/node/promise";
4cd25962JiglioNero4 years ago8import { IDebuggableMobileTarget, MobileTarget } from "../mobileTarget";
9import { MobileTargetManager } from "../mobileTargetManager";
10import { OutputChannelLogger } from "../log/OutputChannelLogger";
11import { TargetType } from "../generalPlatform";
09f6024fHeniker4 years ago12
4cd25962JiglioNero4 years ago13nls.config({
14messageFormat: nls.MessageFormat.bundle,
15bundleFormat: nls.BundleFormat.standalone,
16})();
17const localize = nls.loadMessageBundle();
18
19export interface IDebuggableIOSTarget extends IDebuggableMobileTarget {
20name: string;
21system: string;
22}
23
24export class IOSTarget extends MobileTarget implements IDebuggableIOSTarget {
25protected _system: string;
26protected _name: string;
27
28public static fromInterface(obj: IDebuggableIOSTarget): IOSTarget {
29return new IOSTarget(obj.isOnline, obj.isVirtualTarget, obj.id, obj.name, obj.system);
30}
31
32constructor(
33isOnline: boolean,
34isVirtualTarget: boolean,
35id: string,
36name: string,
37system: string,
38) {
39super(isOnline, isVirtualTarget, id, name);
40this._system = system;
41}
42
43get system(): string {
44return this._system;
45}
46
47get name(): string {
48return this._name;
49}
50
51set name(value: string) {
52this._name = value;
53}
54}
55
56export class IOSTargetManager extends MobileTargetManager {
57private static readonly XCRUN_COMMAND = "xcrun";
58private static readonly SIMCTL_COMMAND = "simctl";
59private static readonly BOOT_COMMAND = `boot`;
60private static readonly SIMULATORS_LIST_COMMAND = `${IOSTargetManager.XCRUN_COMMAND} ${IOSTargetManager.SIMCTL_COMMAND} list devices available --json`;
61private static readonly ALL_DEVICES_LIST_COMMAND = `${IOSTargetManager.XCRUN_COMMAND} xctrace list devices`;
62private static readonly BOOTED_STATE = "Booted";
63private static readonly SIMULATOR_START_TIMEOUT = 120;
64private static readonly ANY_SYSTEM = "AnySystem";
65
66private childProcess: ChildProcess = new ChildProcess();
67private logger: OutputChannelLogger = OutputChannelLogger.getChannel(
68OutputChannelLogger.MAIN_CHANNEL_NAME,
69true,
70);
71protected targets?: IDebuggableIOSTarget[];
72
73public async collectTargets(targetType?: TargetType): Promise<void> {
74this.targets = [];
75if (targetType === undefined || targetType === TargetType.Simulator) {
76const simulators = JSON.parse(
77await this.childProcess.execToString(`${IOSTargetManager.SIMULATORS_LIST_COMMAND}`),
78);
79Object.keys(simulators.devices).forEach(rawSystem => {
80const temp = rawSystem.split(".").slice(-1)[0].split("-"); // "com.apple.CoreSimulator.SimRuntime.iOS-11-4" -> ["iOS", "11", "4"]
81const system = `${temp[0]} ${temp.slice(1).join(".")}`; // ["iOS", "11", "4"] -> iOS 11.4
82simulators.devices[rawSystem].forEach((device: any) => {
83// Now we support selection only for iOS system
84if (system.includes("iOS")) {
85this.targets?.push({
86id: device.udid,
87name: device.name,
88system,
89isVirtualTarget: true,
90isOnline: device.state === IOSTargetManager.BOOTED_STATE,
91});
92}
93});
94});
95}
96
97if (targetType === undefined || targetType === TargetType.Device) {
98const allDevicesOutput = await this.childProcess.execToString(
99`${IOSTargetManager.ALL_DEVICES_LIST_COMMAND}`,
100);
09f6024fHeniker4 years ago101// Output example:
4cd25962JiglioNero4 years ago102// == Devices ==
103// sierra (EFDAAD01-E1A3-5F00-A357-665B501D5520)
104// My iPhone (14.4.2) (33n546e591e707bd64c718bfc1bf3e8b7c16bfc9)
105//
106// == Simulators ==
107// Apple TV (14.5) (417BDFD8-6E22-4F87-BCAA-19C241AC9548)
108// Apple TV 4K (2nd generation) (14.5) (925E6E38-0D7B-45E9-ADE0-89C20779D467)
09f6024fHeniker4 years ago109// ...
4cd25962JiglioNero4 years ago110const lines = allDevicesOutput
111.split("\n")
112.map(line => line.trim())
113.filter(line => !!line);
09f6024fHeniker4 years ago114const firstDevicesIndex = lines.indexOf("== Devices ==") + 1;
115const lastDevicesIndex = lines.indexOf("== Simulators ==") - 1;
4cd25962JiglioNero4 years ago116for (let i = firstDevicesIndex; i <= lastDevicesIndex; i++) {
117const line = lines[i];
118const params = line
119.split(" ")
120.map(el => el.trim())
121.filter(el => !!el);
122// Add only devices with system version
123if (
124params[params.length - 1].match(/\(.+\)/) &&
125params[params.length - 2].match(/\(.+\)/)
126) {
127this.targets.push({
128id: params[params.length - 1].replace(/\(|\)/g, "").trim(),
129name: params.slice(0, params.length - 2).join(" "),
130system: params[params.length - 2].replace(/\(|\)/g, "").trim(),
131isVirtualTarget: false,
132isOnline: true,
133});
134}
135}
136}
137}
138
139public async selectAndPrepareTarget(
140filter?: (el: IDebuggableIOSTarget) => boolean,
141): Promise<IOSTarget | undefined> {
142const selectedTarget = await this.startSelection(filter);
143if (selectedTarget) {
09f6024fHeniker4 years ago144return !selectedTarget.isOnline && selectedTarget.isVirtualTarget
145? this.launchSimulator(selectedTarget)
146: IOSTarget.fromInterface(selectedTarget);
4cd25962JiglioNero4 years ago147}
148return undefined;
149}
150
151public async isVirtualTarget(targetString: string): Promise<boolean> {
152try {
153if (targetString === TargetType.Device) {
154return false;
155} else if (targetString === TargetType.Simulator) {
156return true;
157}
09f6024fHeniker4 years ago158const target = (
159await this.getTargetList(
160target => target.id === targetString || target.name === targetString,
161)
162)[0];
163if (target) {
164return target.isVirtualTarget;
165}
166throw Error("There is no any target with specified target string");
4cd25962JiglioNero4 years ago167} catch {
168throw new Error(
169localize(
170"CouldNotRecognizeTargetType",
171"Could not recognize type of the target {0}",
172targetString,
173),
174);
175}
176}
177
178protected async startSelection(
179filter?: (el: IDebuggableIOSTarget) => boolean,
180): Promise<IDebuggableIOSTarget | undefined> {
181const system = await this.selectSystem(filter);
182if (system) {
183return (await this.selectTarget(
184(el: IDebuggableIOSTarget) =>
185(filter ? filter(el) : true) &&
186(system === IOSTargetManager.ANY_SYSTEM ? true : el.system === system),
187)) as IDebuggableIOSTarget | undefined;
188}
189return;
190}
191
192protected async selectSystem(
193filter?: (el: IDebuggableIOSTarget) => boolean,
194): Promise<string | undefined> {
195const targets = (await this.getTargetList(filter)) as IDebuggableIOSTarget[];
196// If we select only from devices, we should not select system
197if (!targets.find(target => target.isVirtualTarget)) {
198return IOSTargetManager.ANY_SYSTEM;
199}
200const names: Set<string> = new Set(targets.map(target => target.system));
201const systemsList = Array.from(names);
202let result: string | undefined = systemsList[0];
203if (systemsList.length > 1) {
204const quickPickOptions: QuickPickOptions = {
205ignoreFocusOut: true,
206canPickMany: false,
207placeHolder: localize(
208"SelectIOSSystemVersion",
209"Select system version of iOS target",
210),
211};
212result = await window.showQuickPick(systemsList, quickPickOptions);
213}
214return result?.toString();
215}
216
217protected async launchSimulator(
218virtualTarget: IDebuggableIOSTarget,
219): Promise<IOSTarget | undefined> {
220return new Promise<IOSTarget | undefined>((resolve, reject) => {
8df9830bSamriel4 years ago221let emulatorLaunchFailed = false;
4cd25962JiglioNero4 years ago222const emulatorProcess = this.childProcess.spawn(
223IOSTargetManager.XCRUN_COMMAND,
224[IOSTargetManager.SIMCTL_COMMAND, IOSTargetManager.BOOT_COMMAND, virtualTarget.id],
225{
226detached: true,
227},
228true,
229);
230emulatorProcess.spawnedProcess.unref();
231emulatorProcess.outcome.catch(e => {
8df9830bSamriel4 years ago232emulatorLaunchFailed = true;
4cd25962JiglioNero4 years ago233this.logger.error(
234localize(
235"ErrorWhileLaunchingSimulator",
236"Error while launching simulator {0} : {1}",
237`${virtualTarget.name}(${virtualTarget.id})`,
238e,
239),
240);
241reject(e);
242});
243
244const condition = async () => {
8df9830bSamriel4 years ago245if (emulatorLaunchFailed)
246throw new Error("iOS simulator launch failed unexpectedly");
4cd25962JiglioNero4 years ago247await this.collectTargets(TargetType.Simulator);
248const onlineTarget = (await this.getTargetList()).find(
249target => target.id === virtualTarget.id && target.isOnline,
250);
251return onlineTarget ? true : null;
252};
253
09f6024fHeniker4 years ago254void PromiseUtil.waitUntil<boolean>(
4cd25962JiglioNero4 years ago255condition,
2561000,
257IOSTargetManager.SIMULATOR_START_TIMEOUT * 1000,
8df9830bSamriel4 years ago258).then(
259isBooted => {
260if (isBooted) {
261virtualTarget.isOnline = true;
262this.logger.info(
263localize(
264"SimulatorLaunched",
265"Launched simulator {0}",
4cd25962JiglioNero4 years ago266virtualTarget.name,
8df9830bSamriel4 years ago267),
268);
269resolve(IOSTarget.fromInterface(virtualTarget));
270} else {
271reject(
272new Error(
273`Virtual device launch finished with an exception: ${localize(
274"SimulatorStartWarning",
275"Could not start the simulator {0} within {1} seconds.",
276virtualTarget.name,
277IOSTargetManager.SIMULATOR_START_TIMEOUT,
278)}`,
279),
280);
281}
282},
283() => {},
284);
4cd25962JiglioNero4 years ago285});
286}
287}