diff --git a/packages/react-native/scripts/ios-prebuild.js b/packages/react-native/scripts/ios-prebuild.js index efa8689d2e1..01833a2bf3f 100644 --- a/packages/react-native/scripts/ios-prebuild.js +++ b/packages/react-native/scripts/ios-prebuild.js @@ -12,6 +12,7 @@ const {prepareHermesArtifactsAsync} = require('./ios-prebuild/hermes'); const { prepareReactNativeDependenciesArtifactsAsync, } = require('./ios-prebuild/reactNativeDependencies'); +const {buildSwiftPackage} = require('./ios-prebuild/swiftpackage'); const { createFolderIfNotExists, createLogger, @@ -53,10 +54,6 @@ 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 source = path.resolve(root, fromPath); @@ -113,10 +110,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'); @@ -168,6 +176,10 @@ async function main() { 'react/renderer/imagemanager', ); + // BUILD SWIFT PACKAGE + const frameworkPaths = buildSwiftPackage(root, buildFolder, buildType); + + // Done! prebuildLog('🏁 Done!'); } catch (err) { diff --git a/packages/react-native/scripts/ios-prebuild/swiftpackage.js b/packages/react-native/scripts/ios-prebuild/swiftpackage.js new file mode 100644 index 00000000000..dd4d168314d --- /dev/null +++ b/packages/react-native/scripts/ios-prebuild/swiftpackage.js @@ -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 */ { + 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, +};