Skip to content

Commit 626db9c

Browse files
Merge pull request #4907 from NativeScript/feat/support-xcframeworks
feat: Support .xcframework bundles
2 parents 77de5b5 + 7516e54 commit 626db9c

File tree

5 files changed

+38
-17
lines changed

5 files changed

+38
-17
lines changed

lib/definitions/platform.d.ts

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ interface IPlatformData {
2323
appDestinationDirectoryPath: string;
2424
getBuildOutputPath(options: IBuildOutputOptions): string;
2525
getValidBuildOutputData(buildOptions: IBuildOutputOptions): IValidBuildOutputData;
26-
frameworkFilesExtensions: string[];
2726
frameworkDirectoriesExtensions?: string[];
2827
frameworkDirectoriesNames?: string[];
2928
targetedOS?: string[];

lib/services/android-project-service.ts

-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
7979
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")]
8080
};
8181
},
82-
frameworkFilesExtensions: [".jar", ".dat", ".so"],
8382
configurationFileName: constants.MANIFEST_FILE_NAME,
8483
configurationFilePath: path.join(...configurationsDirectoryArr),
8584
relativeToFrameworkConfigurationFilePath: path.join(constants.SRC_DIR, constants.MAIN_DIR, constants.MANIFEST_FILE_NAME),

lib/services/ios-project-service.ts

+38-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface INativeSourceCodeGroup {
2323

2424
const DevicePlatformSdkName = "iphoneos";
2525
const SimulatorPlatformSdkName = "iphonesimulator";
26+
const FRAMEWORK_EXTENSIONS = [".framework", ".xcframework"];
2627

2728
const getPlatformSdkName = (forDevice: boolean): string => forDevice ? DevicePlatformSdkName : SimulatorPlatformSdkName;
2829
const getConfigurationName = (release: boolean): string => release ? Configurations.Release : Configurations.Debug;
@@ -88,8 +89,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
8889
packageNames: [`${projectData.projectName}.app`, `${projectData.projectName}.zip`]
8990
};
9091
},
91-
frameworkFilesExtensions: [".a", ".framework", ".bin"],
92-
frameworkDirectoriesExtensions: [".framework"],
92+
frameworkDirectoriesExtensions: FRAMEWORK_EXTENSIONS,
9393
frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"],
9494
targetedOS: ['darwin'],
9595
configurationFileName: constants.INFO_PLIST_FILE_NAME,
@@ -155,6 +155,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
155155
this.replaceFileName("-Info.plist", projectRootFilePath, projectData);
156156
}
157157
this.replaceFileName("-Prefix.pch", projectRootFilePath, projectData);
158+
if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + ".entitlements"))) {
159+
this.replaceFileName(".entitlements", projectRootFilePath, projectData);
160+
}
158161

159162
const xcschemeDirPath = path.join(this.getPlatformData(projectData).projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IosProjectConstants.XcodeProjExtName, "xcshareddata/xcschemes");
160163
const xcschemeFilePath = path.join(xcschemeDirPath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IosProjectConstants.XcodeSchemeExtName);
@@ -225,17 +228,38 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
225228
return Promise.resolve();
226229
}
227230

231+
private async isDynamicFramework(frameworkPath: string): Promise<boolean> {
232+
const frameworkName = path.basename(frameworkPath, path.extname(frameworkPath));
233+
const isDynamicFrameworkBundle = async (bundlePath: string) => {
234+
const frameworkBinaryPath = path.join(bundlePath, frameworkName);
235+
236+
return _.includes((await this.$childProcess.spawnFromEvent("file", [frameworkBinaryPath], "close")).stdout, "dynamically linked");
237+
};
238+
239+
if (path.extname(frameworkPath) === ".xcframework") {
240+
let isDynamic = true;
241+
const subDirs = this.$fs.readDirectory(frameworkPath).filter(entry => this.$fs.getFsStats(entry).isDirectory());
242+
for (const subDir of subDirs) {
243+
const singlePlatformFramework = path.join(subDir, frameworkName + ".framework");
244+
if (this.$fs.exists(singlePlatformFramework)) {
245+
isDynamic = await isDynamicFrameworkBundle(singlePlatformFramework);
246+
break;
247+
}
248+
}
249+
250+
return isDynamic;
251+
} else {
252+
return await isDynamicFrameworkBundle(frameworkName);
253+
}
254+
}
255+
228256
private async addFramework(frameworkPath: string, projectData: IProjectData): Promise<void> {
229257
if (!this.$hostInfo.isWindows) {
230258
this.validateFramework(frameworkPath);
231259

232260
const project = this.createPbxProj(projectData);
233-
const frameworkName = path.basename(frameworkPath, path.extname(frameworkPath));
234-
const frameworkBinaryPath = path.join(frameworkPath, frameworkName);
235-
const isDynamic = _.includes((await this.$childProcess.spawnFromEvent("file", [frameworkBinaryPath], "close")).stdout, "dynamically linked");
236261
const frameworkAddOptions: IXcode.Options = { customFramework: true };
237-
238-
if (isDynamic) {
262+
if (this.isDynamicFramework(frameworkPath)) {
239263
frameworkAddOptions["embed"] = true;
240264
}
241265

@@ -577,8 +601,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
577601
return target;
578602
}
579603

580-
private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string): string[] {
581-
const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => path.extname(fileName) === fileExtension;
604+
private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string | string[]): string[] {
605+
const fileExtensions = _.isArray(fileExtension) ? fileExtension : [fileExtension];
606+
const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) =>
607+
fileExtensions.indexOf(path.extname(fileName)) !== -1;
582608
return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback);
583609
}
584610

@@ -599,7 +625,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
599625
const plistJson = this.$plistParser.parseFileSync(infoPlistPath);
600626
const packageType = plistJson["CFBundlePackageType"];
601627

602-
if (packageType !== "FMWK") {
628+
if (packageType !== "FMWK" && packageType !== "XFWK") {
603629
this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath);
604630
}
605631
}
@@ -679,7 +705,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
679705
this.savePbxProj(project, projectData);
680706
}
681707
private async prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): Promise<void> {
682-
for (const fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".framework")) {
708+
for (const fileName of this.getAllLibsForPluginWithFileExtension(pluginData, FRAMEWORK_EXTENSIONS)) {
683709
await this.addFramework(path.join(pluginPlatformsFolderPath, fileName), projectData);
684710
}
685711
}
@@ -700,7 +726,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
700726

701727
private removeFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): void {
702728
const project = this.createPbxProj(projectData);
703-
_.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => {
729+
_.each(this.getAllLibsForPluginWithFileExtension(pluginData, FRAMEWORK_EXTENSIONS), fileName => {
704730
const relativeFrameworkPath = this.getLibSubpathRelativeToProjectPath(fileName, projectData);
705731
project.removeFramework(relativeFrameworkPath, { customFramework: true, embed: true });
706732
});

test/platform-commands.ts

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class PlatformData implements IPlatformData {
4242
getBuildOutputPath = () => "";
4343
getValidBuildOutputData = (buildOptions: IBuildOutputOptions) => ({ packageNames: [""] });
4444
validPackageNamesForDevice: string[] = [];
45-
frameworkFilesExtensions = [".jar", ".dat"];
4645
appDestinationDirectoryPath = "";
4746
relativeToFrameworkConfigurationFilePath = "";
4847
fastLivesyncFileExtensions: string[] = [];

test/stubs.ts

-2
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor
383383
projectRoot: "",
384384
getBuildOutputPath: (buildConfig: IBuildConfig) => "",
385385
getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }),
386-
frameworkFilesExtensions: [],
387386
appDestinationDirectoryPath: "",
388387
relativeToFrameworkConfigurationFilePath: "",
389388
fastLivesyncFileExtensions: []
@@ -486,7 +485,6 @@ export class NativeProjectDataStub extends EventEmitter implements IPlatformsDat
486485
appDestinationDirectoryPath: "",
487486
getBuildOutputPath: () => "",
488487
getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }),
489-
frameworkFilesExtensions: [],
490488
relativeToFrameworkConfigurationFilePath: "",
491489
fastLivesyncFileExtensions: []
492490
};

0 commit comments

Comments
 (0)