diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 53dff769c6be..e49618adf0e8 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -101,9 +101,14 @@ jobs: flavor: ${{ matrix.flavor }} prebuild_apple_dependencies: - uses: ./.github/workflows/prebuild-ios.yml + uses: ./.github/workflows/prebuild-ios-dependencies.yml secrets: inherit + prebuild_react_native_core: + uses: ./.github/workflows/prebuild-ios-core.yml + secrets: inherit + needs: [prebuild_apple_dependencies, build_hermes_macos] + build_hermesc_linux: runs-on: ubuntu-latest needs: prepare_hermes_workspace diff --git a/.github/workflows/prebuild-ios-core.yml b/.github/workflows/prebuild-ios-core.yml new file mode 100644 index 000000000000..8959aaa4e75a --- /dev/null +++ b/.github/workflows/prebuild-ios-core.yml @@ -0,0 +1,199 @@ +name: Prebuild iOS Dependencies + +on: + workflow_call: # this directive allow us to call this workflow from other workflows + + +jobs: + build-rn-slice: + runs-on: macos-14 + strategy: + fail-fast: false + matrix: + flavor: ['Debug', 'Release'] + slice: [ + 'ios', + 'ios-simulator', + 'mac-catalyst', + ] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Restore cache if present + id: restore-ios-slice + uses: actions/cache/restore@v4 + with: + path: packages/react-native/third-party/ + key: v2-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }} + - name: Setup node.js + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + uses: ./.github/actions/setup-node + - name: Setup xcode + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + uses: ./.github/actions/setup-xcode + with: + xcode-version: '16.2.0' + - name: Yarn Install + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + uses: ./.github/actions/yarn-install + - name: Download Hermes + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + uses: actions/download-artifact@v4 + with: + name: hermes-darwin-bin-${{ matrix.flavor }} + path: /tmp/hermes/hermes-runtime-darwin + - name: Extract Hermes + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + shell: bash + run: | + HERMES_TARBALL_ARTIFACTS_DIR=/tmp/hermes/hermes-runtime-darwin + if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then + echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR)." + exit 0 + fi + + TARBALL_FILENAME=$(node ./packages/react-native/scripts/hermes/get-tarball-name.js --buildType "${{ matrix.flavor }}") + TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME + + echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR" + echo "$TARBALL_PATH" + + if [ ! -f $TARBALL_PATH ]; then + echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source." + exit 0 + fi + + echo "Found Hermes tarball at $TARBALL_PATH" + echo "HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $GITHUB_ENV + - name: Download ReactNativeDependencies + uses: actions/download-artifact@v4 + with: + name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz + path: /tmp/third-party/ + - name: Extract ReactNativeDependencies + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + shell: bash + run: | + # Extract ReactNativeDependencies + tar -xzf /tmp/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz -C /tmp/third-party/ + + # Create destination folder + mkdir -p packages/react-native/third-party/ + + # Move the XCFramework in the destination directory + mv /tmp/third-party/packages/react-native/third-party/ReactNativeDependencies.xcframework packages/react-native/third-party/ReactNativeDependencies.xcframework + + VERSION=$(jq -r '.version' package.json) + echo "$VERSION-${{matrix.flavor}}" > "packages/react-native/third-party/version.txt" + cat "packages/react-native/third-party/version.txt" + # Check destination directory + ls -lR packages/react-native/third-party/ + - name: Setup the workspace + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + shell: bash + run: | + cd packages/react-native + node scripts/ios-prebuild.js -s -f "${{ matrix.flavor }}" + - name: Build React Native + if: steps.restore-ios-slice.outputs.cache-hit != 'true' + shell: bash + run: | + # This is going to be replaced by a CLI script + cd packages/react-native + node scripts/ios-prebuild -b -f "${{ matrix.flavor }}" -p "${{ matrix.slice }}" + - name: Upload artifacts + uses: actions/upload-artifact@v4.3.4 + with: + name: prebuild-ios-core-slice-${{ matrix.flavor }}-${{ matrix.slice }} + path: | + packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products + - name: Save Cache + uses: actions/cache/save@v4 + if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode + with: + key: v2-ios-core-${{ matrix.slice }}-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }} + enableCrossOsArchive: true + path: | + packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products + + compose-xcframework: + runs-on: macos-14 + needs: [build-rn-slice] + strategy: + fail-fast: false + matrix: + flavor: ['Debug', 'Release'] + env: + REACT_ORG_CODE_SIGNING_P12_CERT: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }} + REACT_ORG_CODE_SIGNING_P12_CERT_PWD: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Restore cache if present + id: restore-ios-xcframework + uses: actions/cache/restore@v4 + with: + path: packages/react-native/.build/output/xcframeworks + key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }} + - name: Setup node.js + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + uses: ./.github/actions/setup-node + - name: Setup xcode + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + uses: ./.github/actions/setup-xcode + with: + xcode-version: '16.2.0' + - name: Yarn Install + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + uses: ./.github/actions/yarn-install + - name: Download slice artifacts + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + uses: actions/download-artifact@v4 + with: + pattern: prebuild-ios-core-slice-${{ matrix.flavor }}-* + path: packages/react-native/.build/output/spm/${{ matrix.flavor }}/Build/Products + merge-multiple: true + - name: Setup Keychain + if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }} + uses: apple-actions/import-codesign-certs@v3 # https://github.com/marketplace/actions/import-code-signing-certificates + with: + p12-file-base64: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT }} + p12-password: ${{ secrets.REACT_ORG_CODE_SIGNING_P12_CERT_PWD }} + - name: Create XCFramework + if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT == '' }} + run: | + cd packages/react-native + node scripts/ios-prebuild -c -f "${{ matrix.flavor }}" + - name: Create and Sign XCFramework + if: ${{ steps.restore-ios-xcframework.outputs.cache-hit != 'true' && env.REACT_ORG_CODE_SIGNING_P12_CERT != '' }} + run: | + cd packages/react-native + node scripts/ios-prebuild -c -f "${{ matrix.flavor }}" -i "React Org" + - name: Compress and Rename XCFramework + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + run: | + cd packages/react-native/.build/output/xcframeworks/${{matrix.flavor}} + tar -cz -f ../React${{matrix.flavor}}.xcframework.tar.gz React.xcframework + - name: Compress and Rename dSYM + if: steps.restore-ios-xcframework.outputs.cache-hit != 'true' + run: | + cd packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}/Symbols + tar -cz -f ../../React${{ matrix.flavor }}.framework.dSYM.tar.gz . + - name: Upload XCFramework Artifact + uses: actions/upload-artifact@v4 + with: + name: React${{ matrix.flavor }}.xcframework.tar.gz + path: packages/react-native/.build/output/xcframeworks/React${{matrix.flavor}}.xcframework.tar.gz + - name: Upload dSYM Artifact + uses: actions/upload-artifact@v4 + with: + name: React${{ matrix.flavor }}.framework.dSYM.tar.gz + path: packages/react-native/.build/output/xcframeworks/React${{matrix.flavor}}.framework.dSYM.tar.gz + - name: Save cache if present + if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode + uses: actions/cache/save@v4 + with: + path: | + packages/react-native/.build/output/xcframeworks/React${{matrix.flavor}}.xcframework.tar.gz + packages/react-native/.build/output/xcframeworks/React${{matrix.flavor}}.framework.dSYM.tar.gz + key: v2-ios-core-xcframework-${{ matrix.flavor }}-${{ hashFiles('packages/react-native/Package.swift') }}-${{ hashFiles('packages/react-native/scripts/ios-prebuild/setup.js') }} diff --git a/.github/workflows/prebuild-ios.yml b/.github/workflows/prebuild-ios-dependencies.yml similarity index 99% rename from .github/workflows/prebuild-ios.yml rename to .github/workflows/prebuild-ios-dependencies.yml index 56f15777fa8d..3ae438892382 100644 --- a/.github/workflows/prebuild-ios.yml +++ b/.github/workflows/prebuild-ios-dependencies.yml @@ -1,4 +1,4 @@ -name: Prebuild iOS +name: Prebuild iOS Dependencies on: workflow_call: # this directive allow us to call this workflow from other workflows diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 6e0521579226..9cdd88bbac5a 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -96,9 +96,15 @@ jobs: hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }} react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }} flavor: ${{ matrix.flavor }} + prebuild_apple_dependencies: - uses: ./.github/workflows/prebuild-ios.yml + uses: ./.github/workflows/prebuild-ios-dependencies.yml + secrets: inherit + + prebuild_react_native_core: + uses: ./.github/workflows/prebuild-ios-core.yml secrets: inherit + needs: [prebuild_apple_dependencies, build_hermes_macos] build_hermesc_linux: runs-on: ubuntu-latest diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index 99797d317ba8..da0d4ed74d8a 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -117,9 +117,14 @@ jobs: flavor: ${{ matrix.flavor }} prebuild_apple_dependencies: - uses: ./.github/workflows/prebuild-ios.yml + uses: ./.github/workflows/prebuild-ios-dependencies.yml secrets: inherit + prebuild_react_native_core: + uses: ./.github/workflows/prebuild-ios-core.yml + secrets: inherit + needs: [prebuild_apple_dependencies, build_hermes_macos] + test_ios_rntester_ruby_3_2_0: runs-on: macos-14 needs: diff --git a/packages/react-native/scripts/ios-prebuild.js b/packages/react-native/scripts/ios-prebuild.js index 0e71fea00d89..1c7319ba7edf 100644 --- a/packages/react-native/scripts/ios-prebuild.js +++ b/packages/react-native/scripts/ios-prebuild.js @@ -71,7 +71,13 @@ async function main() { if (cli.tasks.compose) { const productsFolder = computeProductsFolder(outputFolder); const frameworkPaths = computeFrameworkPaths(productsFolder); - buildXCFrameworks(root, buildFolder, frameworkPaths, buildType); + buildXCFrameworks( + root, + buildFolder, + frameworkPaths, + buildType, + cli.identity, + ); } // Done! diff --git a/packages/react-native/scripts/ios-prebuild/build.js b/packages/react-native/scripts/ios-prebuild/build.js index e0454e658b4b..7381771675e8 100644 --- a/packages/react-native/scripts/ios-prebuild/build.js +++ b/packages/react-native/scripts/ios-prebuild/build.js @@ -9,7 +9,7 @@ */ /*:: -import type {Destination} from './types'; +import type {BuildFlavor, Destination} from './types'; */ const {createLogger} = require('./utils'); @@ -52,13 +52,14 @@ function computeFrameworkPaths( function buildSwiftPackage( rootFolder /*: string */, buildFolder /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, platform /*: Destination */, outputFolder /*: 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"`; + `-configuration "${buildType}" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_SWIFT_FLAGS="-no-verify-emitted-module-interface" ` + + `DEBUG_INFORMATION_FORMAT="dwarf-with-dsym"`; buildLog(`Building Swift package for ${buildType}`); buildLog(buildCommand); diff --git a/packages/react-native/scripts/ios-prebuild/cli.js b/packages/react-native/scripts/ios-prebuild/cli.js index 55a32033961b..01301c800af7 100644 --- a/packages/react-native/scripts/ios-prebuild/cli.js +++ b/packages/react-native/scripts/ios-prebuild/cli.js @@ -11,7 +11,7 @@ const yargs = require('yargs'); /*:: -import type {Destination, Platform} from './types'; +import type {BuildFlavor, Destination, Platform} from './types'; */ const platforms /*: $ReadOnlyArray */ = [ @@ -77,7 +77,7 @@ async function getCLIConfiguration() /*: Promise, identity: ?string, |}> */ { @@ -103,10 +103,10 @@ async function getCLIConfiguration() /*: Promise platformToDestination[p]); // Validate flavor - const resolvedFlavor = argv.flavor.toLowerCase(); - if (resolvedFlavor !== 'debug' && resolvedFlavor !== 'release') { + const flavor = argv.flavor; + if (flavor !== 'Debug' && flavor !== 'Release') { console.error( - `Invalid flavor specified: ${resolvedFlavor}\nValid flavors are: debug, release`, + `Invalid flavor specified: ${flavor}\nValid flavors are: Debug, Release`, ); return undefined; } @@ -121,7 +121,7 @@ async function getCLIConfiguration() /*: Promise */ { hermesLog(`Preparing Hermes...`); @@ -135,7 +139,7 @@ const HermesEngineSourceTypes = { function checkExistingVersion( versionFilePath /*: string */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) { const resolvedVersion = `${version}-${buildType}`; @@ -177,18 +181,18 @@ function hermesEngineTarballEnvvarDefined() /*: boolean */ { function getTarballUrl( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: string */ { const mavenRepoUrl = 'https://repo1.maven.org/maven2'; const namespace = 'com/facebook/react'; - return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-ios-${buildType}.tar.gz`; + return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-hermes-ios-${buildType.toLowerCase()}.tar.gz`; } function getNightlyTarballUrl( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: string */ { - const params = `r=snapshots&g=com.facebook.react&a=react-native-artifacts&c=hermes-ios-${buildType}&e=tar.gz&v=${version}-SNAPSHOT`; + const params = `r=snapshots&g=com.facebook.react&a=react-native-artifacts&c=hermes-ios-${buildType.toLowerCase()}&e=tar.gz&v=${version}-SNAPSHOT`; return `https://oss.sonatype.org/service/local/artifact/maven/redirect?${params}`; } @@ -231,7 +235,7 @@ async function hermesArtifactExists( */ async function hermesSourceType( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: Promise */ { if (hermesEngineTarballEnvvarDefined()) { hermesLog('Using local prebuild tarball'); @@ -262,7 +266,7 @@ async function hermesSourceType( async function resolveSourceFromSourceType( sourceType /*: HermesEngineSourceType */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { switch (sourceType) { @@ -296,7 +300,7 @@ function localPrebuiltTarball() /*: string */ { async function downloadPrebuildTarball( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { const url = getTarballUrl(version, buildType); @@ -306,7 +310,7 @@ async function downloadPrebuildTarball( async function downloadPrebuiltNightlyTarball( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { const url = await resolveUrlRedirects( @@ -318,7 +322,7 @@ async function downloadPrebuiltNightlyTarball( async function downloadStableHermes( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { const tarballUrl = getTarballUrl(version, buildType); @@ -331,7 +335,7 @@ async function downloadStableHermes( async function downloadHermesTarball( tarballUrl /*: string */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { const destPath = buildType diff --git a/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js b/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js index bcc3d2d3d517..1d32b76245a4 100644 --- a/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js +++ b/packages/react-native/scripts/ios-prebuild/reactNativeDependencies.js @@ -8,6 +8,8 @@ * @format */ +/*:: import type {BuildFlavor} from './types'; */ + const {createLogger} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); @@ -26,7 +28,7 @@ const dependencyLog = createLogger('ReactNativeDependencies'); */ async function prepareReactNativeDependenciesArtifactsAsync( version /*:string*/, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: Promise */ { dependencyLog(`Preparing ReactNativeDependencies...`); @@ -133,7 +135,7 @@ const ReactNativeDependenciesEngineSourceTypes = { function checkExistingVersion( versionFilePath /*: string */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) { const resolvedVersion = `${version}-${buildType}`; @@ -146,7 +148,7 @@ function checkExistingVersion( if (fs.existsSync(versionFilePath)) { const versionFileContent = fs.readFileSync(versionFilePath, 'utf8'); dependencyLog(`Version found on disk: ${versionFileContent}`); - if (versionFileContent.trim().toLowerCase() === resolvedVersion) { + if (versionFileContent.trim() === resolvedVersion) { dependencyLog( `ReactNativeDependencies artifacts already downloaded and up to date: ${artifactsPath}`, ); @@ -175,18 +177,18 @@ function checkExistingVersion( function getTarballUrl( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: string */ { const mavenRepoUrl = 'https://repo1.maven.org/maven2'; const namespace = 'com/facebook/react'; - return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-${buildType}.tar.gz`; + return `${mavenRepoUrl}/${namespace}/react-native-artifacts/${version}/react-native-artifacts-${version}-reactnative-dependencies-${buildType.toLowerCase()}.tar.gz`; } function getNightlyTarballUrl( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: string */ { - const params = `r=snapshots&g=com.facebook.react&a=react-native-artifacts&c=reactnative-dependencies-${buildType}&e=tar.gz&v=${version}-SNAPSHOT`; + const params = `r=snapshots&g=com.facebook.react&a=react-native-artifacts&c=reactnative-dependencies-${buildType.toLowerCase()}&e=tar.gz&v=${version}-SNAPSHOT`; return `https://oss.sonatype.org/service/local/artifact/maven/redirect?${params}`; } @@ -229,7 +231,7 @@ async function reactNativeDependenciesArtifactExists( */ async function reactNativeDependenciesSourceType( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) /*: Promise */ { const tarballUrl = getTarballUrl(version, buildType); if (await reactNativeDependenciesArtifactExists(tarballUrl)) { @@ -255,7 +257,7 @@ async function reactNativeDependenciesSourceType( async function resolveSourceFromSourceType( sourceType /*: ReactNativeDependenciesEngineSourceType */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { switch (sourceType) { @@ -273,7 +275,7 @@ async function resolveSourceFromSourceType( async function downloadPrebuildTarball( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { const url = getTarballUrl(version, buildType); @@ -287,7 +289,7 @@ async function downloadPrebuildTarball( async function downloadPrebuiltNightlyTarball( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string*/, ) /*: Promise */ { const url = await resolveUrlRedirects( @@ -304,7 +306,7 @@ async function downloadPrebuiltNightlyTarball( async function downloadStableReactNativeDependencies( version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { const tarballUrl = getTarballUrl(version, buildType); @@ -322,7 +324,7 @@ async function downloadStableReactNativeDependencies( async function downloadReactNativeDependenciesTarball( tarballUrl /*: string */, version /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, artifactsPath /*: string */, ) /*: Promise */ { const destPath = buildType diff --git a/packages/react-native/scripts/ios-prebuild/setup.js b/packages/react-native/scripts/ios-prebuild/setup.js index 0419dd1c206e..bd26c3634f44 100644 --- a/packages/react-native/scripts/ios-prebuild/setup.js +++ b/packages/react-native/scripts/ios-prebuild/setup.js @@ -8,6 +8,8 @@ * @format */ +/*:: import type {BuildFlavor} from './types'; */ + const {prepareHermesArtifactsAsync} = require('./hermes'); const { prepareReactNativeDependenciesArtifactsAsync, @@ -21,7 +23,7 @@ async function setup( root /*:string*/, buildFolder /*: string */, currentVersion /*: string */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, ) { const prebuildLog = createLogger('prebuild'); createFolderIfNotExists(buildFolder); diff --git a/packages/react-native/scripts/ios-prebuild/types.js b/packages/react-native/scripts/ios-prebuild/types.js index 1f7430c198e0..c1ac1489c804 100644 --- a/packages/react-native/scripts/ios-prebuild/types.js +++ b/packages/react-native/scripts/ios-prebuild/types.js @@ -18,6 +18,8 @@ export type Destination = 'iOS' | 'iOS Simulator' | 'macOS,variant=Mac Catalyst'; + +export type BuildFlavor = 'Debug' | 'Release'; */ module.exports = {}; diff --git a/packages/react-native/scripts/ios-prebuild/xcframework.js b/packages/react-native/scripts/ios-prebuild/xcframework.js index b86068e00f91..ffecf7a74e22 100644 --- a/packages/react-native/scripts/ios-prebuild/xcframework.js +++ b/packages/react-native/scripts/ios-prebuild/xcframework.js @@ -8,6 +8,8 @@ * @format */ +/*:: import type {BuildFlavor} from './types'; */ + const {createFolderIfNotExists, createLogger} = require('./utils'); const {execSync} = require('child_process'); const fs = require('fs'); @@ -73,7 +75,8 @@ function buildXCFrameworks( rootFolder /*: string */, buildFolder /*: string */, frameworkFolders /*: Array */, - buildType /*: 'debug' | 'release' */, + buildType /*: BuildFlavor */, + identity /*: ?string */, ) { const outputPath = path.join( buildFolder, @@ -122,6 +125,23 @@ function buildXCFrameworks( // Create the module map file createModuleMapFile(outputPath, umbrellaHeaderFile); + + // Copy Symbols to symbols folder + const symbolPaths = frameworkFolders.map(framework => + path.join(framework, `..`, `..`, `React.framework.dSYM`), + ); + console.log('Copying symbols to symbols folder...'); + const symbolOutput = path.join(outputPath, '..', 'Symbols'); + symbolPaths.forEach(symbol => { + const destination = extractDestinationFromPath(symbol); + const outputFolder = path.join(symbolOutput, destination); + fs.mkdirSync(outputFolder, {recursive: true}); + execSync(`cp -r ${symbol} ${outputFolder}`); + }); + + if (identity) { + signXCFramework(identity, outputPath); + } } function copyHeaderFiles( @@ -320,6 +340,33 @@ function cleanPlatformFolders(outputPath /*:string*/) { }); } +function extractDestinationFromPath(symbolPath /*: string */) /*: string */ { + if (symbolPath.includes('iphoneos')) { + return 'iphoneos'; + } + + if (symbolPath.includes('iphonesimulator')) { + return 'iphonesimulator'; + } + + if (symbolPath.includes('maccatalyst')) { + return 'catalyst'; + } + + throw new Error( + `Impossible to extract destination from ${symbolPath}. Valid destinations are iphoneos, iphonesimulator and catalyst.`, + ); +} + +function signXCFramework( + identity /*: string */, + xcframeworkPath /*: string */, +) { + console.log('Signing XCFramework...'); + const command = `codesign --timestamp --sign "${identity}" ${xcframeworkPath}`; + execSync(command, {stdio: 'inherit'}); +} + module.exports = { buildXCFrameworks, };