diff --git a/.ado/apple-pipeline.yml b/.ado/apple-pipeline.yml new file mode 100644 index 00000000000..8e91b95db5d --- /dev/null +++ b/.ado/apple-pipeline.yml @@ -0,0 +1,156 @@ +# +# Standalone pipeline for building Hermes Apple frameworks (macOS, iOS, tvOS, visionOS). +# This pipeline runs outside the 1ES template since 1ES shared pools +# do not currently offer macOS images. +# + +trigger: none +pr: none + +pool: + vmImage: macos-15 + +variables: + IOS_DEPLOYMENT_TARGET: "15.1" + XROS_DEPLOYMENT_TARGET: "1.0" + MAC_DEPLOYMENT_TARGET: "10.15" + +parameters: +- name: AppleSliceMatrix + type: object + default: + - Name: macosx + - Name: iphoneos + - Name: iphonesimulator + - Name: appletvos + - Name: appletvsimulator + - Name: catalyst + - Name: xros + - Name: xrsimulator + +- name: AppleFlavorMatrix + type: object + default: + - Name: Debug + - Name: Release + +stages: +- stage: build_apple + displayName: Build Apple Frameworks + jobs: + + #========================================================================= + # Step 1: Build the host HermesC compiler. + #========================================================================= + - job: Build_HermesC_Apple + displayName: Build HermesC (Apple host) + steps: + - checkout: self + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + xcodebuild -version + displayName: Setup Xcode + - script: > + node .ado/scripts/build-apple.mts + --no-build + --no-build-xcframework + displayName: Build host hermesc + - publish: $(Build.SourcesDirectory)/build_host_hermesc + artifact: hermesc-apple + displayName: Publish HermesC artifact + + #========================================================================= + # Step 2: Build each Apple platform slice (matrix: slice x flavor). + #========================================================================= + - ${{ each flavor in parameters.AppleFlavorMatrix }}: + - ${{ each slice in parameters.AppleSliceMatrix }}: + - job: Build_Apple_${{ slice.Name }}_${{ flavor.Name }} + displayName: Build Apple ${{ slice.Name }} (${{ flavor.Name }}) + dependsOn: Build_HermesC_Apple + timeoutInMinutes: 120 + steps: + - checkout: self + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + displayName: Setup Xcode + - download: current + artifact: hermesc-apple + displayName: Download HermesC artifact + - script: | + cp -R $(Pipeline.Workspace)/hermesc-apple $(Build.SourcesDirectory)/build_host_hermesc + chmod +x ./build_host_hermesc/bin/hermesc + + node .ado/scripts/build-apple.mts \ + --no-build-hermesc \ + --slice ${{ slice.Name }} \ + --configuration ${{ flavor.Name }} \ + --no-build-xcframework + + # Rename for artifact uniqueness and compress to preserve symlinks + mv "build_${{ slice.Name }}" "build_${{ slice.Name }}_${{ flavor.Name }}" + tar -czf "build_${{ slice.Name }}_${{ flavor.Name }}.tar.gz" "build_${{ slice.Name }}_${{ flavor.Name }}" + displayName: Build ${{ slice.Name }} ${{ flavor.Name }} framework + - publish: $(Build.SourcesDirectory)/build_${{ slice.Name }}_${{ flavor.Name }}.tar.gz + artifact: apple-slice-${{ slice.Name }}-${{ flavor.Name }} + displayName: Publish ${{ slice.Name }} ${{ flavor.Name }} artifact + + #========================================================================= + # Step 3: Assemble universal xcframework from all slices (per flavor). + #========================================================================= + - ${{ each flavor in parameters.AppleFlavorMatrix }}: + - job: Build_Apple_XCFramework_${{ flavor.Name }} + displayName: Build Apple XCFramework (${{ flavor.Name }}) + dependsOn: + - ${{ each slice in parameters.AppleSliceMatrix }}: + - Build_Apple_${{ slice.Name }}_${{ flavor.Name }} + timeoutInMinutes: 60 + steps: + - checkout: self + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + displayName: Setup Xcode + + # Download all slice artifacts for this flavor. + - ${{ each slice in parameters.AppleSliceMatrix }}: + - download: current + artifact: apple-slice-${{ slice.Name }}-${{ flavor.Name }} + displayName: Download ${{ slice.Name }} ${{ flavor.Name }} slice + + # Untar and rename all slices back to build_ for the script. + - script: | + set -euo pipefail + SLICES="macosx iphoneos iphonesimulator appletvos appletvsimulator catalyst xros xrsimulator" + for SLICE in $SLICES; do + cp "$(Pipeline.Workspace)/apple-slice-${SLICE}-${{ flavor.Name }}/build_${SLICE}_${{ flavor.Name }}.tar.gz" . + tar -xzf "build_${SLICE}_${{ flavor.Name }}.tar.gz" + mv "build_${SLICE}_${{ flavor.Name }}" "build_${SLICE}" + done + displayName: Unpack slice artifacts + + # Assemble universal xcframework and create dSYM archive. + - script: > + node .ado/scripts/build-apple.mts + --no-build-hermesc + --no-build + --configuration ${{ flavor.Name }} + --create-dsym-archive + displayName: Create universal xcframework + + - publish: $(Build.SourcesDirectory)/destroot + artifact: apple-hermes-xcframework-${{ flavor.Name }} + displayName: Publish xcframework artifact + - publish: $(Build.SourcesDirectory)/hermes-dSYM-${{ flavor.Name }}.tar.gz + artifact: apple-hermes-dsym-${{ flavor.Name }} + displayName: Publish dSYM artifact diff --git a/.ado/build-template.yml b/.ado/build-template.yml index bf69620acc7..42bf98aa34d 100644 --- a/.ado/build-template.yml +++ b/.ado/build-template.yml @@ -13,6 +13,25 @@ parameters: type: boolean default: false + # Matrix with Apple platform slices for Hermes xcframework. +- name: AppleSliceMatrix + type: object + default: + - Name: macosx + - Name: iphoneos + - Name: iphonesimulator + - Name: appletvos + - Name: appletvsimulator + - Name: catalyst + - Name: xros + - Name: xrsimulator + +- name: AppleFlavorMatrix + type: object + default: + - Name: Debug + - Name: Release + # Matrix with target platforms for Hermes binaries. - name: BuildMatrix type: object @@ -44,6 +63,7 @@ parameters: TargetCPU: arm64ec BuildUWP: --no-uwp + resources: repositories: # The repo for the Office compliant build pipeline templates. @@ -355,3 +375,179 @@ extends: "ToolVersion" : "1.0" } ] + + #============================================================================= + # Apple framework builds + # Builds Hermes for all Apple platforms (macOS, iOS, tvOS, visionOS, catalyst) + # and assembles them into a universal xcframework. + #============================================================================= + - stage: apple + dependsOn: [] # Run in parallel with the Windows 'main' stage + jobs: + + # Step 1: Build the host HermesC compiler on macOS. + - job: Build_HermesC_Apple + displayName: Build HermesC (Apple host) + + pool: + name: Azure Pipelines + vmImage: macos-15 + os: macOS + + variables: + - name: skipComponentGovernanceDetection + value: true + - name: ONEES_ENFORCED_CODEQL_ENABLED + value: false + + templateContext: + outputs: + - output: pipelineArtifact + artifactName: hermesc-apple + targetPath: $(Build.SourcesDirectory)/build_host_hermesc + + steps: + - checkout: self + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + xcodebuild -version + displayName: Setup Xcode + - script: > + node .ado/scripts/build-apple.mts + --no-build + --no-build-xcframework + displayName: Build host hermesc + + # Step 2: Build each Apple platform slice (matrix: slice x flavor). + - ${{ each flavor in parameters.AppleFlavorMatrix }}: + - ${{ each slice in parameters.AppleSliceMatrix }}: + - job: Build_Apple_${{ slice.Name }}_${{ flavor.Name }} + displayName: Build Apple ${{ slice.Name }} (${{ flavor.Name }}) + + dependsOn: + - Build_HermesC_Apple + + pool: + name: Azure Pipelines + vmImage: macos-15 + os: macOS + + timeoutInMinutes: 120 + + variables: + - name: skipComponentGovernanceDetection + value: true + - name: ONEES_ENFORCED_CODEQL_ENABLED + value: false + + templateContext: + outputs: + - output: pipelineArtifact + artifactName: apple-slice-${{ slice.Name }}-${{ flavor.Name }} + targetPath: $(Build.SourcesDirectory)/build_${{ slice.Name }}_${{ flavor.Name }}.tar.gz + + steps: + - checkout: self + + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + displayName: Setup Xcode + + - task: DownloadPipelineArtifact@2 + displayName: Download HermesC artifact + inputs: + artifact: hermesc-apple + path: $(Build.SourcesDirectory)/build_host_hermesc + + - script: | + chmod +x ./build_host_hermesc/bin/hermesc + + node .ado/scripts/build-apple.mts \ + --no-build-hermesc \ + --slice ${{ slice.Name }} \ + --configuration ${{ flavor.Name }} \ + --no-build-xcframework + + # Rename for artifact uniqueness and compress to preserve symlinks + mv "build_${{ slice.Name }}" "build_${{ slice.Name }}_${{ flavor.Name }}" + tar -czf "build_${{ slice.Name }}_${{ flavor.Name }}.tar.gz" "build_${{ slice.Name }}_${{ flavor.Name }}" + displayName: Build ${{ slice.Name }} ${{ flavor.Name }} framework + + # Step 3: Assemble universal xcframework from all slices (per flavor). + - ${{ each flavor in parameters.AppleFlavorMatrix }}: + - job: Build_Apple_XCFramework_${{ flavor.Name }} + displayName: Build Apple XCFramework (${{ flavor.Name }}) + + dependsOn: + - ${{ each slice in parameters.AppleSliceMatrix }}: + - Build_Apple_${{ slice.Name }}_${{ flavor.Name }} + + pool: + name: Azure Pipelines + vmImage: macos-15 + os: macOS + + timeoutInMinutes: 60 + + variables: + - name: skipComponentGovernanceDetection + value: true + - name: ONEES_ENFORCED_CODEQL_ENABLED + value: false + + templateContext: + outputs: + - output: pipelineArtifact + artifactName: apple-hermes-xcframework-${{ flavor.Name }} + targetPath: $(Build.SourcesDirectory)/destroot + - output: pipelineArtifact + artifactName: apple-hermes-dsym-${{ flavor.Name }} + targetPath: $(Build.SourcesDirectory)/hermes-dSYM-${{ flavor.Name }}.tar.gz + + steps: + - checkout: self + + - task: UseNode@1 + displayName: Use Node.js 22.x + inputs: + version: 22.x + + - script: | + sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer + displayName: Setup Xcode + + # Download all slice artifacts for this flavor. + - ${{ each slice in parameters.AppleSliceMatrix }}: + - task: DownloadPipelineArtifact@2 + displayName: Download ${{ slice.Name }} ${{ flavor.Name }} slice + inputs: + artifact: apple-slice-${{ slice.Name }}-${{ flavor.Name }} + path: $(Build.SourcesDirectory) + + # Untar and rename all slices back to build_ for the script. + - script: | + set -euo pipefail + SLICES="macosx iphoneos iphonesimulator appletvos appletvsimulator catalyst xros xrsimulator" + for SLICE in $SLICES; do + tar -xzf "build_${SLICE}_${{ flavor.Name }}.tar.gz" + mv "build_${SLICE}_${{ flavor.Name }}" "build_${SLICE}" + done + displayName: Unpack slice artifacts + + # Assemble universal xcframework and create dSYM archive. + - script: > + node .ado/scripts/build-apple.mts + --no-build-hermesc + --no-build + --configuration ${{ flavor.Name }} + --create-dsym-archive + displayName: Create universal xcframework diff --git a/.ado/scripts/build-apple.mts b/.ado/scripts/build-apple.mts new file mode 100644 index 00000000000..348b5fc2183 --- /dev/null +++ b/.ado/scripts/build-apple.mts @@ -0,0 +1,367 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// +// Script to build Hermes Apple frameworks (macOS, iOS, tvOS, visionOS). +// See the options and printHelp() below for the usage details. +// +// This script is intended to be run in the Azure DevOps pipeline +// or locally with Node.js on a macOS host. +// It delegates to the upstream shell scripts (build-apple-framework.sh, +// build-ios-framework.sh) which handle CMake configuration, compilation, +// and xcframework assembly. +// +// Usage: +// node build-apple.mts # Full local build (hermesc + all slices + xcframework) +// node build-apple.mts --slice macosx # Build hermesc + macosx slice + xcframework +// node build-apple.mts --no-build --no-build-xcframework # CI: hermesc only +// + +import { execSync } from "node:child_process"; +import { existsSync, mkdtempSync, mkdirSync, cpSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { parseArgs } from "node:util"; +import { fileURLToPath } from "node:url"; + +const __filename: string = fileURLToPath(import.meta.url); +const __dirname: string = path.dirname(__filename); + +// The root of the local Hermes repository. +const sourcesPath: string = path.resolve(__dirname, path.join("..", "..")); + +const APPLE_SLICES: string[] = [ + "macosx", + "iphoneos", + "iphonesimulator", + "catalyst", + "appletvos", + "appletvsimulator", + "xros", + "xrsimulator", +]; + +const options = { + help: { type: "boolean" as const, default: false }, + + // Action flags — all default true so a bare invocation builds everything. + // CI uses --no-build-hermesc, --no-build, --no-build-xcframework to run + // individual phases, just like build.js uses --no-build --test etc. + "build-hermesc": { type: "boolean" as const, default: true }, + build: { type: "boolean" as const, default: true }, + "build-xcframework": { type: "boolean" as const, default: true }, + + // Scoping — when empty, all slices are built. + slice: { + type: "string" as const, + multiple: true as const, + default: [] as string[], + }, + + // Build configuration. + configuration: { + type: "string" as const, + default: "release", + }, + + // Deployment targets. + "ios-deployment-target": { type: "string" as const, default: "15.1" }, + "macos-deployment-target": { type: "string" as const, default: "10.15" }, + "xros-deployment-target": { type: "string" as const, default: "1.0" }, + + // CI helpers. + "create-dsym-archive": { type: "boolean" as const, default: false }, +}; + +const validSets: Record = { + slice: APPLE_SLICES, + configuration: ["debug", "release"], +}; + +const { values: args } = parseArgs({ options, allowNegative: true }); + +const scriptRelativePath: string = path.relative(process.cwd(), __filename); + +if (args.help) { + printHelp(); + process.exit(0); +} + +function printHelp(): void { + console.log(`Usage: node ${scriptRelativePath} [options] + +Build Hermes Apple frameworks. Requires macOS with Xcode installed. + +All boolean flags support negation (e.g., --no-build, --no-build-hermesc). +With no flags, a full build is performed: hermesc -> all slices -> xcframework. + +Options: + --help Show this help message and exit + --build-hermesc Build the host hermesc compiler (default: true) + --build Build platform slice(s) (default: true) + --build-xcframework Assemble slices into universal xcframework (default: true) + --slice Specific slice(s) to build (default: all) + [valid: ${APPLE_SLICES.join(", ")}] + --configuration Build configuration (default: release) + [valid: debug, release] + --ios-deployment-target iOS/tvOS deployment target (default: 15.1) + --macos-deployment-target macOS deployment target (default: 10.15) + --xros-deployment-target visionOS deployment target (default: 1.0) + --create-dsym-archive Create dSYM tar.gz archive (default: false) + +Examples (local full build): + node ${scriptRelativePath} # Build everything + node ${scriptRelativePath} --configuration debug # Full debug build + node ${scriptRelativePath} --slice macosx --slice iphoneos # Specific slices only + +Examples (CI — selective phases): + node ${scriptRelativePath} --no-build --no-build-xcframework # hermesc only + node ${scriptRelativePath} --no-build-hermesc --slice iphoneos --no-build-xcframework # one slice + node ${scriptRelativePath} --no-build-hermesc --no-build --create-dsym-archive # xcframework + dSYM +`); +} + +// Normalise string args to lower case. +for (const [key, value] of Object.entries(args)) { + if (typeof value === "string") { + (args as Record)[key] = (value as string).toLowerCase(); + } +} + +// Validate args against valid sets. +for (const [key, allowed] of Object.entries(validSets)) { + const raw = (args as Record)[key]; + const values: string[] = Array.isArray(raw) ? raw : [raw as string]; + for (const item of values) { + if (item && !allowed.includes(item)) { + console.error(`Invalid value for --${key}: ${item}`); + console.error(`Valid values are: ${allowed.join(", ")}`); + process.exit(1); + } + } +} + +// ── Main ──────────────────────────────────────────────────────────────────── + +main(); + +function main(): void { + const startTime: number = Date.now(); + const utilsPath: string = path.join(sourcesPath, "utils"); + + const env: NodeJS.ProcessEnv = { + ...process.env, + IOS_DEPLOYMENT_TARGET: args["ios-deployment-target"] as string, + XROS_DEPLOYMENT_TARGET: args["xros-deployment-target"] as string, + MAC_DEPLOYMENT_TARGET: args["macos-deployment-target"] as string, + }; + + const configuration: string = args.configuration as string; + const slices: string[] = + (args.slice as string[]).length > 0 + ? (args.slice as string[]) + : APPLE_SLICES; + + console.log(); + console.log("Apple build invoked with parameters:"); + console.log(` build-hermesc: ${args["build-hermesc"]}`); + console.log(` build (slices): ${args.build}`); + console.log(` build-xcframework: ${args["build-xcframework"]}`); + console.log(` slice: ${slices.join(", ")}`); + console.log(` configuration: ${configuration}`); + console.log(` ios-deployment-target: ${env.IOS_DEPLOYMENT_TARGET}`); + console.log(` macos-deployment-target: ${env.MAC_DEPLOYMENT_TARGET}`); + console.log(` xros-deployment-target: ${env.XROS_DEPLOYMENT_TARGET}`); + console.log(` create-dsym-archive: ${args["create-dsym-archive"]}`); + console.log(); + + // Phase 1: Build the host hermesc compiler. + if (args["build-hermesc"]) { + buildHermesc(utilsPath, env); + } + + // Phase 2: Build platform slices. + if (args.build) { + for (const slice of slices) { + buildSlice(utilsPath, slice, configuration, env); + } + } + + // Phase 3: Assemble universal xcframework. + if (args["build-xcframework"]) { + buildXCFramework(utilsPath, configuration, env); + } + + // Phase 4 (optional): Create dSYM archive. + if (args["create-dsym-archive"]) { + createDsymArchive(configuration); + } + + const elapsed: number = Date.now() - startTime; + const totalTime: string = new Date(elapsed).toISOString().substring(11, 19); + console.log(`\nApple build completed in ${totalTime}`); +} + +// ── Helpers ───────────────────────────────────────────────────────────────── + +function run(command: string, env: NodeJS.ProcessEnv): void { + console.log(`> ${command}`); + execSync(command, { + stdio: "inherit", + cwd: sourcesPath, + env, + shell: "/bin/bash", + }); +} + +function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); +} + +// ── Build phases ──────────────────────────────────────────────────────────── + +function buildHermesc(utilsPath: string, env: NodeJS.ProcessEnv): void { + console.log("Building host hermesc compiler..."); + const script: string = path.join(utilsPath, "build-apple-framework.sh"); + run( + `set -eo pipefail && source "${script}" && build_host_hermesc_if_needed`, + env, + ); + + // Validate the build produced the expected outputs. + const hermescBin: string = path.join( + sourcesPath, + "build_host_hermesc", + "bin", + "hermesc", + ); + const importCmake: string = path.join( + sourcesPath, + "build_host_hermesc", + "ImportHostCompilers.cmake", + ); + if (!existsSync(hermescBin)) { + throw new Error(`hermesc binary not found at ${hermescBin}`); + } + if (!existsSync(importCmake)) { + throw new Error(`ImportHostCompilers.cmake not found at ${importCmake}`); + } + console.log("Host hermesc built successfully.\n"); +} + +function buildSlice( + utilsPath: string, + slice: string, + configuration: string, + env: NodeJS.ProcessEnv, +): void { + console.log(`Building Apple ${slice} (${configuration})...`); + + // If hermesc was already built (or downloaded from CI), point to it so the + // upstream script doesn't try to rebuild it. + const importCmake: string = path.join( + sourcesPath, + "build_host_hermesc", + "ImportHostCompilers.cmake", + ); + const buildEnv: NodeJS.ProcessEnv = { + ...env, + BUILD_TYPE: capitalize(configuration), + ...(existsSync(importCmake) + ? { HERMES_OVERRIDE_HERMESC_PATH: importCmake } + : {}), + }; + + const script: string = path.join(utilsPath, "build-ios-framework.sh"); + run(`"${script}" "${slice}"`, buildEnv); + + // Validate outputs. + const buildDir: string = path.join(sourcesPath, `build_${slice}`); + const frameworkPath: string = path.join( + buildDir, + "lib", + "hermesvm.framework", + ); + const dsymPath: string = path.join( + buildDir, + "lib", + "hermesvm.framework.dSYM", + ); + + if (!existsSync(frameworkPath)) { + throw new Error( + `hermesvm.framework not found at ${frameworkPath} — ${slice} (${configuration}) build failed`, + ); + } + if (!existsSync(dsymPath)) { + throw new Error( + `hermesvm.framework.dSYM not found at ${dsymPath} — ${slice} (${configuration}) build failed`, + ); + } + + console.log(`${slice} (${configuration}) built successfully.\n`); +} + +function buildXCFramework( + utilsPath: string, + configuration: string, + env: NodeJS.ProcessEnv, +): void { + console.log(`Assembling universal xcframework (${configuration})...`); + const buildEnv: NodeJS.ProcessEnv = { + ...env, + BUILD_TYPE: capitalize(configuration), + }; + + // Prepare the destroot from pre-built slices. + const frameworkScript: string = path.join( + utilsPath, + "build-apple-framework.sh", + ); + run(`source "${frameworkScript}" && prepare_dest_root_for_ci`, buildEnv); + + // Create the universal xcframework. + const iosScript: string = path.join(utilsPath, "build-ios-framework.sh"); + run(`"${iosScript}" build_framework`, buildEnv); + + // Validate output. + const xcframeworkPath: string = path.join( + sourcesPath, + "destroot", + "Library", + "Frameworks", + "universal", + "hermesvm.xcframework", + ); + if (!existsSync(xcframeworkPath)) { + throw new Error( + `hermesvm.xcframework not found at ${xcframeworkPath} — assembly failed`, + ); + } + + console.log("Universal xcframework assembled successfully.\n"); +} + +function createDsymArchive(configuration: string): void { + console.log(`Creating dSYM archive (${configuration})...`); + + const workingDir: string = mkdtempSync(path.join(tmpdir(), "hermes-dsym-")); + const dsymRelPath = "lib/hermesvm.framework.dSYM"; + + for (const slice of APPLE_SLICES) { + const src: string = path.join(sourcesPath, `build_${slice}`, dsymRelPath); + const dest: string = path.join(workingDir, slice); + mkdirSync(dest, { recursive: true }); + cpSync(src, path.join(dest, "hermesvm.framework.dSYM"), { + recursive: true, + }); + } + + const archiveName = `hermes-dSYM-${capitalize(configuration)}.tar.gz`; + execSync(`tar -C "${workingDir}" -czf "${archiveName}" .`, { + stdio: "inherit", + cwd: sourcesPath, + }); + + console.log(`dSYM archive created: ${archiveName}\n`); +} diff --git a/hermes-engine.podspec b/hermes-engine.podspec index 16804b0b731..9700db355a9 100644 --- a/hermes-engine.podspec +++ b/hermes-engine.podspec @@ -33,7 +33,7 @@ Pod::Spec.new do |spec| spec.ios.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework" spec.visionos.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework" spec.tvos.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework" - spec.osx.vendored_frameworks = "destroot/Library/Frameworks/macosx/hermesvm.framework" + spec.osx.vendored_frameworks = "destroot/Library/Frameworks/universal/hermesvm.xcframework" spec.xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", "CLANG_CXX_LIBRARY" => "compiler-default", "GCC_PREPROCESSOR_DEFINITIONS" => "HERMES_ENABLE_DEBUGGER=1" } @@ -43,11 +43,8 @@ Pod::Spec.new do |spec| # See `build-apple-framework.sh` for details DEBUG=#{HermesHelper::BUILD_TYPE == :debug} - # Build iOS framework + # Build all Apple frameworks (iOS, macOS, tvOS, visionOS, catalyst) ./utils/build-ios-framework.sh - - # Build Mac framework - ./utils/build-mac-framework.sh EOS end end diff --git a/utils/build-apple-framework.sh b/utils/build-apple-framework.sh index fb65f811947..48fe4a359ae 100755 --- a/utils/build-apple-framework.sh +++ b/utils/build-apple-framework.sh @@ -45,6 +45,7 @@ function get_mac_deployment_target { } function get_release_version { + # Try npm/hermes-compiler/package.json first (facebook/hermes upstream). local package_json_path="$HERMES_COMPILER_PACKAGE_PATH/package.json" if [[ -f "$package_json_path" ]]; then local version @@ -52,21 +53,31 @@ function get_release_version { if [[ -n "$version" ]]; then echo "$version" return - else - echo >&2 "Error: Failed to read version from $package_json_path" - exit 1 fi - else - echo >&2 "Error: Package file not found at $package_json_path" - exit 1 fi + + # Fall back to the version in CMakeLists.txt (hermes-windows). + local cmake_version + cmake_version=$(grep -oP 'VERSION \K[0-9]+\.[0-9]+\.[0-9]+' "$HERMES_PATH/CMakeLists.txt" 2>/dev/null | head -1) + if [[ -n "$cmake_version" ]]; then + echo "$cmake_version" + return + fi + + echo >&2 "Error: Could not determine Hermes release version" + exit 1 } # Build host hermes compiler for internal bytecode function build_host_hermesc { echo "Building hermesc" pushd "$HERMES_PATH" > /dev/null || exit 1 - cmake -S . -B build_host_hermesc -DJSI_DIR="$JSI_PATH" -DCMAKE_BUILD_TYPE=Release + cmake -S . -B build_host_hermesc -DJSI_DIR="$JSI_PATH" -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_SYSTEM_NAME=Darwin \ + -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="10.15" \ + -DHAVE_CXX_ATOMICS_WITHOUT_LIB=ON \ + -DHAVE_CXX_ATOMICS64_WITHOUT_LIB=ON cmake --build ./build_host_hermesc --target hermesc -j "${NUM_CORES}" popd > /dev/null || exit 1 } diff --git a/utils/build-ios-framework.sh b/utils/build-ios-framework.sh index 3c2106efc2b..2888efd0966 100755 --- a/utils/build-ios-framework.sh +++ b/utils/build-ios-framework.sh @@ -10,7 +10,7 @@ fi set -e # Given a specific target, retrieve the right architecture for it -# $1 the target you want to build. Allowed values: iphoneos, iphonesimulator, catalyst, xros, xrsimulator +# $1 the target you want to build. Allowed values: iphoneos, iphonesimulator, catalyst, macosx, xros, xrsimulator function get_architecture { if [[ $1 == "iphoneos" || $1 == "xros" ]]; then echo "arm64" @@ -20,7 +20,7 @@ function get_architecture { echo "arm64" elif [[ $1 == "appletvsimulator" ]]; then echo "x86_64;arm64" - elif [[ $1 == "catalyst" ]]; then + elif [[ $1 == "catalyst" || $1 == "macosx" ]]; then echo "x86_64;arm64" else echo "Error: unknown architecture passed $1" @@ -29,7 +29,9 @@ function get_architecture { } function get_deployment_target { - if [[ $1 == "xros" || $1 == "xrsimulator" ]]; then + if [[ $1 == "macosx" ]]; then + echo "$(get_mac_deployment_target)" + elif [[ $1 == "xros" || $1 == "xrsimulator" ]]; then echo "$(get_visionos_deployment_target)" else # tvOS and iOS use the same deployment target echo "$(get_ios_deployment_target)" @@ -53,7 +55,7 @@ function build_framework { # group the frameworks together to create a universal framework function build_universal_framework { if [ ! -d destroot/Library/Frameworks/universal/hermesvm.xcframework ]; then - create_universal_framework "iphoneos" "iphonesimulator" "catalyst" "xros" "xrsimulator" "appletvos" "appletvsimulator" + create_universal_framework "macosx" "iphoneos" "iphonesimulator" "catalyst" "xros" "xrsimulator" "appletvos" "appletvsimulator" else echo "Skipping; Clean \"destroot\" to rebuild". fi @@ -63,6 +65,7 @@ function build_universal_framework { # this is used to preserve backward compatibility function create_framework { if [ ! -d destroot/Library/Frameworks/universal/hermesvm.xcframework ]; then + build_framework "macosx" build_framework "iphoneos" build_framework "iphonesimulator" build_framework "appletvos"