Skip to content

feat: initial implementation of pods override #5058

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 2 commits into from
Oct 10, 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
3 changes: 2 additions & 1 deletion lib/controllers/prepare-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { hook } from "../common/helpers";
import { performanceLog, cache } from "../common/decorators";
import { EventEmitter } from "events";
import * as path from "path";
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../constants";
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter, CONFIG_NS_FILE_NAME } from "../constants";
interface IPlatformWatcherData {
hasWebpackCompilerProcess: boolean;
nativeFilesWatcher: choki.FSWatcher;
Expand Down Expand Up @@ -189,6 +189,7 @@ export class PrepareController extends EventEmitter {

const patterns = [
path.join(projectData.projectDir, PACKAGE_JSON_FILE_NAME),
path.join(projectData.projectDir, CONFIG_NS_FILE_NAME),
path.join(projectData.getAppDirectoryPath(), PACKAGE_JSON_FILE_NAME),
path.join(projectData.getAppResourcesRelativeDirectoryPath(), platformData.normalizedPlatformName),
]
Expand Down
7 changes: 6 additions & 1 deletion lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ interface IPlatformsDataService {
}

interface INodeModulesBuilder {
prepareNodeModules(platformData: IPlatformData, projectData: IProjectData): Promise<void>;
prepareNodeModules(prepareNodeModulesData: IPrepareNodeModulesData): Promise<void>;
}

interface IPrepareNodeModulesData {
platformData: IPlatformData;
projectData: IProjectData;
}

interface INodeModulesDependenciesBuilder {
Expand Down
8 changes: 7 additions & 1 deletion lib/definitions/plugins.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ interface IPluginsService {
* @returns {IPackageJsonDepedenciesResult}
*/
getDependenciesFromPackageJson(projectDir: string): IPackageJsonDepedenciesResult;
preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void>;
preparePluginNativeCode(preparePluginNativeCodeData: IPreparePluginNativeCodeData): Promise<void>;
convertToPluginData(cacheData: any, projectDir: string): IPluginData;
isNativeScriptPlugin(pluginPackageJsonPath: string): boolean;
}

interface IPreparePluginNativeCodeData {
pluginData: IPluginData;
platform: string;
projectData: IProjectData;
}

interface IPackageJsonDepedenciesResult {
dependencies: IBasePluginData[],
devDependencies?: IBasePluginData[]
Expand Down
1 change: 1 addition & 0 deletions lib/definitions/project-changes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface IProjectChangesInfo extends IAddedNativePlatform {
configChanged: boolean;
nativeChanged: boolean;
signingChanged: boolean;
nsConfigChanged: boolean;

readonly hasChanges: boolean;
readonly changesRequireBuild: boolean;
Expand Down
5 changes: 3 additions & 2 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ interface INsConfig {
appResourcesPath?: string;
shared?: boolean;
previewAppSchema?: string;
overridePods?: string
}

interface IProjectData extends ICreateProjectData {
Expand Down Expand Up @@ -406,10 +407,10 @@ interface ICocoaPodsService {
* @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.
* @param {IPlatformData} platformData Information about the platform.
* @returns {Promise<void>}
*/
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void>;

/**
* Gives the path to the plugin's Podfile.
Expand Down
68 changes: 59 additions & 9 deletions lib/services/cocoapods-service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { EOL } from "os";
import * as path from "path";
import { PluginNativeDirNames, PODFILE_NAME, NS_BASE_PODFILE } from "../constants";
import { regExpEscape } from "../common/helpers";
import { regExpEscape, getHash } from "../common/helpers";

export class CocoaPodsService implements ICocoaPodsService {
private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
private static INSTALLER_BLOCK_PARAMETER_NAME = "installer";

private getCocoaPodsFromPodfile: Function;
constructor(
private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager,
private $fs: IFileSystem,
private $childProcess: IChildProcess,
private $errors: IErrors,
private $logger: ILogger,
private $config: IConfiguration,
private $xcconfigService: IXcconfigService) { }
private $xcconfigService: IXcconfigService) {
this.getCocoaPodsFromPodfile = _.memoize(this._getCocoaPodsFromPodfile, getHash);
}

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

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

return podInstallResult;
Expand All @@ -59,17 +65,18 @@ export class CocoaPodsService implements ICocoaPodsService {
const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, normalizedPlatformName, PODFILE_NAME);
const projectPodfilePath = this.getProjectPodfilePath(projectRoot);
if (this.$fs.exists(projectPodfilePath) || this.$fs.exists(mainPodfilePath)) {
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, projectRoot);
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, platformData);
}
}

public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void> {
const nativeProjectPath = platformData.projectRoot;
if (!this.$fs.exists(podfilePath)) {
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
return;
}

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

Expand Down Expand Up @@ -222,10 +229,16 @@ 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[], podfilePlatformData: IPodfilePlatformData } {
private buildPodfileContent(pluginPodFilePath: string, pluginName: string, projectData: IProjectData, platformData: IPlatformData): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
const cocoapodsData = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
const podfilePlatformData = cocoapodsData.podfilePlatformData;
let replacedContent = cocoapodsData.replacedContent;

if (projectData.nsConfig && projectData.nsConfig.overridePods && !this.isMainPodFile(pluginPodFilePath, projectData, platformData)) {
replacedContent = this.overridePodsFromFile(replacedContent, projectData, platformData);
}

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

private getMainPodFilePath(projectData: IProjectData, platformData: IPlatformData): string {
return path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName, PODFILE_NAME);
}

private isMainPodFile(podFilePath: string, projectData: IProjectData, platformData: IPlatformData): boolean {
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);

return podFilePath === mainPodfilePath;
}

private overridePodsFromFile(podfileContent: string, projectData: IProjectData, platformData: IPlatformData): string {
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
const mainPodfileContent = this.$fs.readText(mainPodfilePath);
const pods = this.getCocoaPodsFromPodfile(mainPodfileContent);
_.forEach(pods, pod => {
podfileContent = podfileContent.replace(new RegExp(`^[ ]*pod\\s*["']${pod}['"].*$`, "gm"), '#$&');
});

return podfileContent;
}

private _getCocoaPodsFromPodfile(podfileContent: string): Array<string> {
const pods = [];
const podsRegex = /^\s*pod\s*["'](.*?)['"].*$/gm;

let match = podsRegex.exec(podfileContent);
while (match != null) {
const podName: string = match[1];
if (podName) {
pods.push(podName);
}

match = podsRegex.exec(podfileContent);
}

return pods;
}
}

$injector.register("cocoapodsService", CocoaPodsService);
25 changes: 16 additions & 9 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
await this.prepareResources(pluginPlatformsFolderPath, pluginData, projectData);
await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData, projectData);
await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);

const projectRoot = this.getPlatformData(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 @@ -513,8 +510,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ

public async handleNativeDependenciesChange(projectData: IProjectData, opts: IRelease): Promise<void> {
const platformData = this.getPlatformData(projectData);

const pluginsData = await this.getAllInstalledPlugins(projectData);
this.setProductBundleIdentifier(projectData);

await this.applyPluginsCocoaPods(pluginsData, projectData, platformData);
await this.$cocoapodsService.applyPodfileFromAppResources(projectData, platformData);

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

const pbxProjPath = this.getPbxProjPath(projectData);
this.$iOSExtensionsService.removeExtensions({ pbxProjPath });
await this.addExtensions(projectData);
await this.addExtensions(projectData, pluginsData);
}

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

private async addExtensions(projectData: IProjectData): Promise<void> {
private async addExtensions(projectData: IProjectData, pluginsData: IPluginData[]): Promise<void> {
const resorcesExtensionsPath = path.join(
projectData.getAppResourcesDirectoryPath(),
this.getPlatformData(projectData).normalizedPlatformName, constants.NATIVE_EXTENSION_FOLDER
);
const platformData = this.getPlatformData(projectData);
const pbxProjPath = this.getPbxProjPath(projectData);
const addedExtensionsFromResources = await this.$iOSExtensionsService.addExtensionsFromPath({ extensionsFolderPath: resorcesExtensionsPath, projectData, platformData, pbxProjPath });
const plugins = await this.getAllInstalledPlugins(projectData);
let addedExtensionsFromPlugins = false;
for (const pluginIndex in plugins) {
const pluginData = plugins[pluginIndex];
for (const pluginIndex in pluginsData) {
const pluginData = pluginsData[pluginIndex];
const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);

const extensionPath = path.join(pluginPlatformsFolderPath, constants.NATIVE_EXTENSION_FOLDER);
Expand Down Expand Up @@ -829,6 +827,15 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
this.$logger.warn("[WARNING]: The CFBundleIdentifier key inside the 'Info.plist' will be overriden by the 'id' inside 'package.json'.");
}
}

private async applyPluginsCocoaPods(pluginsData: IPluginData[], projectData: IProjectData, platformData: IPlatformData) {
for (const pluginIndex in pluginsData) {
const pluginData = pluginsData[pluginIndex];
if (this.$fs.exists(pluginData.pluginPlatformsFolderPath(platformData.normalizedPlatformName))) {
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, platformData);
}
}
}
}

$injector.register("iOSProjectService", IOSProjectService);
2 changes: 1 addition & 1 deletion lib/services/platform/prepare-native-platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi
}

if (hasNativeModulesChange) {
await this.$nodeModulesBuilder.prepareNodeModules(platformData, projectData);
await this.$nodeModulesBuilder.prepareNodeModules({platformData, projectData});
}

if (hasNativeModulesChange || hasConfigChange) {
Expand Down
2 changes: 1 addition & 1 deletion lib/services/plugins-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class PluginsService implements IPluginsService {
}
}

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

Expand Down
8 changes: 7 additions & 1 deletion lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "path";
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME } from "../constants";
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME, CONFIG_NS_FILE_NAME } from "../constants";
import { getHash, hook } from "../common/helpers";

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

public appResourcesChanged: boolean;
public configChanged: boolean;
public nsConfigChanged: boolean;
public nativeChanged: boolean;
public signingChanged: boolean;
public nativePlatformStatus: NativePlatformStatus;
Expand Down Expand Up @@ -70,6 +71,10 @@ export class ProjectChangesService implements IProjectChangesService {
this._changesInfo.nativeChanged = this.isProjectFileChanged(projectData.projectDir, platformData);
}

// If this causes too much rebuilds of the plugins or uncecessary builds for Android, move overrideCocoapods to prepareInfo.
this._changesInfo.nsConfigChanged = this.filesChanged([path.join(projectData.projectDir, CONFIG_NS_FILE_NAME)]);
this._changesInfo.nativeChanged = this._changesInfo.nativeChanged || this._changesInfo.nsConfigChanged;

this.$logger.trace(`Set nativeChanged to ${this._changesInfo.nativeChanged}.`);

if (platformData.platformNameLowerCase === this.$devicePlatformsConstants.iOS.toLowerCase()) {
Expand Down Expand Up @@ -183,6 +188,7 @@ export class ProjectChangesService implements IProjectChangesService {
this._changesInfo.appResourcesChanged = true;
this._changesInfo.configChanged = true;
this._changesInfo.nativeChanged = true;
this._changesInfo.nsConfigChanged = true;
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/tools/node-modules/node-modules-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
private $pluginsService: IPluginsService
) { }

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