@@ -234,6 +234,13 @@ async function prepareHermesArtifactsAsync(
234234 fs . unlinkSync ( localPath ) ;
235235 }
236236
237+ // [macOS] The Maven tarball contains a standalone macOS framework at
238+ // destroot/Library/Frameworks/macosx/ but does NOT include it in the
239+ // xcframework at destroot/Library/Frameworks/universal/. SPM's BinaryTarget
240+ // requires an xcframework, so we rebuild it to include the macOS slice.
241+ ensureMacOSSliceInXCFramework ( artifactsPath ) ;
242+ // macOS]
243+
237244 return artifactsPath ;
238245}
239246
@@ -525,6 +532,114 @@ async function downloadHermesTarball(
525532}
526533
527534// [macOS
535+ /**
536+ * The Hermes tarball from Maven contains a standalone macOS framework at
537+ * destroot/Library/Frameworks/macosx/hermes.framework (or hermesvm.framework)
538+ * but the xcframework at destroot/Library/Frameworks/universal/ does not include
539+ * a macOS slice. This function rebuilds the xcframework to include it.
540+ *
541+ * This is needed because SPM's BinaryTarget requires an xcframework, and
542+ * xcodebuild needs the macOS slice present to build for macOS.
543+ */
544+ function ensureMacOSSliceInXCFramework ( artifactsPath /*: string */ ) {
545+ const frameworksDir = path . join (
546+ artifactsPath ,
547+ 'destroot' ,
548+ 'Library' ,
549+ 'Frameworks' ,
550+ ) ;
551+ const macosDir = path . join ( frameworksDir , 'macosx' ) ;
552+ const universalDir = path . join ( frameworksDir , 'universal' ) ;
553+
554+ if ( ! fs . existsSync ( macosDir ) ) {
555+ hermesLog ( 'No macOS framework found in tarball, skipping xcframework rebuild' ) ;
556+ return ;
557+ }
558+
559+ // Find the framework name (hermes.framework or hermesvm.framework)
560+ const macosFrameworks = fs . readdirSync ( macosDir ) . filter ( f => f . endsWith ( '.framework' ) ) ;
561+ if ( macosFrameworks . length === 0 ) {
562+ hermesLog ( 'No .framework found in macosx directory, skipping' ) ;
563+ return ;
564+ }
565+ const frameworkName = macosFrameworks [ 0 ] ;
566+
567+ // Find the existing xcframework
568+ const xcframeworks = fs . existsSync ( universalDir )
569+ ? fs . readdirSync ( universalDir ) . filter ( f => f . endsWith ( '.xcframework' ) )
570+ : [ ] ;
571+
572+ if ( xcframeworks . length === 0 ) {
573+ hermesLog ( 'No existing xcframework found, creating one from macOS framework only' ) ;
574+ fs . mkdirSync ( universalDir , { recursive : true } ) ;
575+ const xcframeworkName = frameworkName . replace ( '.framework' , '.xcframework' ) ;
576+ execSync (
577+ `xcodebuild -create-xcframework ` +
578+ `-framework "${ path . join ( macosDir , frameworkName ) } " ` +
579+ `-output "${ path . join ( universalDir , xcframeworkName ) } "` ,
580+ { stdio : 'inherit' } ,
581+ ) ;
582+ hermesLog ( `Created ${ xcframeworkName } with macOS slice` ) ;
583+ return ;
584+ }
585+
586+ const xcframeworkName = xcframeworks [ 0 ] ;
587+ const xcframeworkPath = path . join ( universalDir , xcframeworkName ) ;
588+
589+ // Check if macOS slice already exists in the xcframework
590+ const existingSlices = fs . readdirSync ( xcframeworkPath ) . filter ( d => d . startsWith ( 'macos-' ) ) ;
591+ if ( existingSlices . length > 0 ) {
592+ hermesLog ( 'macOS slice already present in xcframework, skipping rebuild' ) ;
593+ return ;
594+ }
595+
596+ hermesLog ( `Rebuilding ${ xcframeworkName } to include macOS slice...` ) ;
597+
598+ // Collect all existing framework slices from the xcframework
599+ const sliceDirs = fs . readdirSync ( xcframeworkPath ) . filter ( d => {
600+ const slicePath = path . join ( xcframeworkPath , d ) ;
601+ return fs . statSync ( slicePath ) . isDirectory ( ) && d !== '_CodeSignature' ;
602+ } ) ;
603+
604+ // Build the -framework arguments for xcodebuild -create-xcframework
605+ const frameworkArgs = sliceDirs . map ( sliceDir => {
606+ const slicePath = path . join ( xcframeworkPath , sliceDir ) ;
607+ const frameworks = fs . readdirSync ( slicePath ) . filter ( f => f . endsWith ( '.framework' ) ) ;
608+ if ( frameworks . length > 0 ) {
609+ return `-framework "${ path . join ( slicePath , frameworks [ 0 ] ) } "` ;
610+ }
611+ return null ;
612+ } ) . filter ( Boolean ) ;
613+
614+ // Add the macOS framework
615+ frameworkArgs . push ( `-framework "${ path . join ( macosDir , frameworkName ) } "` ) ;
616+
617+ // Rebuild the xcframework
618+ const tmpXCFramework = path . join ( universalDir , `${ xcframeworkName } .tmp` ) ;
619+ try {
620+ execSync (
621+ `xcodebuild -create-xcframework ${ frameworkArgs . join ( ' ' ) } -output "${ tmpXCFramework } "` ,
622+ { stdio : 'inherit' } ,
623+ ) ;
624+
625+ // Replace the original xcframework
626+ fs . rmSync ( xcframeworkPath , { recursive : true , force : true } ) ;
627+ fs . renameSync ( tmpXCFramework , xcframeworkPath ) ;
628+
629+ hermesLog ( `Rebuilt ${ xcframeworkName } with macOS slice added` ) ;
630+ } catch ( e ) {
631+ // Clean up on failure
632+ if ( fs . existsSync ( tmpXCFramework ) ) {
633+ fs . rmSync ( tmpXCFramework , { recursive : true , force : true } ) ;
634+ }
635+ hermesLog (
636+ `Warning: Failed to rebuild xcframework with macOS slice: ${ e . message } . ` +
637+ `The macOS framework is available at ${ path . join ( macosDir , frameworkName ) } ` +
638+ `but could not be added to the xcframework.` ,
639+ ) ;
640+ }
641+ }
642+
528643/**
529644 * Handles the case where no prebuilt Hermes artifacts are available.
530645 * Determines the Hermes commit at the merge base with facebook/react-native
0 commit comments