Skip to content

Native source code and a Podfile without a plugin #4282

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 9 commits into from
Jan 17, 2019
1 change: 1 addition & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require("colors");
export const APP_FOLDER_NAME = "app";
export const APP_RESOURCES_FOLDER_NAME = "App_Resources";
export const PROJECT_FRAMEWORK_FOLDER_NAME = "framework";
export const NS_BASE_PODFILE = "NSPodfileBase";
export const NATIVESCRIPT_KEY_NAME = "nativescript";
export const NODE_MODULES_FOLDER_NAME = "node_modules";
export const TNS_MODULES_FOLDER_NAME = "tns_modules";
Expand Down
24 changes: 17 additions & 7 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ interface IProjectData extends ICreateProjectData {
gradleFilesDirectoryPath: string;
infoPlistPath: string;
buildXcconfigPath: string;
podfilePath: string;
/**
* Defines if the project is a code sharing one.
* Value is true when project has nsconfig.json and it has `shared: true` in it.
Expand Down Expand Up @@ -409,7 +410,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
getAppResourcesDestinationDirectoryPath(projectData: IProjectData): string;

cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise<void>;
processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise<void>;
processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData, installPods: boolean): Promise<void>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processConfigurationFilesFromAppResources(projectData: IProjectData, opts: { release: boolean,  installPods: boolean }): Promise<void>;


/**
* Ensures there is configuration file (AndroidManifest.xml, Info.plist) in app/App_Resources.
Expand Down Expand Up @@ -449,7 +450,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
* Traverse through the production dependencies and find plugins that need build/rebuild
*/
checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<any>>;

/**
* Get the deployment target's version
* Currently implemented only for iOS -> returns the value of IPHONEOS_DEPLOYMENT_TARGET property from xcconfig file
Expand Down Expand Up @@ -484,21 +485,30 @@ interface ICocoaPodsService {

/**
* Prepares the Podfile content of a plugin and merges it in the project's Podfile.
* @param {IPluginData} pluginData Information about the plugin.
* @param {string} moduleName The module which the Podfile is from.
* @param {string} podfilePath The path to the podfile.
* @param {IProjectData} projectData Information about the project.
* @param {string} nativeProjectPath Path to the native Xcode project.
* @returns {Promise<void>}
*/
applyPluginPodfileToProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void>;

/**
* Removes plugins Podfile content from the project.
* Gives the path to the plugin's Podfile.
* @param {IPluginData} pluginData Information about the plugin.
* @param {IProjectData} projectData Information about the project.
* @returns {string} Path to plugin's Podfile
*/
getPluginPodfilePath(pluginData: IPluginData): string;

/**
* Removes plugins Podfile content from the project.
* @param {string} moduleName The name of the module.
* @param {string} podfilePath The path to the module's Podfile.
* @param {string} projectData Information about the project.
* @param {string} nativeProjectPath Path to the native Xcode project.
* @returns {void}
*/
removePluginPodfileFromProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): void;
removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): void;

/**
* Gives the path to project's Podfile.
Expand Down
2 changes: 2 additions & 0 deletions lib/project-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class ProjectData implements IProjectData {
public appGradlePath: string;
public gradleFilesDirectoryPath: string;
public buildXcconfigPath: string;
public podfilePath: string;
public isShared: boolean;

constructor(private $fs: IFileSystem,
Expand Down Expand Up @@ -132,6 +133,7 @@ export class ProjectData implements IProjectData {
this.appGradlePath = path.join(this.gradleFilesDirectoryPath, constants.APP_GRADLE_FILE_NAME);
this.infoPlistPath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.INFO_PLIST_FILE_NAME);
this.buildXcconfigPath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.BUILD_XCCONFIG_FILE_NAME);
this.podfilePath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.PODFILE_NAME);
this.isShared = !!(this.nsConfig && this.nsConfig.shared);
return;
}
Expand Down
39 changes: 20 additions & 19 deletions lib/services/cocoapods-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,39 +52,40 @@ export class CocoaPodsService implements ICocoaPodsService {
return podInstallResult;
}

public async applyPluginPodfileToProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
const pluginPodFilePath = this.getPluginPodfilePath(pluginData);
if (!this.$fs.exists(pluginPodFilePath)) {
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
if (!this.$fs.exists(podfilePath)) {
if (podfilePath === projectData.podfilePath) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this check?

this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
}
return;
}

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

if (projectPodfileContent.indexOf(pluginPodfileContent) === -1) {
if (projectPodfileContent.indexOf(podfileContent) === -1) {
// Remove old occurences of the plugin from the project's Podfile.
this.removePluginPodfileFromProject(pluginData, projectData, nativeProjectPath);
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
let finalPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.getPodfileContentWithoutTarget(projectData, this.$fs.readText(pathToProjectPodfile)) : "";

if (pluginPodfileContent.indexOf(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME) !== -1) {
finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, pluginPodfileContent);
if (podfileContent.indexOf(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME) !== -1) {
finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent);
}

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

public removePluginPodfileFromProject(pluginData: IPluginData, projectData: IProjectData, projectRoot: string): void {
const pluginPodfilePath = this.getPluginPodfilePath(pluginData);
public removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, projectRoot: string): void {

if (this.$fs.exists(pluginPodfilePath) && this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
if ((this.$fs.exists(podfilePath) || podfilePath === projectData.podfilePath) && this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should'n we unify the behavior for plugin's pod file and pod file from App_Resources?

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

const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName);
const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${EOL}${this.getPostInstallHookHeader()}end${EOL}end`;
Expand All @@ -98,7 +99,7 @@ export class CocoaPodsService implements ICocoaPodsService {
}
}

private getPluginPodfilePath(pluginData: IPluginData): string {
public getPluginPodfilePath(pluginData: IPluginData): string {
const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(PluginNativeDirNames.iOS);
const pluginPodFilePath = path.join(pluginPlatformsFolderPath, PODFILE_NAME);
return pluginPodFilePath;
Expand Down Expand Up @@ -157,8 +158,8 @@ export class CocoaPodsService implements ICocoaPodsService {
this.$fs.writeFile(projectPodfilePath, contentToWrite);
}

private removePostInstallHook(pluginData: IPluginData, projectPodFileContent: string): string {
const regExp = new RegExp(`^.*?${this.getHookBasicFuncNameForPlugin(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginData.name)}.*?$\\r?\\n`, "gm");
private removePostInstallHook(moduleName: string, projectPodFileContent: string): string {
const regExp = new RegExp(`^.*?${this.getHookBasicFuncNameForPlugin(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, moduleName)}.*?$\\r?\\n`, "gm");
projectPodFileContent = projectPodFileContent.replace(regExp, "");
return projectPodFileContent;
}
Expand Down Expand Up @@ -206,12 +207,12 @@ 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): { pluginPodfileContent: string, replacedFunctions: IRubyFunction[] } {
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[] } {
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
const { replacedContent, newFunctions: replacedFunctions } = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);

return {
pluginPodfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
replacedFunctions
};
}
Expand Down
30 changes: 24 additions & 6 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,20 +771,27 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
this.$logger.trace(`Images to remove from xcode project: ${imagesToRemove.join(", ")}`);
_.each(imagesToRemove, image => project.removeResourceFile(path.join(this.getAppResourcesDestinationDirectoryPath(projectData), image)));

await this.prepareNativeSourceCode("src", path.join(projectData.appDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "src"), projectData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could extract some "src" constants.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case when the project has .nsconfig file, this code will not be correct

path.join(projectData.appDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME)

You should use projectData.getAppResourcesDirectoryPath()


this.savePbxProj(project, projectData);
}

}

public prepareAppResources(appResourcesDirectoryPath: string, projectData: IProjectData): void {
const platformFolder = path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName);
const filterFile = (filename: string) => this.$fs.deleteFile(path.join(platformFolder, filename));

filterFile(this.getPlatformData(projectData).configurationFileName);
filterFile(constants.PODFILE_NAME);

// src folder should not be copied as the pbxproject will have references to its files
this.$fs.deleteDirectory(path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "src"));

this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath(projectData));
}

public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise<void> {
public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData, installPods: boolean): Promise<void> {
await this.mergeInfoPlists({ release }, projectData);
await this.$iOSEntitlementsService.merge(projectData);
await this.mergeProjectXcconfigFiles(release, projectData);
Expand All @@ -793,6 +800,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
}

this.$pluginVariablesService.interpolateAppIdentifier(this.getPlatformData(projectData).configurationFilePath, projectData.projectIdentifiers.ios);

if (installPods) {
await this.installPodsIfAny(projectData);
}
}

private getInfoPlistPath(projectData: IProjectData): string {
Expand Down Expand Up @@ -955,7 +966,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);

const projectRoot = this.getPlatformData(projectData).projectRoot;
await this.$cocoapodsService.applyPluginPodfileToProject(pluginData, projectData, projectRoot);
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
}

public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise<void> {
Expand All @@ -966,12 +977,17 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
this.removeStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
const projectRoot = this.getPlatformData(projectData).projectRoot;

this.$cocoapodsService.removePluginPodfileFromProject(pluginData, projectData, projectRoot);
this.$cocoapodsService.removePodfileFromProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
}

public async afterPrepareAllPlugins(projectData: IProjectData): Promise<void> {
await this.installPodsIfAny(projectData);
}

public async installPodsIfAny(projectData: IProjectData): Promise<void> {
const projectRoot = this.getPlatformData(projectData).projectRoot;
if (this.$fs.exists(this.$cocoapodsService.getProjectPodfilePath(projectRoot))) {
const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, constants.PODFILE_NAME);
if (this.$fs.exists(this.$cocoapodsService.getProjectPodfilePath(projectRoot)) || this.$fs.exists(mainPodfilePath)) {
const xcodeProjPath = this.getXcodeprojPath(projectData);
const xcuserDataPath = path.join(xcodeProjPath, "xcuserdata");
const sharedDataPath = path.join(xcodeProjPath, "xcshareddata");
Expand All @@ -984,6 +1000,8 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
await this.$childProcess.exec(createSchemeRubyScript, { cwd: this.getPlatformData(projectData).projectRoot });
}

await this.$cocoapodsService.applyPodfileToProject(constants.NS_BASE_PODFILE, mainPodfilePath, projectData, this.getPlatformData(projectData).projectRoot);

await this.$cocoapodsService.executePodInstall(projectRoot, xcodeProjPath);
}
}
Expand Down Expand Up @@ -1094,9 +1112,9 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName));
}

private async prepareNativeSourceCode(pluginName: string, pluginPlatformsFolderPath: string, projectData: IProjectData): Promise<void> {
private async prepareNativeSourceCode(groupName: string, sourceFolderPath: string, projectData: IProjectData): Promise<void> {
const project = this.createPbxProj(projectData);
const group = this.getRootGroup(pluginName, pluginPlatformsFolderPath);
const group = this.getRootGroup(groupName, sourceFolderPath);
project.addPbxGroup(group.files, group.name, group.path, null, { isMain: true });
project.addToHeaderSearchPaths(group.path);
this.savePbxProj(project, projectData);
Expand Down
7 changes: 5 additions & 2 deletions lib/services/prepare-platform-native-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ export class PreparePlatformNativeService extends PreparePlatformService impleme
await config.platformData.platformProjectService.prepareProject(config.projectData, config.platformSpecificData);
}

if (!config.changesInfo || config.changesInfo.modulesChanged) {
const shouldPrepareModules = !config.changesInfo || config.changesInfo.modulesChanged;

if (shouldPrepareModules) {
await this.$pluginsService.validate(config.platformData, config.projectData);

const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
Expand All @@ -60,7 +62,8 @@ export class PreparePlatformNativeService extends PreparePlatformService impleme
}

if (!config.changesInfo || config.changesInfo.configChanged || config.changesInfo.modulesChanged) {
await config.platformData.platformProjectService.processConfigurationFilesFromAppResources(config.appFilesUpdaterOptions.release, config.projectData);
// Passing !shouldPrepareModules` we assume that if the node modules are prepared base Podfile content is added and `pod install` is executed.
await config.platformData.platformProjectService.processConfigurationFilesFromAppResources(config.appFilesUpdaterOptions.release, config.projectData, !shouldPrepareModules);
}

config.platformData.platformProjectService.interpolateConfigurationFile(config.projectData, config.platformSpecificData);
Expand Down
4 changes: 2 additions & 2 deletions test/cocoapods-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ end`,
it(testCase.testCaseDescription, async () => {
mockFileSystem(testInjector, testCase.input, testCase.projectPodfileContent);

await cocoapodsService.applyPluginPodfileToProject(testCase.pluginData || mockPluginData, mockProjectData, nativeProjectPath);
await cocoapodsService.applyPodfileToProject(testCase.pluginData ? testCase.pluginData.name : mockPluginData.name, cocoapodsService.getPluginPodfilePath(testCase.pluginData || mockPluginData), mockProjectData, nativeProjectPath);

assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
});
Expand Down Expand Up @@ -720,7 +720,7 @@ end`
it(testCase.testCaseDescription, async () => {
mockFileSystem(testInjector, testCase.input, testCase.projectPodfileContent);

cocoapodsService.removePluginPodfileFromProject(mockPluginData, mockProjectData, nativeProjectPath);
cocoapodsService.removePodfileFromProject(mockPluginData.name, cocoapodsService.getPluginPodfilePath(mockPluginData), mockProjectData, nativeProjectPath);

assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
});
Expand Down
Loading