Skip to content

fix: remove duplicated platforms from project's podfile #4332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ $injector.require("iOSProvisionService", "./services/ios-provision-service");
$injector.require("xcconfigService", "./services/xcconfig-service");

$injector.require("cocoapodsService", "./services/cocoapods-service");
$injector.require("cocoaPodsPlatformManager", "./services/cocoapods-platform-manager");

$injector.require("projectTemplatesService", "./services/project-templates-service");
$injector.require("projectNameService", "./services/project-name-service");
Expand Down
21 changes: 21 additions & 0 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,28 @@ interface ICocoaPodsService {
mergePodXcconfigFile(projectData: IProjectData, platformData: IPlatformData, opts: IRelease): Promise<void>;
}

interface ICocoaPodsPlatformManager {
addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string;
removePlatformSection(moduleName: string, projectPodFileContent: string, podfilePath: string): string;
replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData };
}

interface IRubyFunction {
functionName: string;
functionParameters?: string;
}

interface IPodfilePlatformData {
/**
* The content of the whole pod's platform row
*/
content: string;
/**
* The version of the pod's platform
*/
version: string;
/**
* The path to the pod's file
*/
path: string;
}
130 changes: 130 additions & 0 deletions lib/services/cocoapods-platform-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { EOL } from "os";
import * as path from "path";
import * as semver from "semver";
import { PODFILE_NAME } from "../constants";

export class CocoaPodsPlatformManager implements ICocoaPodsPlatformManager {
public addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string {
const platformSectionData = this.getPlatformSectionData(projectPodfileContent);
if (platformSectionData && platformSectionData.podfilePlatformData) {
const shouldReplacePlatformSection = this.shouldReplacePlatformSection(projectData, platformSectionData.podfilePlatformData, podfilePlatformData);
if (shouldReplacePlatformSection) {
const newSection = this.buildPlatformSection(podfilePlatformData);
projectPodfileContent = projectPodfileContent.replace(platformSectionData.platformSectionContent, newSection);
}
} else {
projectPodfileContent += this.buildPlatformSection(podfilePlatformData);
}

return projectPodfileContent;
}

public removePlatformSection(moduleName: string, projectPodfileContent: string, podfilePath: string): string {
const platformSectionData = this.getPlatformSectionData(projectPodfileContent);
if (platformSectionData && platformSectionData.podfilePlatformData && platformSectionData.podfilePlatformData.path === podfilePath) {
const podfileContentRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)# End Podfile`, "mg");
const allPodfiles = projectPodfileContent.match(podfileContentRegExp) || [];
const selectedPlatformData = this.selectPlatformDataFromProjectPodfile(allPodfiles);
const newPlatformSection = selectedPlatformData ? this.buildPlatformSection(selectedPlatformData) : "";
const regExp = new RegExp(`\\r?\\n${platformSectionData.platformSectionContent}\\r?\\n`, "mg");
projectPodfileContent = projectPodfileContent.replace(regExp, newPlatformSection);
}

return projectPodfileContent;
}

public replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData } {
let podfilePlatformData: IPodfilePlatformData = null;
const platformRowRegExp = new RegExp(`^\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "gm");
const replacedContent = podfileContent.replace(platformRowRegExp, (substring: string, firstGroup: string, secondGroup: string) => {
podfilePlatformData = { content: firstGroup, version: secondGroup, path: podfilePath };
return `# ${substring.trim()}`;
});

return { replacedContent, podfilePlatformData };
}

private getPlatformSectionData(projectPodfileContent: string): { podfilePlatformData: IPodfilePlatformData, platformSectionContent: string } {
const platformSectionRegExp = new RegExp(`${this.getPlatformSectionHeader()} ([\\s\\S]*?)with (.*)[\\s\\S]*?${this.getPlatformSectionFooter()}`, "m");
const match = platformSectionRegExp.exec(projectPodfileContent);
let result = null;
if (match && match[0]) {
result = {
platformSectionContent: match[0],
podfilePlatformData: {
path: match[1].trim(),
content: "",
version: match[2]
}
};
}

return result;
}

private selectPlatformDataFromProjectPodfile(allPodfiles: string[]): IPodfilePlatformData {
const platformRowRegExp = new RegExp(`^\\s*?#\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "m");
const podfilePathRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)${EOL}`);
let selectedPlatformData: IPodfilePlatformData = null;
_.each(allPodfiles, podfileContent => {
const platformMatch = platformRowRegExp.exec(podfileContent);
const podfilePathMatch: any[] = podfilePathRegExp.exec(podfileContent) || [];
// platform without version -> select it with highest priority
if (platformMatch && platformMatch[0] && !platformMatch[2]) {
selectedPlatformData = {
version: null,
content: platformMatch[1],
path: podfilePathMatch[1]
};
return false;
}

// platform with version
if (platformMatch && platformMatch[0] && platformMatch[2]) {
if (!selectedPlatformData || semver.gt(semver.coerce(platformMatch[2]), semver.coerce(selectedPlatformData.version))) {
selectedPlatformData = {
version: platformMatch[2],
content: platformMatch[1],
path: podfilePathMatch[1]
};
}
}
});

return selectedPlatformData;
}

private shouldReplacePlatformSection(projectData: IProjectData, oldPodfilePlatformData: IPodfilePlatformData, currentPodfilePlatformData: IPodfilePlatformData): boolean {
// The selected platform should be replaced in the following cases:
// 1. When the pod file is from App_Resources and the selected platform is not from App_Resources
// 2. When the pod file is from App_Resources, the selected platform is also from App_Resources
// and the pod's version is greater than the selected one
// 3. When the pod file doesn't have platform's version -> `platform :ios`
// 4. When the pod file has a version greater than the selected platform's version
const appResourcesPodfilePath = path.join(projectData.getAppResourcesDirectoryPath(), "iOS", PODFILE_NAME);
const isFromAppResources = oldPodfilePlatformData.path !== appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath;
const isFromAppResourcesWithGreaterPlatformVersion = oldPodfilePlatformData.path === appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath && semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version));
const isPodfileWithGreaterPlatformVersion = !currentPodfilePlatformData.version || semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version));
const result = isFromAppResources || isFromAppResourcesWithGreaterPlatformVersion || isPodfileWithGreaterPlatformVersion;
return result;
}

private buildPlatformSection(podfilePlatformData: IPodfilePlatformData) {
let result = `${this.getPlatformSectionHeader()} ${podfilePlatformData.path} with`;
if (podfilePlatformData.version) {
result += ` ${podfilePlatformData.version}`;
}

result += `${EOL}${podfilePlatformData.content}${EOL}${this.getPlatformSectionFooter()}`;
return result;
}

private getPlatformSectionHeader(): string {
return '# NativeScriptPlatformSection';
}

private getPlatformSectionFooter(): string {
return '# End NativeScriptPlatformSection';
}
}
$injector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager);
20 changes: 14 additions & 6 deletions lib/services/cocoapods-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export class CocoaPodsService implements ICocoaPodsService {
private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
private static INSTALLER_BLOCK_PARAMETER_NAME = "installer";

constructor(private $fs: IFileSystem,
constructor(
private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager,
private $fs: IFileSystem,
private $childProcess: IChildProcess,
private $errors: IErrors,
private $xcprojService: IXcprojService,
Expand Down Expand Up @@ -79,7 +81,7 @@ export class CocoaPodsService implements ICocoaPodsService {
return;
}

const { podfileContent, replacedFunctions } = this.buildPodfileContent(podfilePath, moduleName);
const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName);
const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath);
const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : "";

Expand All @@ -92,19 +94,23 @@ export class CocoaPodsService implements ICocoaPodsService {
finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent);
}

if (podfilePlatformData) {
finalPodfileContent = this.$cocoaPodsPlatformManager.addPlatformSection(projectData, podfilePlatformData, finalPodfileContent);
}

finalPodfileContent = `${podfileContent}${EOL}${finalPodfileContent}`;
this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath);
}
}

public removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, projectRoot: string): void {

if (this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
let projectPodFileContent = this.$fs.readText(this.getProjectPodfilePath(projectRoot));
// Remove the data between #Begin Podfile and #EndPodfile
const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(podfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg");
projectPodFileContent = projectPodFileContent.replace(regExpToRemove, "");
projectPodFileContent = this.removePostInstallHook(moduleName, projectPodFileContent);
projectPodFileContent = this.$cocoaPodsPlatformManager.removePlatformSection(moduleName, projectPodFileContent, podfilePath);

const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName);
const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${EOL}${this.getPostInstallHookHeader()}end${EOL}end`;
Expand Down Expand Up @@ -226,13 +232,15 @@ export class CocoaPodsService implements ICocoaPodsService {
return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`;
}

private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[] } {
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
const { replacedContent, newFunctions: replacedFunctions } = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);

return {
podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
replacedFunctions
replacedFunctions: data.newFunctions,
podfilePlatformData
};
}

Expand Down
2 changes: 1 addition & 1 deletion lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot);
if (this.$fs.exists(projectPodfilePath)) {
await this.$cocoapodsService.executePodInstall(platformData.projectRoot, this.$xcprojService.getXcodeprojPath(projectData, platformData));
// The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to xcodebuild.
// The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to Xcode project.
// Some of these phases relies on env variables (like PODS_PODFILE_DIR_PATH or PODS_ROOT).
// These variables are produced from merge of pod's xcconfig file and project's xcconfig file.
// So the correct order is `pod install` to be executed before merging pod's xcconfig file.
Expand Down
Loading