microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.8.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/ios/iOSPlatform.ts

440lines · modeblame

8a67e140Artem Egorov8 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
4import * as path from "path";
df8c800dArtem Egorov8 years ago5import * as semver from "semver";
8a67e140Artem Egorov8 years ago6
34472878RedMickey5 years ago7import { ChildProcess } from "../../common/node/childProcess";
8import { CommandExecutor } from "../../common/commandExecutor";
4cd25962JiglioNero4 years ago9import { MobilePlatformDeps, TargetType } from "../generalPlatform";
34472878RedMickey5 years ago10import { IIOSRunOptions, PlatformType } from "../launchArgs";
11import { PlistBuddy } from "./plistBuddy";
12import { IOSDebugModeManager } from "./iOSDebugModeManager";
13import { OutputVerifier, PatternToFailure } from "../../common/outputVerifier";
14import { TelemetryHelper } from "../../common/telemetryHelper";
fc602bb6Yuri Skorokhodov7 years ago15import { InternalErrorCode } from "../../common/error/internalErrorCode";
d7d405aeYuri Skorokhodov7 years ago16import * as nls from "vscode-nls";
7e74daf7Yuri Skorokhodov6 years ago17import { AppLauncher } from "../appLauncher";
4cd25962JiglioNero4 years ago18import { GeneralMobilePlatform } from "../generalMobilePlatform";
19import { IDebuggableIOSTarget, IOSTarget, IOSTargetManager } from "./iOSTargetManager";
20import { ErrorHelper } from "../../common/error/errorHelper";
34472878RedMickey5 years ago21nls.config({
22messageFormat: nls.MessageFormat.bundle,
23bundleFormat: nls.BundleFormat.standalone,
24})();
d7d405aeYuri Skorokhodov7 years ago25const localize = nls.loadMessageBundle();
8022afdfVladimir Kotikov8 years ago26
8a67e140Artem Egorov8 years ago27export class IOSPlatform extends GeneralMobilePlatform {
28public static DEFAULT_IOS_PROJECT_RELATIVE_PATH = "ios";
0a68f8dbArtem Egorov8 years ago29
8a67e140Artem Egorov8 years ago30private plistBuddy = new PlistBuddy();
0db0be15Artem Egorov8 years ago31private iosProjectRoot: string;
7daed3fcArtem Egorov8 years ago32private iosDebugModeManager: IOSDebugModeManager;
33
db6fd42aRuslan Bikkinin7 years ago34private defaultConfiguration: string = "Debug";
35private configurationArgumentName: string = "--configuration";
8a67e140Artem Egorov8 years ago36
4cd25962JiglioNero4 years ago37protected target?: IOSTarget;
38
0a68f8dbArtem Egorov8 years ago39// We should add the common iOS build/run errors we find to this list
34472878RedMickey5 years ago40private static RUN_IOS_FAILURE_PATTERNS: PatternToFailure[] = [
41{
42pattern: "No devices are booted",
43errorCode: InternalErrorCode.IOSSimulatorNotLaunchable,
44},
45{
46pattern: "FBSOpenApplicationErrorDomain",
47errorCode: InternalErrorCode.IOSSimulatorNotLaunchable,
48},
49{
50pattern: "ios-deploy",
51errorCode: InternalErrorCode.IOSDeployNotFound,
52},
53];
8a67e140Artem Egorov8 years ago54
3021756bYuri Skorokhodov6 years ago55private static readonly RUN_IOS_SUCCESS_PATTERNS = ["BUILD SUCCEEDED"];
8a67e140Artem Egorov8 years ago56
0a68f8dbArtem Egorov8 years ago57constructor(protected runOptions: IIOSRunOptions, platformDeps: MobilePlatformDeps = {}) {
58super(runOptions, platformDeps);
8a67e140Artem Egorov8 years ago59
4cd25962JiglioNero4 years ago60this.targetManager = new IOSTargetManager();
db6fd42aRuslan Bikkinin7 years ago61this.runOptions.configuration = this.getConfiguration();
62
34472878RedMickey5 years ago63if (this.runOptions.iosRelativeProjectPath) {
64// Deprecated option
65this.logger.warning(
66localize(
67"iosRelativeProjectPathOptionIsDeprecatedUseRunArgumentsInstead",
68"'iosRelativeProjectPath' option is deprecated. Please use 'runArguments' instead.",
69),
70);
8a67e140Artem Egorov8 years ago71}
72
34472878RedMickey5 years ago73const iosProjectFolderPath = IOSPlatform.getOptFromRunArgs(
74this.runArguments,
75"--project-path",
76false,
77);
78this.iosProjectRoot = path.join(
79this.projectPath,
80iosProjectFolderPath ||
81this.runOptions.iosRelativeProjectPath ||
82IOSPlatform.DEFAULT_IOS_PROJECT_RELATIVE_PATH,
83);
116c3cb0Ruslan Bikkinin7 years ago84const schemeFromArgs = IOSPlatform.getOptFromRunArgs(this.runArguments, "--scheme", false);
34472878RedMickey5 years ago85this.iosDebugModeManager = new IOSDebugModeManager(
86this.iosProjectRoot,
87this.projectPath,
88schemeFromArgs ? schemeFromArgs : this.runOptions.scheme,
89);
4cd25962JiglioNero4 years ago90}
8a67e140Artem Egorov8 years ago91
4cd25962JiglioNero4 years ago92public async getTarget(): Promise<IOSTarget> {
93if (!this.target) {
94const targetFromRunArgs = await this.getTargetFromRunArgs();
95if (targetFromRunArgs) {
96this.target = targetFromRunArgs;
97} else {
98const targets = (await this.targetManager.getTargetList()) as IDebuggableIOSTarget[];
99const targetsBySpecifiedType = targets.filter(target => {
100switch (this.runOptions.target) {
101case TargetType.Simulator:
102return target.isVirtualTarget;
103case TargetType.Device:
104return !target.isVirtualTarget;
105case undefined:
106case "":
107return true;
108default:
109return (
110target.id === this.runOptions.target ||
111target.name === this.runOptions.target
112);
113}
114});
115if (targetsBySpecifiedType.length) {
116this.target = IOSTarget.fromInterface(targetsBySpecifiedType[0]);
117} else if (targets.length) {
118this.logger.warning(
119localize(
120"ThereIsNoTargetWithSpecifiedTargetType",
121"There is no any target with specified target type '{0}'. Continue with any target.",
122this.runOptions.target,
123),
124);
125this.target = IOSTarget.fromInterface(targets[0]);
126} else {
127throw ErrorHelper.getInternalError(
128InternalErrorCode.IOSThereIsNoAnyDebuggableTarget,
129);
130}
131}
8022afdfVladimir Kotikov8 years ago132}
4cd25962JiglioNero4 years ago133return this.target;
134}
0db0be15Artem Egorov8 years ago135
4cd25962JiglioNero4 years ago136public async showDevMenu(appLauncher: AppLauncher): Promise<void> {
137const worker = appLauncher.getAppWorker();
138if (worker) {
139worker.showDevMenuCommand();
8a67e140Artem Egorov8 years ago140}
141}
142
4cd25962JiglioNero4 years ago143public async reloadApp(appLauncher: AppLauncher): Promise<void> {
144const worker = appLauncher.getAppWorker();
145if (worker) {
146worker.reloadAppCommand();
119d7878JiglioNero5 years ago147}
148}
149
0d77292aJiglioNero4 years ago150public async runApp(): Promise<void> {
e7a2c40dRedMickey4 years ago151let extProps: any = {
031832ffArtem Egorov8 years ago152platform: {
259c018fYuri Skorokhodov5 years ago153value: PlatformType.iOS,
031832ffArtem Egorov8 years ago154isPii: false,
155},
156};
157
e7a2c40dRedMickey4 years ago158if (this.runOptions.isDirect) {
159extProps.isDirect = {
160value: true,
161isPii: false,
162};
163this.projectObserver?.updateRNIosHermesProjectState(true);
164}
165
34472878RedMickey5 years ago166extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
167this.runOptions,
168this.runOptions.reactNativeVersions,
169extProps,
170);
ba953e9fRedMickey6 years ago171
0d77292aJiglioNero4 years ago172await TelemetryHelper.generate("iOSPlatform.runApp", extProps, async () => {
031832ffArtem Egorov8 years ago173// Compile, deploy, and launch the app on either a simulator or a device
34472878RedMickey5 years ago174const env = GeneralMobilePlatform.getEnvArgument(
175process.env,
176this.runOptions.env,
177this.runOptions.envFile,
178);
179
180if (
181!semver.valid(
182this.runOptions.reactNativeVersions.reactNativeVersion,
183) /*Custom RN implementations should support this flag*/ ||
184semver.gte(
185this.runOptions.reactNativeVersions.reactNativeVersion,
186IOSPlatform.NO_PACKAGER_VERSION,
187)
188) {
7fa90b3bRedMickey6 years ago189this.runArguments.push("--no-packager");
190}
191// Since @react-native-community/cli@2.1.0 build output are hidden by default
192// we are using `--verbose` to show it as it contains `BUILD SUCCESSFUL` and other patterns
193if (semver.gte(this.runOptions.reactNativeVersions.reactNativeVersion, "0.60.0")) {
194this.runArguments.push("--verbose");
195}
34472878RedMickey5 years ago196const runIosSpawn = new CommandExecutor(
4dfb1c4cetatanova5 years ago197this.runOptions.nodeModulesRoot,
34472878RedMickey5 years ago198this.projectPath,
199this.logger,
200).spawnReactCommand("run-ios", this.runArguments, { env });
0d77292aJiglioNero4 years ago201await new OutputVerifier(
ce5e88eeYuri Skorokhodov5 years ago202() =>
34472878RedMickey5 years ago203this.generateSuccessPatterns(
204this.runOptions.reactNativeVersions.reactNativeVersion,
205),
206() => Promise.resolve(IOSPlatform.RUN_IOS_FAILURE_PATTERNS),
207PlatformType.iOS,
208).process(runIosSpawn);
031832ffArtem Egorov8 years ago209});
8a67e140Artem Egorov8 years ago210}
211
0d77292aJiglioNero4 years ago212public async enableJSDebuggingMode(): Promise<void> {
8a67e140Artem Egorov8 years ago213// Configure the app for debugging
4cd25962JiglioNero4 years ago214if (!(await this.getTarget()).isVirtualTarget) {
8a67e140Artem Egorov8 years ago215// Note that currently we cannot automatically switch the device into debug mode.
34472878RedMickey5 years ago216this.logger.info(
217"Application is running on a device, please shake device and select 'Debug JS Remotely' to enable debugging.",
218);
0d77292aJiglioNero4 years ago219return;
8a67e140Artem Egorov8 years ago220}
221
222// Wait until the configuration file exists, and check to see if debugging is enabled
0d77292aJiglioNero4 years ago223const [debugModeEnabled, bundleId] = await Promise.all<boolean | string>([
1c2424f4RedMickey5 years ago224this.iosDebugModeManager.getAppRemoteDebuggingSetting(
34472878RedMickey5 years ago225this.runOptions.configuration,
226this.runOptions.productName,
227),
8a67e140Artem Egorov8 years ago228this.getBundleId(),
0d77292aJiglioNero4 years ago229]);
230if (debugModeEnabled) {
231return;
232}
233// Debugging must still be enabled
234// We enable debugging by writing to a plist file that backs a NSUserDefaults object,
235// but that file is written to by the app on occasion. To avoid races, we shut the app
236// down before writing to the file.
237const childProcess = new ChildProcess();
238const output = await childProcess.execToString("xcrun simctl spawn booted launchctl list");
239// Try to find an entry that looks like UIKitApplication:com.example.myApp[0x4f37]
240const regex = new RegExp(`(\\S+${bundleId}\\S+)`);
241const match = regex.exec(output);
242// If we don't find a match, the app must not be running and so we do not need to close it
243if (match) {
244await childProcess.exec(`xcrun simctl spawn booted launchctl stop ${match[1]}`);
245}
246// Write to the settings file while the app is not running to avoid races
247await this.iosDebugModeManager.setAppRemoteDebuggingSetting(
248/*enable=*/ true,
249this.runOptions.configuration,
250this.runOptions.productName,
251);
252// Relaunch the app
253return await this.runApp();
8a67e140Artem Egorov8 years ago254}
255
0d77292aJiglioNero4 years ago256public async disableJSDebuggingMode(): Promise<void> {
4cd25962JiglioNero4 years ago257if (!(await this.getTarget()).isVirtualTarget) {
0d77292aJiglioNero4 years ago258return;
c73f53cbJiglioNero5 years ago259}
1c2424f4RedMickey5 years ago260return this.iosDebugModeManager.setAppRemoteDebuggingSetting(
34472878RedMickey5 years ago261/*enable=*/ false,
262this.runOptions.configuration,
263this.runOptions.productName,
264);
0a68f8dbArtem Egorov8 years ago265}
266
ce5e88eeYuri Skorokhodov5 years ago267public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago268return this.packager.prewarmBundleCache(PlatformType.iOS);
8a67e140Artem Egorov8 years ago269}
270
cbc7ac5bArtem Egorov7 years ago271public getRunArguments(): string[] {
8a67e140Artem Egorov8 years ago272let runArguments: string[] = [];
0db0be15Artem Egorov8 years ago273
274if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
b57ea017Artem Egorov8 years ago275runArguments = this.runOptions.runArguments;
116c3cb0Ruslan Bikkinin7 years ago276if (this.runOptions.scheme) {
34472878RedMickey5 years ago277const schemeFromArgs = IOSPlatform.getOptFromRunArgs(
278runArguments,
279"--scheme",
280false,
281);
116c3cb0Ruslan Bikkinin7 years ago282if (!schemeFromArgs) {
283runArguments.push("--scheme", this.runOptions.scheme);
284} else {
34472878RedMickey5 years ago285this.logger.warning(
286localize(
287"iosSchemeParameterAlreadySetInRunArguments",
288"'--scheme' is set as 'runArguments' configuration parameter value, 'scheme' configuration parameter value will be omitted",
289),
290);
116c3cb0Ruslan Bikkinin7 years ago291}
292}
b57ea017Artem Egorov8 years ago293} else {
294if (this.runOptions.target) {
de838bbfJiglioNero6 years ago295runArguments.push(...this.handleTargetArg(this.runOptions.target));
b57ea017Artem Egorov8 years ago296}
8abbd163Artem Egorov8 years ago297
b57ea017Artem Egorov8 years ago298if (this.runOptions.iosRelativeProjectPath) {
299runArguments.push("--project-path", this.runOptions.iosRelativeProjectPath);
8022afdfVladimir Kotikov8 years ago300}
8a67e140Artem Egorov8 years ago301
b57ea017Artem Egorov8 years ago302// provide any defined scheme
303if (this.runOptions.scheme) {
304runArguments.push("--scheme", this.runOptions.scheme);
305}
8a67e140Artem Egorov8 years ago306}
307
308return runArguments;
309}
310
4cd25962JiglioNero4 years ago311public async getTargetFromRunArgs(): Promise<IOSTarget | undefined> {
312if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
c76b733bJiglioNero4 years ago313const targets = (await this.targetManager.getTargetList()) as IDebuggableIOSTarget[];
314
4cd25962JiglioNero4 years ago315const udid = GeneralMobilePlatform.getOptFromRunArgs(
316this.runOptions.runArguments,
317"--udid",
318);
319if (udid) {
320const target = targets.find(target => target.id === udid);
321if (target) {
322return IOSTarget.fromInterface(target);
c76b733bJiglioNero4 years ago323} else {
324this.logger.warning(
325localize(
326"ThereIsNoIosTargetWithSuchUdid",
327"There is no iOS target with such UDID: {0}",
328udid,
329),
330);
4cd25962JiglioNero4 years ago331}
332}
333
334const device = GeneralMobilePlatform.getOptFromRunArgs(
335this.runOptions.runArguments,
336"--device",
337);
338if (device) {
339const target = targets.find(
c76b733bJiglioNero4 years ago340target => !target.isVirtualTarget && target.name === device,
4cd25962JiglioNero4 years ago341);
342if (target) {
343return IOSTarget.fromInterface(target);
c76b733bJiglioNero4 years ago344} else {
345this.logger.warning(
346localize(
347"ThereIsNoIosDeviceWithSuchName",
348"There is no iOS device with such name: {0}",
349device,
350),
351);
4cd25962JiglioNero4 years ago352}
353}
354
355const simulator = GeneralMobilePlatform.getOptFromRunArgs(
356this.runOptions.runArguments,
357"--simulator",
358);
359if (simulator) {
360const target = targets.find(
c76b733bJiglioNero4 years ago361target => target.isVirtualTarget && target.name === simulator,
4cd25962JiglioNero4 years ago362);
363if (target) {
364return IOSTarget.fromInterface(target);
c76b733bJiglioNero4 years ago365} else {
366this.logger.warning(
367localize(
368"ThereIsNoIosSimulatorWithSuchName",
369"There is no iOS simulator with such name: {0}",
370simulator,
371),
372);
4cd25962JiglioNero4 years ago373}
374}
375}
376
377return undefined;
378}
379
de838bbfJiglioNero6 years ago380private handleTargetArg(target: string): string[] {
4cd25962JiglioNero4 years ago381if (target === TargetType.Device || target === TargetType.Simulator) {
382return [`--${target}`];
de838bbfJiglioNero6 years ago383} else {
4cd25962JiglioNero4 years ago384return ["--udid", target];
de838bbfJiglioNero6 years ago385}
386}
387
0d77292aJiglioNero4 years ago388private async generateSuccessPatterns(version: string): Promise<string[]> {
3021756bYuri Skorokhodov6 years ago389// Clone RUN_IOS_SUCCESS_PATTERNS to avoid its runtime mutation
390let successPatterns = [...IOSPlatform.RUN_IOS_SUCCESS_PATTERNS];
4cd25962JiglioNero4 years ago391if (!(await this.getTarget()).isVirtualTarget) {
3021756bYuri Skorokhodov6 years ago392if (semver.gte(version, "0.60.0")) {
393successPatterns.push("success Installed the app on the device");
394} else {
395successPatterns.push("INSTALLATION SUCCEEDED");
396}
0d77292aJiglioNero4 years ago397return successPatterns;
3021756bYuri Skorokhodov6 years ago398} else {
0d77292aJiglioNero4 years ago399const bundleId = await this.getBundleId();
400if (semver.gte(version, "0.60.0")) {
401successPatterns.push(
402`Launching "${bundleId}"\nsuccess Successfully launched the app `,
403);
404} else {
405successPatterns.push(`Launching ${bundleId}\n${bundleId}: `);
406}
407return successPatterns;
3021756bYuri Skorokhodov6 years ago408}
8a67e140Artem Egorov8 years ago409}
410
db6fd42aRuslan Bikkinin7 years ago411private getConfiguration(): string {
34472878RedMickey5 years ago412return (
413IOSPlatform.getOptFromRunArgs(this.runArguments, this.configurationArgumentName) ||
414this.defaultConfiguration
415);
db6fd42aRuslan Bikkinin7 years ago416}
417
ce5e88eeYuri Skorokhodov5 years ago418private getBundleId(): Promise<string> {
116c3cb0Ruslan Bikkinin7 years ago419let scheme = this.runOptions.scheme;
420if (!scheme) {
34472878RedMickey5 years ago421const schemeFromArgs = IOSPlatform.getOptFromRunArgs(
422this.runArguments,
423"--scheme",
424false,
425);
116c3cb0Ruslan Bikkinin7 years ago426if (schemeFromArgs) {
427scheme = schemeFromArgs;
428}
429}
34472878RedMickey5 years ago430return this.plistBuddy.getBundleId(
431this.iosProjectRoot,
432this.projectPath,
1c2424f4RedMickey5 years ago433PlatformType.iOS,
34472878RedMickey5 years ago434true,
435this.runOptions.configuration,
436this.runOptions.productName,
437scheme,
438);
8a67e140Artem Egorov8 years ago439}
440}