From 7d477004c2ed0cbbb2bfba1b3a240ea28e1b8ae9 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Mon, 1 Jul 2019 13:55:47 +0300 Subject: [PATCH 1/2] feat(iOS): Support .xcframework bundles --- lib/services/ios-project-service.ts | 49 ++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 7811974f2d..de0d342d8c 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -23,6 +23,7 @@ interface INativeSourceCodeGroup { const DevicePlatformSdkName = "iphoneos"; const SimulatorPlatformSdkName = "iphonesimulator"; +const FRAMEWORK_EXTENSIONS = [".framework", ".xcframework"]; const getPlatformSdkName = (forDevice: boolean): string => forDevice ? DevicePlatformSdkName : SimulatorPlatformSdkName; const getConfigurationName = (release: boolean): string => release ? Configurations.Release : Configurations.Debug; @@ -89,7 +90,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ }; }, frameworkFilesExtensions: [".a", ".framework", ".bin"], - frameworkDirectoriesExtensions: [".framework"], + frameworkDirectoriesExtensions: FRAMEWORK_EXTENSIONS, frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], targetedOS: ['darwin'], configurationFileName: constants.INFO_PLIST_FILE_NAME, @@ -155,6 +156,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ this.replaceFileName("-Info.plist", projectRootFilePath, projectData); } this.replaceFileName("-Prefix.pch", projectRootFilePath, projectData); + if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + ".entitlements"))) { + this.replaceFileName(".entitlements", projectRootFilePath, projectData); + } const xcschemeDirPath = path.join(this.getPlatformData(projectData).projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IosProjectConstants.XcodeProjExtName, "xcshareddata/xcschemes"); const xcschemeFilePath = path.join(xcschemeDirPath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IosProjectConstants.XcodeSchemeExtName); @@ -225,17 +229,38 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return Promise.resolve(); } + private async isDynamicFramework(frameworkPath: string): Promise { + const frameworkName = path.basename(frameworkPath, path.extname(frameworkPath)); + const isDynamicFrameworkBundle = async (bundlePath: string) => { + const frameworkBinaryPath = path.join(bundlePath, frameworkName); + + return _.includes((await this.$childProcess.spawnFromEvent("file", [frameworkBinaryPath], "close")).stdout, "dynamically linked"); + }; + + if (path.extname(frameworkPath) === ".xcframework") { + let isDynamic = true; + const subDirs = this.$fs.readDirectory(frameworkPath).filter(entry => this.$fs.getFsStats(entry).isDirectory()); + for (const subDir of subDirs) { + const singlePlatformFramework = path.join(subDir, frameworkName + ".framework"); + if (this.$fs.exists(singlePlatformFramework)) { + isDynamic = await isDynamicFrameworkBundle(singlePlatformFramework); + break; + } + } + + return isDynamic; + } else { + return await isDynamicFrameworkBundle(frameworkName); + } + } + private async addFramework(frameworkPath: string, projectData: IProjectData): Promise { if (!this.$hostInfo.isWindows) { this.validateFramework(frameworkPath); const project = this.createPbxProj(projectData); - const frameworkName = path.basename(frameworkPath, path.extname(frameworkPath)); - const frameworkBinaryPath = path.join(frameworkPath, frameworkName); - const isDynamic = _.includes((await this.$childProcess.spawnFromEvent("file", [frameworkBinaryPath], "close")).stdout, "dynamically linked"); const frameworkAddOptions: IXcode.Options = { customFramework: true }; - - if (isDynamic) { + if (this.isDynamicFramework(frameworkPath)) { frameworkAddOptions["embed"] = true; } @@ -577,8 +602,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return target; } - private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string): string[] { - const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => path.extname(fileName) === fileExtension; + private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string | string[]): string[] { + const fileExtensions = _.isArray(fileExtension) ? fileExtension : [fileExtension]; + const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => + fileExtensions.indexOf(path.extname(fileName)) !== -1; return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback); } @@ -599,7 +626,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ const plistJson = this.$plistParser.parseFileSync(infoPlistPath); const packageType = plistJson["CFBundlePackageType"]; - if (packageType !== "FMWK") { + if (packageType !== "FMWK" && packageType !== "XFWK") { this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath); } } @@ -679,7 +706,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ this.savePbxProj(project, projectData); } private async prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): Promise { - for (const fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".framework")) { + for (const fileName of this.getAllLibsForPluginWithFileExtension(pluginData, FRAMEWORK_EXTENSIONS)) { await this.addFramework(path.join(pluginPlatformsFolderPath, fileName), projectData); } } @@ -700,7 +727,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private removeFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): void { const project = this.createPbxProj(projectData); - _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => { + _.each(this.getAllLibsForPluginWithFileExtension(pluginData, FRAMEWORK_EXTENSIONS), fileName => { const relativeFrameworkPath = this.getLibSubpathRelativeToProjectPath(fileName, projectData); project.removeFramework(relativeFrameworkPath, { customFramework: true, embed: true }); }); From 7516e540b2da7b02adb8b6f26216312a3e984c39 Mon Sep 17 00:00:00 2001 From: Martin Bektchiev Date: Mon, 1 Jul 2019 14:04:29 +0300 Subject: [PATCH 2/2] chore: Delete unused `frameworkFilesExtensions` property --- lib/definitions/platform.d.ts | 1 - lib/services/android-project-service.ts | 1 - lib/services/ios-project-service.ts | 1 - test/platform-commands.ts | 1 - test/stubs.ts | 2 -- 5 files changed, 6 deletions(-) diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index a981b455ac..e5ec3a2ba4 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -23,7 +23,6 @@ interface IPlatformData { appDestinationDirectoryPath: string; getBuildOutputPath(options: IBuildOutputOptions): string; getValidBuildOutputData(buildOptions: IBuildOutputOptions): IValidBuildOutputData; - frameworkFilesExtensions: string[]; frameworkDirectoriesExtensions?: string[]; frameworkDirectoriesNames?: string[]; targetedOS?: string[]; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index e6e8c362e6..7991a94a89 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -79,7 +79,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject regexes: [new RegExp(`${constants.APP_FOLDER_NAME}-.*-(${Configurations.Debug}|${Configurations.Release})${constants.APK_EXTENSION_NAME}`, "i"), new RegExp(`${packageName}-.*-(${Configurations.Debug}|${Configurations.Release})${constants.APK_EXTENSION_NAME}`, "i")] }; }, - frameworkFilesExtensions: [".jar", ".dat", ".so"], configurationFileName: constants.MANIFEST_FILE_NAME, configurationFilePath: path.join(...configurationsDirectoryArr), relativeToFrameworkConfigurationFilePath: path.join(constants.SRC_DIR, constants.MAIN_DIR, constants.MANIFEST_FILE_NAME), diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index de0d342d8c..8a0d737068 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -89,7 +89,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ packageNames: [`${projectData.projectName}.app`, `${projectData.projectName}.zip`] }; }, - frameworkFilesExtensions: [".a", ".framework", ".bin"], frameworkDirectoriesExtensions: FRAMEWORK_EXTENSIONS, frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], targetedOS: ['darwin'], diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 0a3808baa5..392ffea227 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -42,7 +42,6 @@ class PlatformData implements IPlatformData { getBuildOutputPath = () => ""; getValidBuildOutputData = (buildOptions: IBuildOutputOptions) => ({ packageNames: [""] }); validPackageNamesForDevice: string[] = []; - frameworkFilesExtensions = [".jar", ".dat"]; appDestinationDirectoryPath = ""; relativeToFrameworkConfigurationFilePath = ""; fastLivesyncFileExtensions: string[] = []; diff --git a/test/stubs.ts b/test/stubs.ts index e3ee506cd7..f478556883 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -383,7 +383,6 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor projectRoot: "", getBuildOutputPath: (buildConfig: IBuildConfig) => "", getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }), - frameworkFilesExtensions: [], appDestinationDirectoryPath: "", relativeToFrameworkConfigurationFilePath: "", fastLivesyncFileExtensions: [] @@ -486,7 +485,6 @@ export class NativeProjectDataStub extends EventEmitter implements IPlatformsDat appDestinationDirectoryPath: "", getBuildOutputPath: () => "", getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }), - frameworkFilesExtensions: [], relativeToFrameworkConfigurationFilePath: "", fastLivesyncFileExtensions: [] };