microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
smoke-actionbar-use-packager-helper

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/ios/iOSContainerUtility.ts

276lines · 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 .execFileToString(idbPath, [
186 "--log",
187 idbLogLevel,
188 "file",
189 "push",
190 "--udid",
191 udid,
192 "--bundle-id",
193 bundleId,
194 src,
195 dst,
196 ])
197 .then(() => {
198 return;
199 })
200 .catch(e => handleMissingIdb(e)),
201 logger,
202 );
203}
204
205async function pull(
206 udid: string,
207 src: string,
208 bundleId: string,
209 dst: string,
210 logger?: OutputChannelLogger,
211): Promise<void> {
212 const cp = new ChildProcess();
213 await checkIdbIsInstalled();
214 return wrapWithErrorMessage(
215 cp
216 .execFileToString(idbPath, [
217 "--log",
218 idbLogLevel,
219 "file",
220 "pull",
221 "--udid",
222 udid,
223 "--bundle-id",
224 bundleId,
225 src,
226 dst,
227 ])
228 .then(() => {
229 return;
230 })
231 .catch(e => handleMissingIdb(e)),
232 logger,
233 );
234}
235
236export async function checkIdbIsInstalled(): Promise<void> {
237 const isInstalled = await isIdbAvailable();
238 if (!isInstalled) {
239 throw new Error(
240 `idb is required to use iOS devices. Please install it with instructions from https://github.com/facebook/idb.`,
241 );
242 }
243}
244
245// The fb-internal idb binary is a shim that downloads the proper one on first run. It requires sudo to do so.
246// If we detect this, Tell the user how to fix it.
247function handleMissingIdb(e: Error): void {
248 if (e.message && e.message.includes("sudo: no tty present and no askpass program specified")) {
249 throw new Error(
250 `idb doesn't appear to be installed. Run "${idbPath} list-targets" to fix this.`,
251 );
252 }
253 throw e;
254}
255
256function wrapWithErrorMessage<T>(p: Promise<T>, logger?: OutputChannelLogger): Promise<T> {
257 return p.catch((e: Error) => {
258 logger?.error(e.message);
259 // Give the user instructions. Don't embed the error because it's unique per invocation so won't be deduped.
260 throw new Error(
261 "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.",
262 );
263 });
264}
265
266export default {
267 isAvailable,
268 targets,
269 push,
270 pull,
271};
272
273/**
274 * @preserve
275 * End region: https://github.com/facebook/flipper/blob/c2848df7f210c363113797c0f2e3db8c5d4fd49f/desktop/app/src/server/devices/ios/iOSContainerUtility.tsx
276 */