microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
test-microbuild1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/ios/iOSContainerUtility.ts

258lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4/* eslint-disable */
5/* eslint-enable prettier/prettier*/
6
7import * as fs from "fs";
8import * as path from "path";
9import { logger } from "@vscode/debugadapter/lib/logger";
10import { ChildProcess } from "../../common/node/childProcess";
11import { PromiseUtil } from "../../common/node/promise";
12import { OutputChannelLogger } from "../log/OutputChannelLogger";
13import { IDebuggableMobileTarget } from "../mobileTarget";
14
15/**
16 * @preserve
17 * Start region: the code is borrowed from https://github.com/facebook/flipper/blob/c2848df7f210c363113797c0f2e3db8c5d4fd49f/desktop/app/src/server/devices/ios/iOSContainerUtility.tsx
18 *
19 * Copyright (c) Facebook, Inc. and its affiliates.
20 *
21 * This source code is licensed under the MIT license found in the
22 * LICENSE file in the root directory of this source tree.
23 *
24 * @format
25 */
26
27export const idbPath = "/usr/local/bin/idb";
28// Use debug to get helpful logs when idb fails
29const idbLogLevel = "DEBUG";
30
31type IdbTarget = {
32 name: string;
33 udid: string;
34 state: string;
35 type: string;
36 target_type?: string;
37 os_version: string;
38 architecture: string;
39};
40
41export type DeviceTarget = IDebuggableMobileTarget;
42
43export const isIdbAvailable = PromiseUtil.promiseCacheDecorator<boolean>(isAvailable);
44
45function isAvailable(): Promise<boolean> {
46 if (!idbPath) {
47 return Promise.resolve(false);
48 }
49 return fs.promises
50 .access(idbPath, fs.constants.X_OK)
51 .then(() => true)
52 .catch(() => false);
53}
54
55export async function isXcodeDetected(): Promise<boolean> {
56 return new ChildProcess()
57 .execToString("xcode-select -p")
58 .then(stdout => {
59 return fs.existsSync(stdout.trim());
60 })
61 .catch(_ => false);
62}
63
64async function queryTargetsWithoutXcodeDependency(
65 idbCompanionPath: string,
66 isAvailableFunc: (idbPath: string) => Promise<boolean>,
67): Promise<Array<DeviceTarget>> {
68 if (await isAvailableFunc(idbCompanionPath)) {
69 return new ChildProcess()
70 .execToString(`${idbCompanionPath} --list 1 --only device`)
71 .then(stdout => parseIdbTargets(stdout))
72 .catch((e: Error) => {
73 logger.warn(
74 `Failed to query idb_companion --list 1 --only device for physical targets: ${e}`,
75 );
76 return [];
77 });
78 } else {
79 logger.warn(
80 `Unable to locate idb_companion in ${idbCompanionPath}. Try running sudo yum install -y fb-idb`,
81 );
82 return [];
83 }
84}
85
86function parseIdbTargets(lines: string): Array<DeviceTarget> {
87 return lines
88 .trim()
89 .split("\n")
90 .map(line => line.trim())
91 .filter(Boolean)
92 .map(line => JSON.parse(line))
93 .filter(({ state }: IdbTarget) => state.toLocaleLowerCase() === "booted")
94 .map<IdbTarget>(({ type, target_type, ...rest }: IdbTarget) => ({
95 type: (type || target_type) === "simulator" ? "emulator" : "physical",
96 ...rest,
97 }))
98 .map<DeviceTarget>((target: IdbTarget) => ({
99 id: target.udid,
100 isVirtualTarget: target.type === "emulator",
101 name: target.name,
102 isOnline: true,
103 }));
104}
105
106export async function idbListTargets(idbPath: string): Promise<Array<DeviceTarget>> {
107 return new ChildProcess()
108 .execToString(`${idbPath} list-targets --json`)
109 .then(stdout =>
110 // See above.
111 parseIdbTargets(stdout),
112 )
113 .catch((e: Error) => {
114 logger.warn(`Failed to query idb for targets: ${e}`);
115 return [];
116 });
117}
118
119async function targets(): Promise<Array<DeviceTarget>> {
120 if (process.platform !== "darwin") {
121 return [];
122 }
123 const isXcodeInstalled = await isXcodeDetected();
124 if (!isXcodeInstalled) {
125 const idbCompanionPath = path.dirname(idbPath) + "/idb_companion";
126 return queryTargetsWithoutXcodeDependency(idbCompanionPath, isAvailable);
127 }
128
129 // Not all users have idb installed because you can still use
130 // Flipper with Simulators without it.
131 // But idb is MUCH more CPU efficient than xcrun, so
132 // when installed, use it. This still holds true
133 // with the move from instruments to xcrun.
134 // TODO: Move idb availability check up.
135 return (await isIdbAvailable())
136 ? await idbListTargets(idbPath)
137 : new ChildProcess()
138 .execToString("xcrun xctrace list devices")
139 .then(stdout => {
140 const targets: DeviceTarget[] = [];
141 const lines = stdout
142 .split("\n")
143 .map(line => line.trim())
144 .filter(line => !!line);
145 const firstDevicesIndex = lines.indexOf("== Devices ==") + 1;
146 const lastDevicesIndex = lines.indexOf("== Simulators ==") - 1;
147 for (let i = firstDevicesIndex; i <= lastDevicesIndex; i++) {
148 const line = lines[i];
149 const params = line
150 .split(" ")
151 .map(el => el.trim())
152 .filter(el => !!el);
153 // Add only devices with system version
154 if (
155 params[params.length - 1].match(/\(.+\)/) &&
156 params[params.length - 2].match(/\(.+\)/)
157 ) {
158 targets.push({
159 id: params[params.length - 1].replace(/\(|\)/g, "").trim(),
160 name: params.slice(0, params.length - 2).join(" "),
161 isVirtualTarget: false,
162 isOnline: true,
163 });
164 }
165 }
166 return targets;
167 })
168 .catch(e => {
169 logger.warn(`Failed to query for devices using xctrace: ${e}`);
170 return [];
171 });
172}
173
174async function push(
175 udid: string,
176 src: string,
177 bundleId: string,
178 dst: string,
179 logger?: OutputChannelLogger,
180): Promise<void> {
181 const cp = new ChildProcess();
182 await checkIdbIsInstalled();
183 return wrapWithErrorMessage(
184 cp
185 .execToString(
186 `${idbPath} --log ${idbLogLevel} file push --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
187 )
188 .then(() => {
189 return;
190 })
191 .catch(e => handleMissingIdb(e)),
192 logger,
193 );
194}
195
196async function pull(
197 udid: string,
198 src: string,
199 bundleId: string,
200 dst: string,
201 logger?: OutputChannelLogger,
202): Promise<void> {
203 const cp = new ChildProcess();
204 await checkIdbIsInstalled();
205 return wrapWithErrorMessage(
206 cp
207 .execToString(
208 `${idbPath} --log ${idbLogLevel} file pull --udid ${udid} --bundle-id ${bundleId} '${src}' '${dst}'`,
209 )
210 .then(() => {
211 return;
212 })
213 .catch(e => handleMissingIdb(e)),
214 logger,
215 );
216}
217
218export async function checkIdbIsInstalled(): Promise<void> {
219 const isInstalled = await isIdbAvailable();
220 if (!isInstalled) {
221 throw new Error(
222 `idb is required to use iOS devices. Please install it with instructions from https://github.com/facebook/idb.`,
223 );
224 }
225}
226
227// The fb-internal idb binary is a shim that downloads the proper one on first run. It requires sudo to do so.
228// If we detect this, Tell the user how to fix it.
229function handleMissingIdb(e: Error): void {
230 if (e.message && e.message.includes("sudo: no tty present and no askpass program specified")) {
231 throw new Error(
232 `idb doesn't appear to be installed. Run "${idbPath} list-targets" to fix this.`,
233 );
234 }
235 throw e;
236}
237
238function wrapWithErrorMessage<T>(p: Promise<T>, logger?: OutputChannelLogger): Promise<T> {
239 return p.catch((e: Error) => {
240 logger?.error(e.message);
241 // Give the user instructions. Don't embed the error because it's unique per invocation so won't be deduped.
242 throw new Error(
243 "A problem with idb has ocurred. Please run `sudo rm -rf /tmp/idb*` and `sudo yum install -y fb-idb` to update it, if that doesn't fix it, post in https://github.com/microsoft/vscode-react-native.",
244 );
245 });
246}
247
248export default {
249 isAvailable,
250 targets,
251 push,
252 pull,
253};
254
255/**
256 * @preserve
257 * End region: https://github.com/facebook/flipper/blob/c2848df7f210c363113797c0f2e3db8c5d4fd49f/desktop/app/src/server/devices/ios/iOSContainerUtility.tsx
258 */
259