microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
transitive-dependency-serialize-javascript

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/ios/plistBuddy.ts

355lines · modeblame

0a68f8dbArtem 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.
bad42581EzioLi3 years ago3
0a68f8dbArtem Egorov8 years ago4import * as path from "path";
a949e43fRuslan Bikkinin7 years ago5import * as fs from "fs";
09f6024fHeniker4 years ago6import * as glob from "glob";
a949e43fRuslan Bikkinin7 years ago7import * as semver from "semver";
b3753d4eRedMickey6 years ago8import { Node } from "../../common/node/node";
9import { ChildProcess } from "../../common/node/childProcess";
d7d405aeYuri Skorokhodov7 years ago10import { ErrorHelper } from "../../common/error/errorHelper";
11import { InternalErrorCode } from "../../common/error/internalErrorCode";
e3706a1cRedMickey6 years ago12import { ProjectVersionHelper } from "../../common/projectVersionHelper";
de838bbfJiglioNero6 years ago13import { getFileNameWithoutExtension } from "../../common/utils";
2d8af448Yuri Skorokhodov6 years ago14import customRequire from "../../common/customRequire";
1c2424f4RedMickey5 years ago15import { PlatformType } from "../launchArgs";
4dfb1c4cetatanova5 years ago16import { AppLauncher } from "../appLauncher";
7a4506dclexie0111 years ago17import { SettingsHelper } from "../settingsHelper";
de838bbfJiglioNero6 years ago18
19export interface ConfigurationData {
20fullProductName: string;
21configurationFolder: string;
22}
1c2424f4RedMickey5 years ago23export interface IOSBuildLocationData {
24executable: string;
25configurationFolder: string;
26}
0a68f8dbArtem Egorov8 years ago27export class PlistBuddy {
47927908RedMickey4 years ago28private static readonly plistBuddyExecutable = "/usr/libexec/PlistBuddy";
29private static readonly SCHEME_IN_PRODUCTS_FOLDER_PATH_VERSION = "0.59.0";
30private static readonly NEW_RN_IOS_CLI_LOCATION_VERSION = "0.60.0";
8f9463c2Samriel3 years ago31private static readonly RN69_FUND_XCODE_PROJECT_LOCATION_VERSION = "0.69.0";
4c1a7831lexie0112 years ago32private static readonly RN_VERSION_CLI_PLATFORM_APPLE = "0.74.0";
1bac57dblexie0111 years ago33private static readonly RN_VERSION_CLI_CONFIG_APPLE = "0.76.2";
de838bbfJiglioNero6 years ago34private readonly TARGET_BUILD_DIR_SEARCH_KEY = "TARGET_BUILD_DIR";
35private readonly FULL_PRODUCT_NAME_SEARCH_KEY = "FULL_PRODUCT_NAME";
0a68f8dbArtem Egorov8 years ago36private nodeChildProcess: ChildProcess;
34472878RedMickey5 years ago37constructor({ nodeChildProcess = new Node.ChildProcess() } = {}) {
0a68f8dbArtem Egorov8 years ago38this.nodeChildProcess = nodeChildProcess;
39}
0d77292aJiglioNero4 years ago40public async getBundleId(
1c2424f4RedMickey5 years ago41platformProjectRoot: string,
34472878RedMickey5 years ago42projectRoot: string,
1c2424f4RedMickey5 years ago43platform: PlatformType.iOS | PlatformType.macOS,
34472878RedMickey5 years ago44simulator: boolean = true,
45configuration: string = "Debug",
46productName?: string,
47scheme?: string,
48): Promise<string> {
0d77292aJiglioNero4 years ago49const iOSBuildLocationData = await this.getExecutableAndConfigurationFolder(
1c2424f4RedMickey5 years ago50platformProjectRoot,
51projectRoot,
52platform,
53simulator,
54configuration,
55productName,
56scheme,
0d77292aJiglioNero4 years ago57);
58const infoPlistPath = path.join(
59iOSBuildLocationData.configurationFolder,
60iOSBuildLocationData.executable,
61platform === PlatformType.iOS ? "Info.plist" : path.join("Contents", "Info.plist"),
62);
63return await this.invokePlistBuddy("Print:CFBundleIdentifier", infoPlistPath);
1c2424f4RedMickey5 years ago64}
0d77292aJiglioNero4 years ago65public async getExecutableAndConfigurationFolder(
1c2424f4RedMickey5 years ago66platformProjectRoot: string,
67projectRoot: string,
68platform: PlatformType.iOS | PlatformType.macOS,
69simulator: boolean = true,
70configuration: string = "Debug",
71productName?: string,
72scheme?: string,
73): Promise<IOSBuildLocationData> {
0d77292aJiglioNero4 years ago74const rnVersions = await ProjectVersionHelper.getReactNativeVersions(projectRoot);
75let productsFolder;
47927908RedMickey4 years ago76if (
77semver.gte(
78rnVersions.reactNativeVersion,
79PlistBuddy.SCHEME_IN_PRODUCTS_FOLDER_PATH_VERSION,
80) ||
81ProjectVersionHelper.isCanaryVersion(rnVersions.reactNativeVersion)
82) {
0d77292aJiglioNero4 years ago83if (!scheme) {
84// If no scheme were provided via runOptions.scheme or via runArguments then try to get scheme using the way RN CLI does.
85scheme = this.getInferredScheme(
86platformProjectRoot,
87projectRoot,
88rnVersions.reactNativeVersion,
89);
90if (platform === PlatformType.macOS) {
09f6024fHeniker4 years ago91scheme = `${scheme}-macOS`;
a949e43fRuslan Bikkinin7 years ago92}
0d77292aJiglioNero4 years ago93}
94productsFolder = path.join(platformProjectRoot, "build", scheme, "Build", "Products");
95} else {
96productsFolder = path.join(platformProjectRoot, "build", "Build", "Products");
97}
09f6024fHeniker4 years ago98const sdkType =
0d77292aJiglioNero4 years ago99platform === PlatformType.iOS ? this.getSdkType(simulator, scheme) : undefined;
100let configurationFolder = path.join(
101productsFolder,
102`${configuration}${sdkType ? `-${sdkType}` : ""}`,
103);
104let executable = "";
105if (productName) {
106executable = `${productName}.app`;
107if (!fs.existsSync(path.join(configurationFolder, executable))) {
108const configurationData = this.getConfigurationData(
109projectRoot,
110rnVersions.reactNativeVersion,
1c2424f4RedMickey5 years ago111platformProjectRoot,
0d77292aJiglioNero4 years ago112configuration,
1c2424f4RedMickey5 years ago113scheme,
0d77292aJiglioNero4 years ago114configurationFolder,
115sdkType,
1c2424f4RedMickey5 years ago116);
0d77292aJiglioNero4 years ago117configurationFolder = configurationData.configurationFolder;
db6fd42aRuslan Bikkinin7 years ago118}
0d77292aJiglioNero4 years ago119} else {
120const executableList = this.findExecutable(configurationFolder);
121if (!executableList.length) {
122const configurationData_1 = this.getConfigurationData(
123projectRoot,
124rnVersions.reactNativeVersion,
125platformProjectRoot,
126configuration,
127scheme,
128configurationFolder,
129sdkType,
130);
131configurationFolder = configurationData_1.configurationFolder;
132executableList.push(configurationData_1.fullProductName);
133} else if (executableList.length > 1) {
134throw ErrorHelper.getInternalError(
135InternalErrorCode.IOSFoundMoreThanOneExecutablesCleanupBuildFolder,
136configurationFolder,
137);
138}
139executable = `${executableList[0]}`;
140}
141return {
142executable,
143configurationFolder,
144};
0a68f8dbArtem Egorov8 years ago145}
0d77292aJiglioNero4 years ago146public async setPlistProperty(
147plistFile: string,
148property: string,
149value: string,
150): Promise<void> {
0a68f8dbArtem Egorov8 years ago151// Attempt to set the value, and if it fails due to the key not existing attempt to create the key
0d77292aJiglioNero4 years ago152try {
153await this.invokePlistBuddy(`Set ${property} ${value}`, plistFile);
154} catch (e) {
155await this.invokePlistBuddy(`Add ${property} string ${value}`, plistFile);
156}
0a68f8dbArtem Egorov8 years ago157}
0d77292aJiglioNero4 years ago158public async setPlistBooleanProperty(
34472878RedMickey5 years ago159plistFile: string,
160property: string,
161value: boolean,
162): Promise<void> {
0a68f8dbArtem Egorov8 years ago163// Attempt to set the value, and if it fails due to the key not existing attempt to create the key
0d77292aJiglioNero4 years ago164try {
09f6024fHeniker4 years ago165await this.invokePlistBuddy(`Set ${property} ${String(value)}`, plistFile);
0d77292aJiglioNero4 years ago166} catch (e) {
09f6024fHeniker4 years ago167await this.invokePlistBuddy(`Add ${property} bool ${String(value)}`, plistFile);
0d77292aJiglioNero4 years ago168}
0a68f8dbArtem Egorov8 years ago169}
0d77292aJiglioNero4 years ago170public async deletePlistProperty(plistFile: string, property: string): Promise<void> {
171try {
172await this.invokePlistBuddy(`Delete ${property}`, plistFile);
173} catch (err) {
174if (!err.toString().toLowerCase().includes("does not exist")) {
175throw err;
176}
177}
0a68f8dbArtem Egorov8 years ago178}
ce5e88eeYuri Skorokhodov5 years ago179public readPlistProperty(plistFile: string, property: string): Promise<string> {
0a68f8dbArtem Egorov8 years ago180return this.invokePlistBuddy(`Print ${property}`, plistFile);
181}
de838bbfJiglioNero6 years ago182public getBuildPathAndProductName(
1c2424f4RedMickey5 years ago183platformProjectRoot: string,
b3753d4eRedMickey6 years ago184projectWorkspaceConfigName: string,
185configuration: string,
186scheme: string,
1c2424f4RedMickey5 years ago187sdkType?: string,
de838bbfJiglioNero6 years ago188): ConfigurationData {
1c2424f4RedMickey5 years ago189const xcodebuildParams = ["-workspace", projectWorkspaceConfigName, "-scheme", scheme];
190if (sdkType) {
191xcodebuildParams.push("-sdk", sdkType);
192}
193xcodebuildParams.push("-configuration", configuration, "-showBuildSettings");
194const buildSettings = this.nodeChildProcess.execFileSync("xcodebuild", xcodebuildParams, {
195encoding: "utf8",
196cwd: platformProjectRoot,
197});
34472878RedMickey5 years ago198const targetBuildDir = this.fetchParameterFromBuildSettings(
199<string>buildSettings,
200this.TARGET_BUILD_DIR_SEARCH_KEY,
201);
202const fullProductName = this.fetchParameterFromBuildSettings(
203<string>buildSettings,
204this.FULL_PRODUCT_NAME_SEARCH_KEY,
205);
b3753d4eRedMickey6 years ago206if (!targetBuildDir) {
207throw new Error("Failed to get the target build directory.");
208}
de838bbfJiglioNero6 years ago209if (!fullProductName) {
210throw new Error("Failed to get full product name.");
211}
212return {
213fullProductName,
214configurationFolder: targetBuildDir,
215};
b3753d4eRedMickey6 years ago216}
34472878RedMickey5 years ago217public getInferredScheme(
1c2424f4RedMickey5 years ago218platformProjectRoot: string,
34472878RedMickey5 years ago219projectRoot: string,
220rnVersion: string,
221): string {
222const projectWorkspaceConfigName = this.getProjectWorkspaceConfigName(
1c2424f4RedMickey5 years ago223platformProjectRoot,
34472878RedMickey5 years ago224projectRoot,
225rnVersion,
226);
de838bbfJiglioNero6 years ago227return getFileNameWithoutExtension(projectWorkspaceConfigName);
228}
cc826bdaRedMickey5 years ago229public getSdkType(simulator: boolean, scheme?: string): string {
9cde42d9Michele Bonazza5 years ago230const sdkSuffix = simulator ? "simulator" : "os";
231const deviceType =
232(scheme?.toLowerCase().indexOf("tvos") ?? -1) > -1 ? "appletv" : "iphone";
233return `${deviceType}${sdkSuffix}`;
234}
34472878RedMickey5 years ago235public getProjectWorkspaceConfigName(
1c2424f4RedMickey5 years ago236platformProjectRoot: string,
34472878RedMickey5 years ago237projectRoot: string,
238rnVersion: string,
239): string {
3021756bYuri Skorokhodov6 years ago240// Portion of code was taken from https://github.com/react-native-community/cli/blob/master/packages/platform-ios/src/commands/runIOS/index.js
a949e43fRuslan Bikkinin7 years ago241// and modified a little bit
242/**
243* Copyright (c) Facebook, Inc. and its affiliates.
244*
245* This source code is licensed under the MIT license found in the
246* LICENSE file in the root directory of this source tree.
247*
248* @flow
249* @format
250*/
4c1a7831lexie0112 years ago251
01f9e3d8lexie0111 years ago252const nodeModulesRoot: string = AppLauncher.getNodeModulesRootByProjectPath(projectRoot);
253
4c1a7831lexie0112 years ago254const iOSCliPlatform = semver.gte(rnVersion, PlistBuddy.RN_VERSION_CLI_PLATFORM_APPLE)
1bac57dblexie0111 years ago255? semver.gte(rnVersion, PlistBuddy.RN_VERSION_CLI_CONFIG_APPLE)
256? "cli-config-apple"
257: "cli-platform-apple"
4c1a7831lexie0112 years ago258: "cli-platform-ios";
47927908RedMickey4 years ago259const iOSCliFolderName =
260semver.gte(rnVersion, PlistBuddy.NEW_RN_IOS_CLI_LOCATION_VERSION) ||
261ProjectVersionHelper.isCanaryVersion(rnVersion)
4c1a7831lexie0112 years ago262? iOSCliPlatform
47927908RedMickey4 years ago263: "cli";
01f9e3d8lexie0111 years ago264
265let findXcodeBase = "node_modules/@react-native-community";
266
267const pnpmProjectPath = path.resolve(nodeModulesRoot, "node_modules", ".pnpm");
268
269const isPnpmProject =
270fs.existsSync(pnpmProjectPath) && SettingsHelper.getPackageManager() === "pnpm";
271if (isPnpmProject) {
272const modules = fs.readdirSync(pnpmProjectPath);
273const regex = new RegExp(`\@react-native-community\\+${iOSCliFolderName}@`);
274const communityModule = modules.find(module => regex.test(module));
275if (communityModule) {
276findXcodeBase = path.join(
277"node_modules",
278".pnpm",
279communityModule,
280"node_modules",
281"@react-native-community",
282);
283}
284}
285
286const findXcodeProjectLocation = `${findXcodeBase}/${iOSCliFolderName}/build/${
8f9463c2Samriel3 years ago287semver.gte(rnVersion, PlistBuddy.RN69_FUND_XCODE_PROJECT_LOCATION_VERSION)
288? "config/findXcodeProject"
289: "commands/runIOS/findXcodeProject"
290}`;
01f9e3d8lexie0111 years ago291
34472878RedMickey5 years ago292const findXcodeProject = customRequire(
01f9e3d8lexie0111 years ago293path.join(nodeModulesRoot, findXcodeProjectLocation),
34472878RedMickey5 years ago294).default;
1c2424f4RedMickey5 years ago295const xcodeProject = findXcodeProject(fs.readdirSync(platformProjectRoot));
a949e43fRuslan Bikkinin7 years ago296if (!xcodeProject) {
1c2424f4RedMickey5 years ago297throw new Error(
298`Could not find Xcode project files in "${platformProjectRoot}" folder`,
299);
a949e43fRuslan Bikkinin7 years ago300}
de838bbfJiglioNero6 years ago301return xcodeProject.name;
302}
303public getConfigurationData(
304projectRoot: string,
305reactNativeVersion: string,
1c2424f4RedMickey5 years ago306platformProjectRoot: string,
de838bbfJiglioNero6 years ago307configuration: string,
308scheme: string | undefined,
34472878RedMickey5 years ago309oldConfigurationFolder: string,
1c2424f4RedMickey5 years ago310sdkType?: string,
de838bbfJiglioNero6 years ago311): ConfigurationData {
312if (!scheme) {
34472878RedMickey5 years ago313throw ErrorHelper.getInternalError(
314InternalErrorCode.IOSCouldNotFoundExecutableInFolder,
315oldConfigurationFolder,
316);
de838bbfJiglioNero6 years ago317}
34472878RedMickey5 years ago318const projectWorkspaceConfigName = this.getProjectWorkspaceConfigName(
1c2424f4RedMickey5 years ago319platformProjectRoot,
34472878RedMickey5 years ago320projectRoot,
321reactNativeVersion,
322);
de838bbfJiglioNero6 years ago323return this.getBuildPathAndProductName(
1c2424f4RedMickey5 years ago324platformProjectRoot,
de838bbfJiglioNero6 years ago325projectWorkspaceConfigName,
326configuration,
327scheme,
34472878RedMickey5 years ago328sdkType,
a949e43fRuslan Bikkinin7 years ago329);
330}
b3753d4eRedMickey6 years ago331/**
332* @param {string} buildSettings
de838bbfJiglioNero6 years ago333* @param {string} parameterName
b3753d4eRedMickey6 years ago334* @returns {string | null}
335*/
34472878RedMickey5 years ago336public fetchParameterFromBuildSettings(
337buildSettings: string,
338parameterName: string,
339): string | null {
de838bbfJiglioNero6 years ago340const targetBuildMatch = new RegExp(`${parameterName} = (.+)$`, "m").exec(buildSettings);
34472878RedMickey5 years ago341return targetBuildMatch && targetBuildMatch[1] ? targetBuildMatch[1].trim() : null;
b3753d4eRedMickey6 years ago342}
db6fd42aRuslan Bikkinin7 years ago343private findExecutable(folder: string): string[] {
344return glob.sync("*.app", {
345cwd: folder,
346});
347}
0d77292aJiglioNero4 years ago348private async invokePlistBuddy(command: string, plistFile: string): Promise<string> {
349const res = await this.nodeChildProcess.exec(
350`${PlistBuddy.plistBuddyExecutable} -c '${command}' '${plistFile}'`,
351);
352const outcome = await res.outcome;
353return outcome.toString().trim();
0a68f8dbArtem Egorov8 years ago354}
355}