Skip to content

Commit 2e7112f

Browse files
authored
Merge pull request #5058 from NativeScript/kddimitrov/cocoapods-version-override
feat: initial implementation of pods override
2 parents 72a4cd5 + 5bc58a7 commit 2e7112f

14 files changed

+161
-48
lines changed

lib/controllers/prepare-controller.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { hook } from "../common/helpers";
33
import { performanceLog, cache } from "../common/decorators";
44
import { EventEmitter } from "events";
55
import * as path from "path";
6-
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../constants";
6+
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter, CONFIG_NS_FILE_NAME } from "../constants";
77
interface IPlatformWatcherData {
88
hasWebpackCompilerProcess: boolean;
99
nativeFilesWatcher: choki.FSWatcher;
@@ -189,6 +189,7 @@ export class PrepareController extends EventEmitter {
189189

190190
const patterns = [
191191
path.join(projectData.projectDir, PACKAGE_JSON_FILE_NAME),
192+
path.join(projectData.projectDir, CONFIG_NS_FILE_NAME),
192193
path.join(projectData.getAppDirectoryPath(), PACKAGE_JSON_FILE_NAME),
193194
path.join(projectData.getAppResourcesRelativeDirectoryPath(), platformData.normalizedPlatformName),
194195
]

lib/definitions/platform.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ interface IPlatformsDataService {
4646
}
4747

4848
interface INodeModulesBuilder {
49-
prepareNodeModules(platformData: IPlatformData, projectData: IProjectData): Promise<void>;
49+
prepareNodeModules(prepareNodeModulesData: IPrepareNodeModulesData): Promise<void>;
50+
}
51+
52+
interface IPrepareNodeModulesData {
53+
platformData: IPlatformData;
54+
projectData: IProjectData;
5055
}
5156

5257
interface INodeModulesDependenciesBuilder {

lib/definitions/plugins.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ interface IPluginsService {
1212
* @returns {IPackageJsonDepedenciesResult}
1313
*/
1414
getDependenciesFromPackageJson(projectDir: string): IPackageJsonDepedenciesResult;
15-
preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void>;
15+
preparePluginNativeCode(preparePluginNativeCodeData: IPreparePluginNativeCodeData): Promise<void>;
1616
convertToPluginData(cacheData: any, projectDir: string): IPluginData;
1717
isNativeScriptPlugin(pluginPackageJsonPath: string): boolean;
1818
}
1919

20+
interface IPreparePluginNativeCodeData {
21+
pluginData: IPluginData;
22+
platform: string;
23+
projectData: IProjectData;
24+
}
25+
2026
interface IPackageJsonDepedenciesResult {
2127
dependencies: IBasePluginData[],
2228
devDependencies?: IBasePluginData[]

lib/definitions/project-changes.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface IProjectChangesInfo extends IAddedNativePlatform {
1717
configChanged: boolean;
1818
nativeChanged: boolean;
1919
signingChanged: boolean;
20+
nsConfigChanged: boolean;
2021

2122
readonly hasChanges: boolean;
2223
readonly changesRequireBuild: boolean;

lib/definitions/project.d.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ interface INsConfig {
7575
appResourcesPath?: string;
7676
shared?: boolean;
7777
previewAppSchema?: string;
78+
overridePods?: string
7879
}
7980

8081
interface IProjectData extends ICreateProjectData {
@@ -406,10 +407,10 @@ interface ICocoaPodsService {
406407
* @param {string} moduleName The module which the Podfile is from.
407408
* @param {string} podfilePath The path to the podfile.
408409
* @param {IProjectData} projectData Information about the project.
409-
* @param {string} nativeProjectPath Path to the native Xcode project.
410+
* @param {IPlatformData} platformData Information about the platform.
410411
* @returns {Promise<void>}
411412
*/
412-
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
413+
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void>;
413414

414415
/**
415416
* Gives the path to the plugin's Podfile.

lib/services/cocoapods-service.ts

+59-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { EOL } from "os";
22
import * as path from "path";
33
import { PluginNativeDirNames, PODFILE_NAME, NS_BASE_PODFILE } from "../constants";
4-
import { regExpEscape } from "../common/helpers";
4+
import { regExpEscape, getHash } from "../common/helpers";
55

66
export class CocoaPodsService implements ICocoaPodsService {
77
private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
88
private static INSTALLER_BLOCK_PARAMETER_NAME = "installer";
9-
9+
private getCocoaPodsFromPodfile: Function;
1010
constructor(
1111
private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager,
1212
private $fs: IFileSystem,
1313
private $childProcess: IChildProcess,
1414
private $errors: IErrors,
1515
private $logger: ILogger,
1616
private $config: IConfiguration,
17-
private $xcconfigService: IXcconfigService) { }
17+
private $xcconfigService: IXcconfigService) {
18+
this.getCocoaPodsFromPodfile = _.memoize(this._getCocoaPodsFromPodfile, getHash);
19+
}
1820

1921
public getPodfileHeader(targetName: string): string {
2022
return `use_frameworks!${EOL}${EOL}target "${targetName}" do${EOL}`;
@@ -35,7 +37,11 @@ export class CocoaPodsService implements ICocoaPodsService {
3537
const podInstallResult = await this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: projectRoot, stdio: ['pipe', process.stdout, process.stdout] }, { throwError: false });
3638

3739
if (podInstallResult.exitCode !== 0) {
38-
this.$errors.fail(`'${podTool} install' command failed.${podInstallResult.stderr ? " Error is: " + podInstallResult.stderr : ""}`);
40+
// https://github.com/CocoaPods/CocoaPods/blob/92aaf0f1120d32f3487960b485fb69fcaf61486c/lib/cocoapods/resolver.rb#L498
41+
// TODO add article
42+
const versionResolutionHint = podInstallResult.exitCode === 31 ? `For more information on resolving CocoaPod issues in NativeScript read.` : "";
43+
this.$errors.fail(`'${podTool} install' command failed.${podInstallResult.stderr ? " Error is: " + podInstallResult.stderr : ""}
44+
${versionResolutionHint}`);
3945
}
4046

4147
return podInstallResult;
@@ -59,17 +65,18 @@ export class CocoaPodsService implements ICocoaPodsService {
5965
const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, normalizedPlatformName, PODFILE_NAME);
6066
const projectPodfilePath = this.getProjectPodfilePath(projectRoot);
6167
if (this.$fs.exists(projectPodfilePath) || this.$fs.exists(mainPodfilePath)) {
62-
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, projectRoot);
68+
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, platformData);
6369
}
6470
}
6571

66-
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
72+
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void> {
73+
const nativeProjectPath = platformData.projectRoot;
6774
if (!this.$fs.exists(podfilePath)) {
6875
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
6976
return;
7077
}
7178

72-
const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName);
79+
const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName, projectData, platformData);
7380
const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath);
7481
const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : "";
7582

@@ -222,10 +229,16 @@ export class CocoaPodsService implements ICocoaPodsService {
222229
return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`;
223230
}
224231

225-
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
232+
private buildPodfileContent(pluginPodFilePath: string, pluginName: string, projectData: IProjectData, platformData: IPlatformData): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
226233
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
227234
const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
228-
const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
235+
const cocoapodsData = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
236+
const podfilePlatformData = cocoapodsData.podfilePlatformData;
237+
let replacedContent = cocoapodsData.replacedContent;
238+
239+
if (projectData.nsConfig && projectData.nsConfig.overridePods && !this.isMainPodFile(pluginPodFilePath, projectData, platformData)) {
240+
replacedContent = this.overridePodsFromFile(replacedContent, projectData, platformData);
241+
}
229242

230243
return {
231244
podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
@@ -234,6 +247,43 @@ export class CocoaPodsService implements ICocoaPodsService {
234247
};
235248
}
236249

250+
private getMainPodFilePath(projectData: IProjectData, platformData: IPlatformData): string {
251+
return path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName, PODFILE_NAME);
252+
}
253+
254+
private isMainPodFile(podFilePath: string, projectData: IProjectData, platformData: IPlatformData): boolean {
255+
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
256+
257+
return podFilePath === mainPodfilePath;
258+
}
259+
260+
private overridePodsFromFile(podfileContent: string, projectData: IProjectData, platformData: IPlatformData): string {
261+
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
262+
const mainPodfileContent = this.$fs.readText(mainPodfilePath);
263+
const pods = this.getCocoaPodsFromPodfile(mainPodfileContent);
264+
_.forEach(pods, pod => {
265+
podfileContent = podfileContent.replace(new RegExp(`^[ ]*pod\\s*["']${pod}['"].*$`, "gm"), '#$&');
266+
});
267+
268+
return podfileContent;
269+
}
270+
271+
private _getCocoaPodsFromPodfile(podfileContent: string): Array<string> {
272+
const pods = [];
273+
const podsRegex = /^\s*pod\s*["'](.*?)['"].*$/gm;
274+
275+
let match = podsRegex.exec(podfileContent);
276+
while (match != null) {
277+
const podName: string = match[1];
278+
if (podName) {
279+
pods.push(podName);
280+
}
281+
282+
match = podsRegex.exec(podfileContent);
283+
}
284+
285+
return pods;
286+
}
237287
}
238288

239289
$injector.register("cocoapodsService", CocoaPodsService);

lib/services/ios-project-service.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
495495
await this.prepareResources(pluginPlatformsFolderPath, pluginData, projectData);
496496
await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData, projectData);
497497
await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
498-
499-
const projectRoot = this.getPlatformData(projectData).projectRoot;
500-
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
501498
}
502499

503500
public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise<void> {
@@ -513,8 +510,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
513510

514511
public async handleNativeDependenciesChange(projectData: IProjectData, opts: IRelease): Promise<void> {
515512
const platformData = this.getPlatformData(projectData);
516-
513+
const pluginsData = await this.getAllInstalledPlugins(projectData);
517514
this.setProductBundleIdentifier(projectData);
515+
516+
await this.applyPluginsCocoaPods(pluginsData, projectData, platformData);
518517
await this.$cocoapodsService.applyPodfileFromAppResources(projectData, platformData);
519518

520519
const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot);
@@ -529,7 +528,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
529528

530529
const pbxProjPath = this.getPbxProjPath(projectData);
531530
this.$iOSExtensionsService.removeExtensions({ pbxProjPath });
532-
await this.addExtensions(projectData);
531+
await this.addExtensions(projectData, pluginsData);
533532
}
534533

535534
public beforePrepareAllPlugins(): Promise<void> {
@@ -642,18 +641,17 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
642641
this.savePbxProj(project, projectData);
643642
}
644643

645-
private async addExtensions(projectData: IProjectData): Promise<void> {
644+
private async addExtensions(projectData: IProjectData, pluginsData: IPluginData[]): Promise<void> {
646645
const resorcesExtensionsPath = path.join(
647646
projectData.getAppResourcesDirectoryPath(),
648647
this.getPlatformData(projectData).normalizedPlatformName, constants.NATIVE_EXTENSION_FOLDER
649648
);
650649
const platformData = this.getPlatformData(projectData);
651650
const pbxProjPath = this.getPbxProjPath(projectData);
652651
const addedExtensionsFromResources = await this.$iOSExtensionsService.addExtensionsFromPath({ extensionsFolderPath: resorcesExtensionsPath, projectData, platformData, pbxProjPath });
653-
const plugins = await this.getAllInstalledPlugins(projectData);
654652
let addedExtensionsFromPlugins = false;
655-
for (const pluginIndex in plugins) {
656-
const pluginData = plugins[pluginIndex];
653+
for (const pluginIndex in pluginsData) {
654+
const pluginData = pluginsData[pluginIndex];
657655
const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
658656

659657
const extensionPath = path.join(pluginPlatformsFolderPath, constants.NATIVE_EXTENSION_FOLDER);
@@ -829,6 +827,15 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
829827
this.$logger.warn("[WARNING]: The CFBundleIdentifier key inside the 'Info.plist' will be overriden by the 'id' inside 'package.json'.");
830828
}
831829
}
830+
831+
private async applyPluginsCocoaPods(pluginsData: IPluginData[], projectData: IProjectData, platformData: IPlatformData) {
832+
for (const pluginIndex in pluginsData) {
833+
const pluginData = pluginsData[pluginIndex];
834+
if (this.$fs.exists(pluginData.pluginPlatformsFolderPath(platformData.normalizedPlatformName))) {
835+
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, platformData);
836+
}
837+
}
838+
}
832839
}
833840

834841
$injector.register("iOSProjectService", IOSProjectService);

lib/services/platform/prepare-native-platform-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi
3737
}
3838

3939
if (hasNativeModulesChange) {
40-
await this.$nodeModulesBuilder.prepareNodeModules(platformData, projectData);
40+
await this.$nodeModulesBuilder.prepareNodeModules({platformData, projectData});
4141
}
4242

4343
if (hasNativeModulesChange || hasConfigChange) {

lib/services/plugins-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class PluginsService implements IPluginsService {
114114
}
115115
}
116116

117-
public async preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void> {
117+
public async preparePluginNativeCode({pluginData, platform, projectData}: IPreparePluginNativeCodeData): Promise<void> {
118118
const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
119119
pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform.toLowerCase());
120120

lib/services/project-changes-service.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from "path";
2-
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME } from "../constants";
2+
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME, CONFIG_NS_FILE_NAME } from "../constants";
33
import { getHash, hook } from "../common/helpers";
44

55
const prepareInfoFileName = ".nsprepareinfo";
@@ -8,6 +8,7 @@ class ProjectChangesInfo implements IProjectChangesInfo {
88

99
public appResourcesChanged: boolean;
1010
public configChanged: boolean;
11+
public nsConfigChanged: boolean;
1112
public nativeChanged: boolean;
1213
public signingChanged: boolean;
1314
public nativePlatformStatus: NativePlatformStatus;
@@ -70,6 +71,10 @@ export class ProjectChangesService implements IProjectChangesService {
7071
this._changesInfo.nativeChanged = this.isProjectFileChanged(projectData.projectDir, platformData);
7172
}
7273

74+
// If this causes too much rebuilds of the plugins or uncecessary builds for Android, move overrideCocoapods to prepareInfo.
75+
this._changesInfo.nsConfigChanged = this.filesChanged([path.join(projectData.projectDir, CONFIG_NS_FILE_NAME)]);
76+
this._changesInfo.nativeChanged = this._changesInfo.nativeChanged || this._changesInfo.nsConfigChanged;
77+
7378
this.$logger.trace(`Set nativeChanged to ${this._changesInfo.nativeChanged}.`);
7479

7580
if (platformData.platformNameLowerCase === this.$devicePlatformsConstants.iOS.toLowerCase()) {
@@ -183,6 +188,7 @@ export class ProjectChangesService implements IProjectChangesService {
183188
this._changesInfo.appResourcesChanged = true;
184189
this._changesInfo.configChanged = true;
185190
this._changesInfo.nativeChanged = true;
191+
this._changesInfo.nsConfigChanged = true;
186192
return true;
187193
}
188194

lib/tools/node-modules/node-modules-builder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
55
private $pluginsService: IPluginsService
66
) { }
77

8-
public async prepareNodeModules(platformData: IPlatformData, projectData: IProjectData): Promise<void> {
8+
public async prepareNodeModules({platformData , projectData}: IPrepareNodeModulesData): Promise<void> {
99
const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir);
1010
if (_.isEmpty(dependencies)) {
1111
return;
@@ -19,7 +19,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
1919
if (isPlugin) {
2020
this.$logger.debug(`Successfully prepared plugin ${dependency.name} for ${platformData.normalizedPlatformName.toLowerCase()}.`);
2121
const pluginData = this.$pluginsService.convertToPluginData(dependency, projectData.projectDir);
22-
await this.$pluginsService.preparePluginNativeCode(pluginData, platformData.normalizedPlatformName.toLowerCase(), projectData);
22+
await this.$pluginsService.preparePluginNativeCode({pluginData, platform: platformData.normalizedPlatformName.toLowerCase(), projectData});
2323
}
2424
}
2525
}

0 commit comments

Comments
 (0)