mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08155e12cf | ||
|
|
03e91819d6 | ||
|
|
d4e57d5243 | ||
|
|
3a59941a73 |
+70
-10
@@ -12,11 +12,13 @@ const {prepareHermesArtifactsAsync} = require('./ios-prebuild/hermes');
|
||||
const {
|
||||
prepareReactNativeDependenciesArtifactsAsync,
|
||||
} = require('./ios-prebuild/reactNativeDependencies');
|
||||
const {buildSwiftPackage} = require('./ios-prebuild/swiftpackage');
|
||||
const {
|
||||
createFolderIfNotExists,
|
||||
createLogger,
|
||||
throwIfOnEden,
|
||||
} = require('./ios-prebuild/utils');
|
||||
const {buildXCFrameworks} = require('./ios-prebuild/xcframework');
|
||||
const {execSync} = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@@ -53,14 +55,10 @@ async function main() {
|
||||
* Creates a hard link from one path to another. For each subfolder
|
||||
* in the source path, it creates a link in the target path with an
|
||||
* underscore prefix.
|
||||
* @param {string} fromPath - The path to the source file or directory
|
||||
* @param {string} includePath - Path in the headers folder to create the link
|
||||
* @throws {Error} If the source path does not exist or if the link creation fails
|
||||
* @returns {void}
|
||||
*/
|
||||
const link = (fromPath /*:string*/, includePath /*:string*/) => {
|
||||
const link = (fromPath /*:string*/, includePath /*:?string*/) => {
|
||||
const source = path.resolve(root, fromPath);
|
||||
const target = path.resolve(linksFolder, includePath);
|
||||
const target = path.resolve(linksFolder, includePath ?? fromPath);
|
||||
|
||||
createFolderIfNotExists(target);
|
||||
|
||||
@@ -99,12 +97,16 @@ async function main() {
|
||||
}
|
||||
|
||||
if (linkedFiles > 0) {
|
||||
prebuildLog(`Linking ${source} to ${target}...`);
|
||||
prebuildLog(
|
||||
`Linked ${path.relative(root, source)} → ${path.relative(root, target)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const subfolders = entries
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.filter(dirent => dirent.name !== '__tests__')
|
||||
.filter(dirent => dirent.name !== 'tests')
|
||||
.filter(dirent => dirent.name !== 'platform')
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
// Create links for subfolders
|
||||
@@ -113,10 +115,21 @@ async function main() {
|
||||
});
|
||||
};
|
||||
|
||||
// HERMES ARTIFACTS
|
||||
await prepareHermesArtifactsAsync(currentVersion, 'debug');
|
||||
// BUILD TYPE
|
||||
const buildType = process.env.BUILD_TYPE ?? 'debug';
|
||||
if (buildType !== 'debug' && buildType !== 'release') {
|
||||
throw new Error(
|
||||
`Invalid build type: ${buildType}. Must be either "debug" or "release".`,
|
||||
);
|
||||
}
|
||||
|
||||
await prepareReactNativeDependenciesArtifactsAsync(currentVersion, 'debug');
|
||||
// HERMES ARTIFACTS
|
||||
await prepareHermesArtifactsAsync(currentVersion, buildType);
|
||||
|
||||
await prepareReactNativeDependenciesArtifactsAsync(
|
||||
currentVersion,
|
||||
buildType,
|
||||
);
|
||||
|
||||
// CODEGEN
|
||||
const codegenPath = path.join(root, '.build/codegen');
|
||||
@@ -146,6 +159,8 @@ async function main() {
|
||||
link('React/Views/ScrollView', 'React');
|
||||
link('React/Views/RefreshControl', 'React');
|
||||
link('Libraries/Text', 'React');
|
||||
link('Libraries/AppDelegate');
|
||||
link('ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported', 'React');
|
||||
link(
|
||||
'ReactApple/Libraries/RCTFoundation/RCTDeprecation/Exported',
|
||||
'RCTDeprecation',
|
||||
@@ -160,6 +175,10 @@ async function main() {
|
||||
link('Libraries/LinkingIOS', 'React');
|
||||
link('Libraries/Settings', 'React');
|
||||
|
||||
link('Libraries/PushNotificationIOS', 'React');
|
||||
link('Libraries/Settings', 'React');
|
||||
link('Libraries/Vibration', 'React');
|
||||
|
||||
link('ReactCommon/hermes', 'reacthermes');
|
||||
link('ReactCommon/hermes', 'jsireact');
|
||||
|
||||
@@ -167,6 +186,47 @@ async function main() {
|
||||
'ReactCommon/react/renderer/imagemanager',
|
||||
'react/renderer/imagemanager',
|
||||
);
|
||||
link('ReactCommon/yoga/Yoga', 'ReactCommon/yoga/Yoga');
|
||||
link('ReactCommon/callinvoker', 'ReactCommon');
|
||||
link('ReactCommon/react/renderer/componentregistry');
|
||||
link('ReactCommon/react/renderer/core');
|
||||
link('ReactCommon/react/bridging');
|
||||
link('ReactCommon/react/timing');
|
||||
link('ReactCommon/react/utils');
|
||||
link('ReactCommon/react/debug');
|
||||
link('ReactCommon/react/renderer/debug');
|
||||
link('ReactCommon/react/featureflags');
|
||||
link('ReactCommon/react/renderer/graphics');
|
||||
link(
|
||||
'ReactCommon/react/renderer/graphics/platform/ios',
|
||||
'ReactCommon/react/renderer/graphics',
|
||||
);
|
||||
link('ReactCommon/react/nativemodule/core', 'ReactCommon');
|
||||
link('ReactCommon/react/nativemodule/core/platform/ios', 'ReactCommon');
|
||||
|
||||
link('ReactCommon/react/utils/platform/ios', 'ReactCommon/react/utils');
|
||||
link('ReactCommon/react/runtime');
|
||||
link('ReactCommon/react/runtime/platform/ios', 'ReactCommon/react/runtime');
|
||||
link('ReactCommon/jsitooling/react/runtime', 'ReactCommon/react/runtime');
|
||||
link('ReactCommon/react/renderer/components/legacyviewmanagerinterop');
|
||||
link('ReactCommon/react/renderer/components/view');
|
||||
link(
|
||||
'ReactCommon/react/renderer/components/view/platform/cxx',
|
||||
'ReactCommon/react/renderer/components/view',
|
||||
);
|
||||
link('ReactCommon/react/renderer/mounting');
|
||||
link('ReactCommon/react/renderer/attributedstring');
|
||||
link('ReactCommon/runtimeexecutor/ReactCommon', 'ReactCommon');
|
||||
link('ReactCommon/jsinspector-modern');
|
||||
link('ReactCommon/cxxreact');
|
||||
|
||||
link('.build/codegen/build/generated/ios', 'ReactCodegen');
|
||||
|
||||
// BUILD SWIFT PACKAGE
|
||||
const frameworkPaths = buildSwiftPackage(root, buildFolder, buildType);
|
||||
|
||||
// GENERATE XCFrameworks
|
||||
buildXCFrameworks(root, buildFolder, frameworkPaths, buildType);
|
||||
|
||||
// Done!
|
||||
prebuildLog('🏁 Done!');
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {createLogger} = require('./utils');
|
||||
const {execSync} = require('child_process');
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const buildLog = createLogger('SPM');
|
||||
|
||||
function buildSwiftPackage(
|
||||
rootFolder /*: string */,
|
||||
buildFolder /*: string */,
|
||||
buildType /*: 'debug' | 'release' */,
|
||||
) /*: Array<string> */ {
|
||||
const outputFolder = path.join(buildFolder, 'output', 'spm', buildType);
|
||||
const buildPlatform = (platform /*:string*/) => {
|
||||
const buildCommand =
|
||||
`xcodebuild -scheme React -destination "generic/platform=${platform}" -derivedDataPath "${outputFolder}" ` +
|
||||
`-configuration "${buildType}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface"`;
|
||||
buildLog('Building Swift package for ' + 'debug');
|
||||
buildLog(buildCommand);
|
||||
|
||||
execSync(buildCommand, {
|
||||
cwd: rootFolder,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Extend with platforms/targets
|
||||
buildPlatform('iOS simulator');
|
||||
buildPlatform('iOS');
|
||||
|
||||
// Use glob to find all the frameworks in the output folder
|
||||
const productsFolder = path.join(outputFolder, 'Build/Products');
|
||||
if (!fs.existsSync(productsFolder)) {
|
||||
throw new Error(
|
||||
`Output folder does not exist: ${productsFolder}. Did the build fail?`,
|
||||
);
|
||||
}
|
||||
|
||||
// The frameworks are in the products folder under a platform/buildType folder and are directories ending with .framework
|
||||
const frameworks = glob.sync('**/*.framework', {
|
||||
cwd: productsFolder,
|
||||
absolute: true,
|
||||
});
|
||||
|
||||
if (frameworks.length === 0) {
|
||||
throw new Error(
|
||||
`No frameworks found in the output folder: ${productsFolder}`,
|
||||
);
|
||||
}
|
||||
|
||||
const frameworkPaths = frameworks.filter(p => p.endsWith('React.framework'));
|
||||
if (frameworkPaths.length === 0) {
|
||||
throw new Error(
|
||||
`No React.framework found in the output folder: ${productsFolder}`,
|
||||
);
|
||||
}
|
||||
buildLog('React.frameworks:');
|
||||
frameworkPaths.forEach(p => {
|
||||
buildLog(' ' + p);
|
||||
});
|
||||
return frameworkPaths;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildSwiftPackage,
|
||||
};
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
const {createFolderIfNotExists, createLogger} = require('./utils');
|
||||
const {execSync} = require('child_process');
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
|
||||
const frameworkLog = createLogger('XCFramework');
|
||||
|
||||
const HEADERFILE_IGNORE_LIST = [
|
||||
'RCTCxxMethod.h',
|
||||
'RCTComponentViewClassDescriptor.h',
|
||||
'RCTActivityIndicatorViewManager.h',
|
||||
'RCTComponentViewDescriptor.h',
|
||||
'RCTComponentViewFactory.h',
|
||||
'RCTComponentViewProtocol.h',
|
||||
'RCTComponentViewRegistry.h',
|
||||
'RCTConstants.h',
|
||||
'RCTConversions.h',
|
||||
'RCTConvert+CoreLocation.h',
|
||||
'RCTConvert+Text.h',
|
||||
'RCTConvert+Transform.h',
|
||||
'RCTFabricComponentsPlugins.h',
|
||||
'RCTFabricModalHostViewController.h',
|
||||
'RCTFabricSurface.h',
|
||||
'RCTFollyConvert.h',
|
||||
'RCTLegacyViewManagerInteropCoordinatorAdapter.h',
|
||||
'RCTLinearGradient.h',
|
||||
'RCTRadialGradient.h',
|
||||
'RCTMountingManager.h',
|
||||
'RCTMountingTransactionObserving.h',
|
||||
'RCTParagraphComponentAccessibilityProvider.h',
|
||||
'RuntimeExecutor.h',
|
||||
'RCTSurfacePresenter.h',
|
||||
'RCTSurfacePresenterBridgeAdapter.h',
|
||||
'*View.h',
|
||||
'*ViewProtocol.h',
|
||||
'*ComponentView.h',
|
||||
'RCTViewAccessibilityElement.h',
|
||||
'RCTImageManager*.h',
|
||||
'RCTImagePrimitivesConversions.h',
|
||||
'RCTSyncImageManager.h',
|
||||
'CallbackWrapper.h',
|
||||
'LongLivedObject.h',
|
||||
'Bridging.h',
|
||||
'RCTLegacyViewManagerInteropCoordinator.h',
|
||||
'conversions.h',
|
||||
'Float.h',
|
||||
'Geometry.h',
|
||||
'ObjCTimerRegistry.h',
|
||||
'RCTContextContainerHandling.h',
|
||||
'RCTHost*.h',
|
||||
'ReactCdp.h',
|
||||
'RCTInstance.h',
|
||||
'JsArgumentHelpers-inl.h',
|
||||
'RCTJscInstance.h',
|
||||
'RCTJSThreadManager.h',
|
||||
];
|
||||
|
||||
function buildXCFrameworks(
|
||||
rootFolder /*: string */,
|
||||
buildFolder /*: string */,
|
||||
frameworkFolders /*: Array<string> */,
|
||||
buildType /*: 'debug' | 'release' */,
|
||||
) /*: Array<string> */ {
|
||||
const outputPath = path.join(
|
||||
buildFolder,
|
||||
'output',
|
||||
'xcframeworks',
|
||||
buildType,
|
||||
'React.xcframework',
|
||||
);
|
||||
// Delete all target platform folders (everything but the Headers and Modules folders)
|
||||
cleanPlatformFolders(outputPath);
|
||||
|
||||
// Build the XCFrameworks by using each framework folder as input
|
||||
const frameworks = frameworkFolders.map(frameworkFolder => {
|
||||
const buildCommand = `xcodebuild -create-xcframework -framework "${frameworkFolder}" -output ${outputPath} -allow-internal-distribution`;
|
||||
frameworkLog(`Building ${frameworkFolder}...`);
|
||||
|
||||
frameworkLog(buildCommand);
|
||||
try {
|
||||
execSync(buildCommand, {
|
||||
cwd: rootFolder,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error building XCFramework: ${error.message}. Check if the build was successful.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
});
|
||||
|
||||
// Copy header files from the headers folder that we used to build the swift package
|
||||
const outputHeaderFiles = copyHeaderFiles(
|
||||
path.join(buildFolder, 'Headers'),
|
||||
outputPath,
|
||||
true,
|
||||
);
|
||||
|
||||
// Create the umbrella header file
|
||||
const umbrellaHeaderFile = createUmbrellaHeaderFile(
|
||||
outputPath,
|
||||
outputHeaderFiles,
|
||||
);
|
||||
|
||||
// Create the module map file
|
||||
createModuleMapFile(outputPath, umbrellaHeaderFile);
|
||||
|
||||
return frameworks;
|
||||
}
|
||||
|
||||
function copyHeaderFiles(
|
||||
headersSourceFolder /*: string */,
|
||||
outputPath /*: string */,
|
||||
cleanOutputPath /*: boolean */ = false,
|
||||
) {
|
||||
// Re-create headers folder in the output XCFramework
|
||||
const headersTargetFolder = path.join(outputPath, 'Headers');
|
||||
if (cleanOutputPath && fs.existsSync(headersTargetFolder)) {
|
||||
// Delete the headers folder if it exists
|
||||
try {
|
||||
fs.rmSync(headersTargetFolder, {recursive: true, force: true});
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error deleting headers folder: ${error.message}. Check if the folder exists.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
}
|
||||
createFolderIfNotExists(headersTargetFolder);
|
||||
|
||||
// Now we can copy headers to the headers folder. We need to create the same folder structure as in the
|
||||
// header files inside the Headers folder:
|
||||
frameworkLog('Copying header files to: ' + headersTargetFolder);
|
||||
const headerFiles = glob.sync('**/*.{h,hpp}', {
|
||||
cwd: headersSourceFolder,
|
||||
absolute: true,
|
||||
});
|
||||
|
||||
const outputHeaderFiles /*: Array<string> */ = [];
|
||||
headerFiles.forEach(headerFile => {
|
||||
// The headerFile is a full path to a header file. We need to get the relative path based on the
|
||||
// rootpath parameter.
|
||||
const relativePath = path.relative(headersSourceFolder, headerFile);
|
||||
const targetFolder = path.join(
|
||||
headersTargetFolder,
|
||||
path.dirname(relativePath),
|
||||
);
|
||||
const targetFile = path.join(headersTargetFolder, relativePath);
|
||||
createFolderIfNotExists(targetFolder);
|
||||
try {
|
||||
// Check if the file contains c++ code
|
||||
fs.copyFileSync(headerFile, targetFile);
|
||||
outputHeaderFiles.push(targetFile);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error copying header file: ${error.message}. Check if the file exists.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return outputHeaderFiles;
|
||||
}
|
||||
|
||||
function createUmbrellaHeaderFile(
|
||||
outputPath /*: string */,
|
||||
headerFiles /*: Array<string> */,
|
||||
) /*: string */ {
|
||||
// Create the umbrella header file
|
||||
const umbrellaHeaderPath = path.join(outputPath, 'Headers');
|
||||
const umbrellaHeaderFile = path.join(umbrellaHeaderPath, 'React-umbrella.h');
|
||||
|
||||
frameworkLog('Creating umbrella header file: ' + umbrellaHeaderFile);
|
||||
|
||||
// Create the umbrella header file
|
||||
let umbrellaHeader = `
|
||||
// Generated by React Native
|
||||
// Do not edit this file directly. It is generated by the React Native build process.
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
`;
|
||||
headerFiles.forEach(headerFile => {
|
||||
if (!isCppHeaderFile(headerFile) && !isHeaderFileIgnored(headerFile)) {
|
||||
// The headerFile is a full path to a header file. We need to get the relative path based on the
|
||||
// rootpath parameter.
|
||||
const relativePath = path.relative(umbrellaHeaderPath, headerFile);
|
||||
umbrellaHeader += `#import "${relativePath}"\n`;
|
||||
}
|
||||
});
|
||||
umbrellaHeader += '\n';
|
||||
|
||||
// Write out the umbrella header file
|
||||
try {
|
||||
fs.writeFileSync(umbrellaHeaderFile, umbrellaHeader);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error creating umbrella header file: ${error.message}. Check if the file exists.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
|
||||
return umbrellaHeaderFile;
|
||||
}
|
||||
|
||||
//const cppHeaderRegex = /(#include|#import)\s*<[^>]+>|namespace\s+[\w:]+::/;
|
||||
const cppHeaderRegex = /(#include|#import)\s*<[^.>]+>|\bnamespace\s+[\w:]+::/;
|
||||
|
||||
function isCppHeaderFile(headerFilePath /*: string */) /*: boolean */ {
|
||||
// Check if there is a cpp or mm file with the same name
|
||||
const fileName = path.basename(headerFilePath, path.extname(headerFilePath));
|
||||
const dirName = path.dirname(headerFilePath);
|
||||
|
||||
const checkFileExists = (extension /*: string */) /*: boolean */ => {
|
||||
const cppFilePath = path.join(dirName, fileName + extension);
|
||||
if (fs.existsSync(cppFilePath)) {
|
||||
const fileStat = fs.statSync(cppFilePath);
|
||||
return fileStat.isFile();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (checkFileExists('.cpp') || checkFileExists('.mm')) {
|
||||
// If there is a cpp or mm file with the same name, we assume it is a C++ header file
|
||||
return true;
|
||||
}
|
||||
// Check if the file contains c++ code
|
||||
const fileContent = fs.readFileSync(headerFilePath, 'utf8');
|
||||
return cppHeaderRegex.test(fileContent);
|
||||
}
|
||||
|
||||
function isHeaderFileIgnored(headerFile /*: string */) /*: boolean */ {
|
||||
// Check if the header file is in the ignore list
|
||||
return HEADERFILE_IGNORE_LIST.some(pattern =>
|
||||
// Glob match the header file name to the ignore list
|
||||
glob
|
||||
.sync(pattern, {
|
||||
cwd: path.dirname(headerFile),
|
||||
absolute: true,
|
||||
})
|
||||
.some(ignoredHeaderFile => {
|
||||
return path.basename(headerFile) === path.basename(ignoredHeaderFile);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function createModuleMapFile(
|
||||
outputPath /*: string */,
|
||||
umbrellaPath /*: string */,
|
||||
) {
|
||||
// Create/get the module map folder
|
||||
const moduleMapFolder = path.join(outputPath, 'Modules');
|
||||
createFolderIfNotExists(moduleMapFolder);
|
||||
|
||||
// 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)}"
|
||||
export *
|
||||
module * { export * }
|
||||
}`;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(moduleMapFile, moduleMapContent);
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error creating module map file: ${error.message}. Check if the file exists.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
}
|
||||
function cleanPlatformFolders(outputPath /*:string*/) {
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
return;
|
||||
}
|
||||
const targetPlatformFolders = fs.readdirSync(outputPath).filter(folder => {
|
||||
const folderPath = path.join(outputPath, folder);
|
||||
return (
|
||||
fs.statSync(folderPath).isDirectory() &&
|
||||
folder !== 'Headers' &&
|
||||
folder !== 'Modules'
|
||||
);
|
||||
});
|
||||
targetPlatformFolders.forEach(folder => {
|
||||
const folderPath = path.join(outputPath, folder);
|
||||
frameworkLog('Deleting folder: ' + folderPath);
|
||||
try {
|
||||
fs.rmSync(folderPath, {recursive: true, force: true});
|
||||
} catch (error) {
|
||||
frameworkLog(
|
||||
`Error deleting folder: ${folderPath}. Check if the folder exists.`,
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildXCFrameworks,
|
||||
};
|
||||
Reference in New Issue
Block a user