mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Update the xcframework.js script to support swift (#52135)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52135 Update the xcframework.js script to support Swift ## Context This PR introduces the first working version of building React Native apps on iOS using prebuilt RNCore and cocoapods. - Added React-Core-prebuilt.podspec for installing/consuming XCFrameworks - Added logic in react_native_pods.rb for switching between build from source and using prebuilts - Added rncore.rb - utilities for the ReactCore prebuilts - Updated rndependencies with some extra error handling modelled after rncode.rb - Added support for hard linking headers and modules in each inner framework in the XCFramework in xcframework.js ## Swift: To enable support for the objective-c types from swift, the swift compiler uses a module map to gather exports from the framework (module.modulemap). This file basically points to an umbrella header file that exports the valid objective-c types (non c++) to Swift. In addition these files are read from the DerivedData and not the project source - so it is a bit hard to control everyting. I was initially not able to use cocoapods own module definitions (module_name, module_file props) to use a custom module map. I finally found that these files are expected in the deriveddata (build folder) where only the active inner framework is copied - so then I had to hard link both module map and header files for each arch. ## Changelog: [INTERNAL] - Update the xcframework.js script to support Swift Pull Request resolved: https://github.com/facebook/react-native/pull/52109 Test Plan: Run with RN Tester. We need to remove all extra pods from RNTester pod file since none of them are yet compatible with prebuilt (they reference non-prebuilt pods) Rollback Plan: Reviewed By: cortinico Differential Revision: D76980285 Pulled By: cipolleschi fbshipit-source-id: 4e5486b79c406ba4b375e2ada24cbe5450e2346f
This commit is contained in:
committed by
Facebook GitHub Bot
parent
152cb538f6
commit
69e028da45
+115
-5
@@ -69,6 +69,8 @@ const HEADERFILE_IGNORE_LIST = [
|
||||
'JsArgumentHelpers-inl.h',
|
||||
'RCTJscInstance.h',
|
||||
'RCTJSThreadManager.h',
|
||||
'YGEnums.h',
|
||||
'YGNode.h',
|
||||
];
|
||||
|
||||
function buildXCFrameworks(
|
||||
@@ -106,8 +108,9 @@ function buildXCFrameworks(
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error building XCFramework: ${error.message}. Check if the build was successful.`,
|
||||
'warning',
|
||||
'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy header files from the headers folder that we used to build the swift package
|
||||
@@ -124,7 +127,22 @@ function buildXCFrameworks(
|
||||
);
|
||||
|
||||
// Create the module map file
|
||||
createModuleMapFile(outputPath, umbrellaHeaderFile);
|
||||
const moduleMapFile = createModuleMapFile(outputPath, umbrellaHeaderFile);
|
||||
if (!moduleMapFile) {
|
||||
frameworkLog(
|
||||
'Failed to create module map file. The XCFramework may not work correctly. Stopping.',
|
||||
'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the platforms in the framework folder and copy modulemaps and headers into each platform folder
|
||||
linkArchFolders(
|
||||
outputPath,
|
||||
moduleMapFile,
|
||||
umbrellaHeaderFile,
|
||||
outputHeaderFiles,
|
||||
);
|
||||
|
||||
// Copy Symbols to symbols folder
|
||||
const symbolPaths = frameworkFolders.map(framework =>
|
||||
@@ -144,6 +162,91 @@ function buildXCFrameworks(
|
||||
}
|
||||
}
|
||||
|
||||
function linkArchFolders(
|
||||
outputPath /*:string*/,
|
||||
moduleMapFile /*:string*/,
|
||||
umbrellaHeaderFile /*:string*/,
|
||||
outputHeaderFiles /*: Array<string> */,
|
||||
) {
|
||||
frameworkLog('Linking modules and headers to platform folders...');
|
||||
const headerRootFolder = path.dirname(umbrellaHeaderFile);
|
||||
|
||||
fs.readdirSync(outputPath)
|
||||
.filter(folder => {
|
||||
const folderPath = path.join(outputPath, folder);
|
||||
return (
|
||||
fs.statSync(folderPath).isDirectory() &&
|
||||
folder !== 'Headers' &&
|
||||
folder !== 'Modules'
|
||||
);
|
||||
})
|
||||
.forEach(folder => {
|
||||
// Get full platform folder path
|
||||
const platformFolder = path.join(outputPath, folder);
|
||||
// Link the Modules folder into the platform folder
|
||||
const targetModulesFolder = path.join(
|
||||
platformFolder,
|
||||
'React.Framework',
|
||||
'Modules',
|
||||
);
|
||||
createFolderIfNotExists(targetModulesFolder);
|
||||
try {
|
||||
fs.linkSync(
|
||||
moduleMapFile,
|
||||
path.join(targetModulesFolder, path.basename(moduleMapFile)),
|
||||
);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error copying module map file: ${error.message}. Check if the file exists at ${moduleMapFile}.`,
|
||||
'error',
|
||||
);
|
||||
}
|
||||
// Copy headers folder into the platform folder
|
||||
const targetHeadersFolder = path.join(
|
||||
platformFolder,
|
||||
'React.Framework',
|
||||
'Headers',
|
||||
);
|
||||
// Link header files into the platform folder
|
||||
outputHeaderFiles.forEach(headerFile => {
|
||||
// Get the relative path of the header file based on the root folder
|
||||
const relativePath = path.relative(headerRootFolder, headerFile);
|
||||
// Create the target folder for the header file
|
||||
const targetFolder = path.join(
|
||||
targetHeadersFolder,
|
||||
path.dirname(relativePath),
|
||||
);
|
||||
// Create the target folder if it doesn't exist
|
||||
createFolderIfNotExists(targetFolder);
|
||||
// Link the header file to the target folder
|
||||
try {
|
||||
fs.linkSync(
|
||||
headerFile,
|
||||
path.join(targetFolder, path.basename(headerFile)),
|
||||
);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error linking header file: ${error.message}. Check if the file exists.`,
|
||||
'error',
|
||||
);
|
||||
}
|
||||
});
|
||||
// Link the umbrella header file to the target headers folder
|
||||
try {
|
||||
const targetUmbrellaPath = path.join(
|
||||
targetHeadersFolder,
|
||||
'React-umbrella.h',
|
||||
);
|
||||
fs.linkSync(umbrellaHeaderFile, targetUmbrellaPath);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error linking umbrella header file: ${error.message}. Check if the file exists.`,
|
||||
'error',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function copyHeaderFiles(
|
||||
headersSourceFolder /*: string */,
|
||||
outputPath /*: string */,
|
||||
@@ -249,7 +352,11 @@ function createUmbrellaHeaderFile(
|
||||
return umbrellaHeaderFile;
|
||||
}
|
||||
|
||||
const cppHeaderRegex = /(#include|#import)\s*<[^.>]+>|\bnamespace\s+[\w:]+::/;
|
||||
// This regex matches some C++ construct that might be present in a header file.
|
||||
// To uniquely identify them. We need to exclude headers with C++ constructs from the module map
|
||||
// otherwise Swift won't be able to import the React.xcframework
|
||||
const cppHeaderRegex =
|
||||
/(#include|#import)\s*<[^.>]+>|\bnamespace\s+[\w:]+::|NS_ENUM\s*\([^)]*\)|NS_OPTIONS\s*\([^)]*\)|typedef\s+enum|static\s+const|@interface|static\s+inline/;
|
||||
|
||||
function isCppHeaderFile(headerFilePath /*: string */) /*: boolean */ {
|
||||
// Check if there is a cpp or mm file with the same name
|
||||
@@ -299,21 +406,24 @@ function createModuleMapFile(
|
||||
// Create the module map file
|
||||
const moduleMapFile = path.join(moduleMapFolder, 'module.modulemap');
|
||||
frameworkLog('Creating module map file: ' + moduleMapFile);
|
||||
const moduleMapContent = `module React {
|
||||
umbrella header "../Headers/${path.basename(umbrellaPath)}"
|
||||
const moduleMapContent = `framework module React {
|
||||
umbrella header "${path.basename(umbrellaPath)}"
|
||||
export *
|
||||
module * { export * }
|
||||
}`;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(moduleMapFile, moduleMapContent);
|
||||
return moduleMapFile;
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error creating module map file: ${error.message}. Check if the file exists.`,
|
||||
'warning',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanPlatformFolders(outputPath /*:string*/) {
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user