expo-modules-jsi 56.0.1 → 56.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/apple/ExpoModulesJSI.podspec +0 -1
- package/apple/Package.swift +20 -8
- package/apple/Sources/ExpoModulesJSI/Runtime/JavaScriptRuntime.swift +34 -7
- package/apple/Sources/ExpoModulesJSI-Cxx/include/RuntimeScheduler.h +42 -31
- package/apple/scripts/build-xcframework.sh +99 -75
- package/apple/scripts/create-stub-xcframework.sh +30 -75
- package/apple/scripts/generate-modulemap.sh +18 -6
- package/apple/scripts/xcframework-helpers.sh +124 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.3 — 2026-05-11
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Fixed launch-time crash in apps with source-built React Native. ([#45636](https://github.com/expo/expo/pull/45636) by [@tsapeta](https://github.com/tsapeta))
|
|
18
|
+
|
|
19
|
+
## 56.0.2 — 2026-05-08
|
|
20
|
+
|
|
21
|
+
### 🐛 Bug fixes
|
|
22
|
+
|
|
23
|
+
- [iOS] Fixed `ExpoModulesJSI.xcframework` build failing under `useFrameworks: "static"` + `buildReactNativeFromSource: true` due to missing header search paths. ([#45508](https://github.com/expo/expo/pull/45508) by [@chrfalch](https://github.com/chrfalch))
|
|
24
|
+
- [iOS] Fixed missing slices in `ExpoModulesJSI.xcframework` causing `No such module 'ExpoModulesJSI'` build errors. ([#45542](https://github.com/expo/expo/pull/45542) by [@tsapeta](https://github.com/tsapeta))
|
|
25
|
+
|
|
13
26
|
## 56.0.1 — 2026-05-06
|
|
14
27
|
|
|
15
28
|
_This version does not introduce any user-facing changes._
|
|
@@ -51,7 +51,6 @@ Pod::Spec.new do |s|
|
|
|
51
51
|
|
|
52
52
|
s.dependency 'React-Core'
|
|
53
53
|
s.dependency 'ReactCommon'
|
|
54
|
-
s.dependency 'React-runtimescheduler'
|
|
55
54
|
|
|
56
55
|
# Create a stub xcframework if needed, so CocoaPods generates the
|
|
57
56
|
# "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks" build phases.
|
package/apple/Package.swift
CHANGED
|
@@ -7,15 +7,19 @@ import Foundation
|
|
|
7
7
|
let packageDir = URL(fileURLWithPath: #filePath).deletingLastPathComponent().path
|
|
8
8
|
let podsRoot = resolvePodsRoot()
|
|
9
9
|
|
|
10
|
-
// Header roots
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// Header roots for ExpoModulesJSI and ExpoModulesJSI-Cxx. The
|
|
11
|
+
// Pods/Headers/Public paths cover no-frameworks and prebuilt-RN; the trailing
|
|
12
|
+
// entries fall back to canonical sources for the static + source-built RN
|
|
13
|
+
// combo, where each React-X / third-party-deps pod compiles as a static
|
|
14
|
+
// framework and its headers don't get mirrored to Pods/Headers/Public. Clang
|
|
15
|
+
// ignores missing `-I` paths, so they're no-ops elsewhere. `RN_ROOT` is
|
|
16
|
+
// forwarded from build-xcframework.sh (Node-resolved for hoisted monorepos).
|
|
17
|
+
// `REACT_NATIVE_PATH` is exported by Xcode for hosts that build RN from a
|
|
18
|
+
// non-npm location, e.g. Expo Go.
|
|
13
19
|
let publicHeaders = "\(podsRoot)/Headers/Public"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// others place them directly (e.g. boost/preprocessor/), so we include both
|
|
18
|
-
// the Headers/Public root and the per-pod dirs for the nested ones.
|
|
20
|
+
let reactNative = ProcessInfo.processInfo.environment["RN_ROOT"]
|
|
21
|
+
?? ProcessInfo.processInfo.environment["REACT_NATIVE_PATH"]
|
|
22
|
+
?? "\(podsRoot)/../../node_modules/react-native"
|
|
19
23
|
let headerSearchPaths = [
|
|
20
24
|
publicHeaders,
|
|
21
25
|
"\(publicHeaders)/React-jsi",
|
|
@@ -33,6 +37,14 @@ let headerSearchPaths = [
|
|
|
33
37
|
"\(publicHeaders)/DoubleConversion",
|
|
34
38
|
"\(publicHeaders)/fmt",
|
|
35
39
|
"\(publicHeaders)/fast_float",
|
|
40
|
+
"\(reactNative)/ReactCommon",
|
|
41
|
+
"\(reactNative)/ReactCommon/jsi",
|
|
42
|
+
"\(reactNative)/ReactCommon/runtimeexecutor",
|
|
43
|
+
"\(reactNative)/ReactCommon/callinvoker",
|
|
44
|
+
"\(podsRoot)/RCT-Folly",
|
|
45
|
+
"\(podsRoot)/fmt/include",
|
|
46
|
+
"\(podsRoot)/glog/src",
|
|
47
|
+
"\(podsRoot)/DoubleConversion",
|
|
36
48
|
]
|
|
37
49
|
|
|
38
50
|
// Path to the generated module map for the `jsi` Clang module. The
|
|
@@ -38,28 +38,55 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
|
|
|
38
38
|
lazy var runtimeActor: JavaScriptRuntimeActor = JavaScriptRuntimeActor(runtime: self)
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
Creates a runtime from the JSI runtime.
|
|
41
|
+
Creates a runtime from the JSI runtime. The scheduler runs tasks synchronously
|
|
42
|
+
on the caller's thread — for the React-backed runtime, use
|
|
43
|
+
`init(unsafePointer:nativeScheduler:dispatch:)` instead.
|
|
42
44
|
*/
|
|
43
45
|
internal init(_ runtime: facebook.jsi.Runtime) {
|
|
44
46
|
self.pointee = runtime
|
|
45
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
47
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
Creates Hermes runtime.
|
|
51
|
+
Creates a standalone Hermes runtime. Scheduled tasks run synchronously —
|
|
52
|
+
no React scheduler is wired up.
|
|
50
53
|
*/
|
|
51
54
|
public init() {
|
|
52
55
|
self.pointee = expo.createHermesRuntime()
|
|
53
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
56
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
57
60
|
Creates a runtime from a raw pointer to the underlying `facebook.jsi.Runtime`.
|
|
61
|
+
Scheduled tasks run synchronously — for the React-backed runtime, use
|
|
62
|
+
`init(unsafePointer:nativeScheduler:dispatch:)` instead.
|
|
58
63
|
*/
|
|
59
64
|
public init(unsafePointer: UnsafeMutableRawPointer) {
|
|
60
65
|
let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
|
|
61
66
|
self.pointee = runtime
|
|
62
|
-
self.scheduler = expo.RuntimeScheduler(
|
|
67
|
+
self.scheduler = expo.RuntimeScheduler()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
Creates a runtime bound to a host-provided React `RuntimeScheduler`. Calls to
|
|
72
|
+
`schedule(...)` / `.execute(...)` dispatch through `dispatch`, which the host
|
|
73
|
+
implements against the real `react::RuntimeScheduler`. This is the path the
|
|
74
|
+
React Native factory uses.
|
|
75
|
+
|
|
76
|
+
- `unsafePointer`: raw pointer to the underlying `facebook::jsi::Runtime`.
|
|
77
|
+
- `scheduler`: raw pointer to the `react::RuntimeScheduler` instance.
|
|
78
|
+
- `dispatch`: raw pointer to a C function with signature
|
|
79
|
+
`void (*)(void *scheduler, int priority, void (^callback)())`.
|
|
80
|
+
*/
|
|
81
|
+
public init(
|
|
82
|
+
unsafePointer: UnsafeMutableRawPointer,
|
|
83
|
+
scheduler: UnsafeMutableRawPointer,
|
|
84
|
+
dispatch: UnsafeRawPointer
|
|
85
|
+
) {
|
|
86
|
+
let runtime = unsafeBitCast(unsafePointer, to: facebook.jsi.Runtime.self)
|
|
87
|
+
let fn = unsafeBitCast(dispatch, to: expo.RuntimeScheduler.ScheduleFn.self)
|
|
88
|
+
self.pointee = runtime
|
|
89
|
+
self.scheduler = expo.RuntimeScheduler(scheduler, fn)
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
/**
|
|
@@ -348,8 +375,8 @@ open class JavaScriptRuntime: Equatable, @unchecked Sendable {
|
|
|
348
375
|
Schedules a closure to be executed with granted synchronized access to the runtime.
|
|
349
376
|
*/
|
|
350
377
|
public func schedule(priority: SchedulerPriority = .normal, @_implicitSelfCapture _ closure: @escaping @JavaScriptActor () -> sending Void) -> Void {
|
|
351
|
-
let
|
|
352
|
-
scheduler.scheduleTask(
|
|
378
|
+
let cxxPriority = expo.RuntimeScheduler.Priority(rawValue: priority.rawValue) ?? .NormalPriority
|
|
379
|
+
scheduler.scheduleTask(cxxPriority) {
|
|
353
380
|
JavaScriptActor.assumeIsolated(closure)
|
|
354
381
|
}
|
|
355
382
|
}
|
|
@@ -2,65 +2,76 @@
|
|
|
2
2
|
|
|
3
3
|
#ifdef __cplusplus
|
|
4
4
|
|
|
5
|
-
#include <
|
|
5
|
+
#include <atomic>
|
|
6
6
|
#include <swift/bridging>
|
|
7
|
-
#include <jsi/jsi.h>
|
|
8
|
-
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
|
9
|
-
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
|
|
10
|
-
|
|
11
|
-
namespace jsi = facebook::jsi;
|
|
12
|
-
namespace react = facebook::react;
|
|
13
7
|
|
|
14
8
|
namespace expo {
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
|
-
Wrapper
|
|
18
|
-
|
|
11
|
+
Wrapper around React Native's RuntimeScheduler. The native scheduler reference
|
|
12
|
+
and dispatch trampoline are supplied by the host (e.g. ExpoReactNativeFactory)
|
|
13
|
+
at construction time. The xcframework intentionally avoids linking against
|
|
14
|
+
React-runtimescheduler so the prebuilt binary works with hosts that build RN
|
|
15
|
+
either as a dynamic framework or as a static archive — without needing
|
|
16
|
+
-undefined dynamic_lookup to resolve React internals.
|
|
17
|
+
|
|
18
|
+
Priority values mirror facebook::react::SchedulerPriority — kept here as a
|
|
19
|
+
plain enum so we don't include the React header.
|
|
19
20
|
*/
|
|
20
21
|
class RuntimeScheduler {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
public:
|
|
23
|
+
enum class Priority : int {
|
|
24
|
+
ImmediatePriority = 1,
|
|
25
|
+
UserBlockingPriority = 2,
|
|
26
|
+
NormalPriority = 3,
|
|
27
|
+
LowPriority = 4,
|
|
28
|
+
IdlePriority = 5,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
using ScheduleTaskCallback = void(^)();
|
|
23
32
|
|
|
24
33
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
Trampoline implemented by the host — casts `nativeScheduler` back to
|
|
35
|
+
react::RuntimeScheduler* and calls scheduleTask on it. Keeping it as a
|
|
36
|
+
function pointer keeps React types out of this header.
|
|
27
37
|
*/
|
|
38
|
+
using ScheduleFn = void (*)(void *nativeScheduler, int priority, ScheduleTaskCallback callback);
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
void *const nativeScheduler{nullptr};
|
|
42
|
+
const ScheduleFn scheduleFn{nullptr};
|
|
43
|
+
|
|
28
44
|
std::atomic<int> refCount{1};
|
|
29
45
|
|
|
30
46
|
public:
|
|
31
47
|
/**
|
|
32
|
-
Constructs
|
|
33
|
-
|
|
48
|
+
Constructs a scheduler bound to a host-provided native RuntimeScheduler.
|
|
49
|
+
`scheduleTask` dispatches through `fn`, which the host implements against
|
|
50
|
+
the real react::RuntimeScheduler.
|
|
34
51
|
*/
|
|
35
|
-
RuntimeScheduler(
|
|
36
|
-
|
|
37
|
-
reactRuntimeScheduler = binding->getRuntimeScheduler();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
52
|
+
RuntimeScheduler(void *scheduler, ScheduleFn fn) noexcept
|
|
53
|
+
: nativeScheduler(scheduler), scheduleFn(fn) {}
|
|
40
54
|
|
|
41
55
|
/**
|
|
42
|
-
Constructs a no-op scheduler
|
|
43
|
-
|
|
56
|
+
Constructs a no-op scheduler. Scheduled tasks run synchronously on the
|
|
57
|
+
caller's thread — intended for standalone runtimes (e.g. tests) that have
|
|
58
|
+
no React scheduler.
|
|
44
59
|
*/
|
|
45
60
|
RuntimeScheduler() {}
|
|
46
61
|
|
|
47
|
-
RuntimeScheduler(const RuntimeScheduler &) = delete;
|
|
62
|
+
RuntimeScheduler(const RuntimeScheduler &) = delete;
|
|
48
63
|
|
|
49
64
|
/**
|
|
50
65
|
Whether the scheduler can dispatch work asynchronously to the JS thread.
|
|
51
66
|
When false, tasks run synchronously and callers should avoid dispatching to background queues.
|
|
52
67
|
*/
|
|
53
68
|
bool supportsAsyncScheduling() const noexcept {
|
|
54
|
-
return
|
|
69
|
+
return scheduleFn != nullptr;
|
|
55
70
|
}
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (reactRuntimeScheduler) {
|
|
61
|
-
reactRuntimeScheduler->scheduleTask(priority, [callback = std::move(callback)](jsi::Runtime &runtime) {
|
|
62
|
-
callback();
|
|
63
|
-
});
|
|
72
|
+
void scheduleTask(Priority priority, ScheduleTaskCallback callback) noexcept {
|
|
73
|
+
if (scheduleFn != nullptr) {
|
|
74
|
+
scheduleFn(nativeScheduler, static_cast<int>(priority), callback);
|
|
64
75
|
} else {
|
|
65
76
|
callback();
|
|
66
77
|
}
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
# but can also be invoked manually with PODS_ROOT set.
|
|
7
7
|
#
|
|
8
8
|
# Features:
|
|
9
|
-
# -
|
|
10
|
-
#
|
|
9
|
+
# - Per-slice hash-based caching: a slice is rebuilt only when its own
|
|
10
|
+
# recorded source hash differs from the current one. Other slices —
|
|
11
|
+
# including those built for other platforms in earlier runs — are left
|
|
12
|
+
# untouched.
|
|
11
13
|
# - Reads React/JSI/Hermes headers directly from Pods/Headers/Public, so the
|
|
12
14
|
# same configuration works for both prebuilt and source-built React Native
|
|
13
15
|
# - Cleans .swiftinterface files for cross-compiler compatibility
|
|
@@ -20,13 +22,11 @@
|
|
|
20
22
|
# PLATFORM_NAME (optional) Build for a specific platform (e.g. iphoneos, iphonesimulator).
|
|
21
23
|
# When unset, builds for both iphoneos and iphonesimulator.
|
|
22
24
|
|
|
23
|
-
set -
|
|
25
|
+
set -euo pipefail
|
|
24
26
|
|
|
25
27
|
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
26
28
|
PACKAGE_NAME="ExpoModulesJSI"
|
|
27
29
|
XCFRAMEWORK_PATH="${PACKAGE_DIR}/Products/${PACKAGE_NAME}.xcframework"
|
|
28
|
-
SLICES_DIR="${PACKAGE_DIR}/.xcframework-slices"
|
|
29
|
-
HASH_FILE="${SLICES_DIR}/.build-hash"
|
|
30
30
|
|
|
31
31
|
CONFIGURATION="Release"
|
|
32
32
|
DERIVED_DATA_PATH="${PACKAGE_DIR}/.DerivedData"
|
|
@@ -34,6 +34,8 @@ SPM_BUILD_PATH="${PACKAGE_DIR}/.build"
|
|
|
34
34
|
SPM_WORKSPACE_PATH="${PACKAGE_DIR}/.swiftpm"
|
|
35
35
|
BUILD_PRODUCTS_PATH="${DERIVED_DATA_PATH}/Build/Products"
|
|
36
36
|
|
|
37
|
+
source "${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
|
|
38
|
+
|
|
37
39
|
CLEAN=false
|
|
38
40
|
|
|
39
41
|
while [[ $# -gt 0 ]]; do
|
|
@@ -49,7 +51,6 @@ while [[ $# -gt 0 ]]; do
|
|
|
49
51
|
esac
|
|
50
52
|
done
|
|
51
53
|
|
|
52
|
-
# Use colors only when stdout is a terminal.
|
|
53
54
|
if [[ -t 1 ]]; then
|
|
54
55
|
BLUE="\033[34m"
|
|
55
56
|
RESET="\033[0m"
|
|
@@ -71,9 +72,10 @@ SOURCE_DIRS=(
|
|
|
71
72
|
SOURCE_FILES=(
|
|
72
73
|
"${PACKAGE_DIR}/Package.swift"
|
|
73
74
|
"${PACKAGE_DIR}/scripts/build-xcframework.sh"
|
|
75
|
+
"${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
|
|
76
|
+
"${PACKAGE_DIR}/scripts/xcframework-helpers.sh"
|
|
74
77
|
)
|
|
75
78
|
|
|
76
|
-
# Computes a SHA256 hash of all source files.
|
|
77
79
|
compute_hash() {
|
|
78
80
|
local all_files
|
|
79
81
|
all_files=$(
|
|
@@ -87,8 +89,10 @@ compute_hash() {
|
|
|
87
89
|
# Force C locale so sort order is consistent regardless of the environment.
|
|
88
90
|
# Xcode build phases run without locale variables, which changes sort ordering.
|
|
89
91
|
(
|
|
90
|
-
# Include PODS_ROOT so switching between worktrees
|
|
92
|
+
# Include PODS_ROOT and RN_ROOT so switching between worktrees or RN
|
|
93
|
+
# sources invalidates the cache.
|
|
91
94
|
echo "PODS_ROOT=${PODS_ROOT:-}"
|
|
95
|
+
echo "RN_ROOT=${RN_ROOT:-}"
|
|
92
96
|
echo "$all_files" | LC_ALL=C sort | while IFS= read -r file; do
|
|
93
97
|
echo "$file"
|
|
94
98
|
cat "$file"
|
|
@@ -111,11 +115,30 @@ platform_destination() {
|
|
|
111
115
|
esac
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
#
|
|
118
|
+
# Resolves the xcframework slice ID that a built platform should land in.
|
|
119
|
+
# Mirrors the slice IDs xcodebuild -create-xcframework would have assigned for
|
|
120
|
+
# a single-arch device build / dual-arch simulator build.
|
|
121
|
+
platform_slice_id() {
|
|
122
|
+
case "$1" in
|
|
123
|
+
iphoneos) echo "ios-arm64" ;;
|
|
124
|
+
iphonesimulator) echo "ios-arm64_x86_64-simulator" ;;
|
|
125
|
+
appletvos) echo "tvos-arm64" ;;
|
|
126
|
+
appletvsimulator) echo "tvos-arm64_x86_64-simulator" ;;
|
|
127
|
+
*)
|
|
128
|
+
log "error: No slice mapping for platform: $1"
|
|
129
|
+
exit 1
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# Builds a single framework slice for the given platform and replaces the
|
|
135
|
+
# matching slice inside XCFRAMEWORK_PATH. Other slices on disk are untouched.
|
|
115
136
|
build_slice() {
|
|
116
137
|
local platform="$1"
|
|
117
138
|
local destination
|
|
118
139
|
destination=$(platform_destination "$platform")
|
|
140
|
+
local slice_id
|
|
141
|
+
slice_id=$(platform_slice_id "$platform")
|
|
119
142
|
local build_dir_name="${CONFIGURATION}-${platform}"
|
|
120
143
|
|
|
121
144
|
log "Building framework slice for ${platform}..."
|
|
@@ -124,10 +147,10 @@ build_slice() {
|
|
|
124
147
|
|
|
125
148
|
# Use env -i to clear inherited Xcode environment variables from the parent build.
|
|
126
149
|
# Without this, the nested xcodebuild inherits SDKROOT, PLATFORM_NAME, etc.
|
|
127
|
-
# which causes SDK/platform mismatches. PODS_ROOT
|
|
128
|
-
# because Package.swift reads
|
|
150
|
+
# which causes SDK/platform mismatches. PODS_ROOT and RN_ROOT are forwarded
|
|
151
|
+
# explicitly because Package.swift reads them to resolve header search paths.
|
|
129
152
|
# Run from PACKAGE_DIR so xcodebuild finds the SPM package, not the Pods project.
|
|
130
|
-
(cd "$PACKAGE_DIR" && env -i PATH="$PATH" HOME="$HOME" PODS_ROOT="$PODS_ROOT" \
|
|
153
|
+
(cd "$PACKAGE_DIR" && env -i PATH="$PATH" HOME="$HOME" PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" \
|
|
131
154
|
xcodebuild \
|
|
132
155
|
build \
|
|
133
156
|
-scheme "$PACKAGE_NAME" \
|
|
@@ -148,22 +171,29 @@ build_slice() {
|
|
|
148
171
|
)
|
|
149
172
|
|
|
150
173
|
local product_path="${BUILD_PRODUCTS_PATH}/${build_dir_name}"
|
|
151
|
-
local
|
|
174
|
+
local framework_src="${product_path}/PackageFrameworks/${PACKAGE_NAME}.framework"
|
|
152
175
|
local swiftmodule_src="${product_path}/${PACKAGE_NAME}.swiftmodule"
|
|
153
176
|
local generated_maps="${DERIVED_DATA_PATH}/Build/Intermediates.noindex/GeneratedModuleMaps-${platform}"
|
|
154
177
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
178
|
+
if [[ ! -d "$framework_src" ]]; then
|
|
179
|
+
log "error: xcodebuild did not produce ${framework_src}"
|
|
180
|
+
exit 1
|
|
181
|
+
fi
|
|
159
182
|
|
|
160
|
-
|
|
183
|
+
# Replace the slice in place. Stage to a temp location first so a partial
|
|
184
|
+
# write can't leave the xcframework in a broken state.
|
|
185
|
+
local slice_dir="${XCFRAMEWORK_PATH}/${slice_id}"
|
|
186
|
+
local staging_dir="${XCFRAMEWORK_PATH}/.${slice_id}.new"
|
|
187
|
+
rm -rf "$staging_dir"
|
|
188
|
+
mkdir -p "$staging_dir"
|
|
189
|
+
|
|
190
|
+
cp -r "$framework_src" "$staging_dir/"
|
|
161
191
|
if [[ -d "${product_path}/${PACKAGE_NAME}.framework.dSYM" ]]; then
|
|
162
|
-
cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$
|
|
192
|
+
cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$staging_dir/"
|
|
163
193
|
fi
|
|
164
194
|
|
|
165
195
|
# Copy Swift module interfaces and generated headers into the staged framework.
|
|
166
|
-
local modules_dir="${
|
|
196
|
+
local modules_dir="${staging_dir}/${PACKAGE_NAME}.framework/Modules"
|
|
167
197
|
mkdir -p "$modules_dir"
|
|
168
198
|
cp -r "$swiftmodule_src/" "${modules_dir}/${PACKAGE_NAME}.swiftmodule"
|
|
169
199
|
rm -rf "${modules_dir}/${PACKAGE_NAME}.swiftmodule/Project"
|
|
@@ -185,33 +215,15 @@ build_slice() {
|
|
|
185
215
|
find "${modules_dir}/${PACKAGE_NAME}.swiftmodule" -name '*.swiftinterface' \
|
|
186
216
|
-exec sed -i '' '/^extension __ObjC\./,/^}/d;/^@usableFromInline$/{N;/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d;};/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d' {} +
|
|
187
217
|
|
|
188
|
-
local headers_dir="${
|
|
218
|
+
local headers_dir="${staging_dir}/${PACKAGE_NAME}.framework/Headers"
|
|
189
219
|
mkdir -p "$headers_dir"
|
|
190
220
|
cp "${generated_maps}/${PACKAGE_NAME}-Swift.h" "$headers_dir/"
|
|
191
221
|
cp "${generated_maps}/${PACKAGE_NAME}.modulemap" "$headers_dir/module.modulemap"
|
|
192
|
-
}
|
|
193
222
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
rm -rf "$
|
|
197
|
-
|
|
198
|
-
local xcframework_args=()
|
|
199
|
-
for slice_dir in "${SLICES_DIR}"/*/; do
|
|
200
|
-
local framework="${slice_dir}${PACKAGE_NAME}.framework"
|
|
201
|
-
local dsym="${slice_dir}${PACKAGE_NAME}.framework.dSYM"
|
|
202
|
-
if [[ -d "$framework" ]]; then
|
|
203
|
-
xcframework_args+=(-framework "$framework")
|
|
204
|
-
if [[ -d "$dsym" ]]; then
|
|
205
|
-
xcframework_args+=(-debug-symbols "$(cd "$dsym" && pwd)")
|
|
206
|
-
fi
|
|
207
|
-
fi
|
|
208
|
-
done
|
|
209
|
-
|
|
210
|
-
xcodebuild -create-xcframework "${xcframework_args[@]}" -output "$XCFRAMEWORK_PATH"
|
|
211
|
-
|
|
212
|
-
# Write the hash so subsequent builds can skip if nothing changed.
|
|
213
|
-
mkdir -p "$SLICES_DIR"
|
|
214
|
-
echo "$current_hash" > "$HASH_FILE"
|
|
223
|
+
echo "$current_hash" > "${staging_dir}/.build-hash"
|
|
224
|
+
|
|
225
|
+
rm -rf "$slice_dir"
|
|
226
|
+
mv "$staging_dir" "$slice_dir"
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
# --- Main ---
|
|
@@ -233,6 +245,25 @@ fi
|
|
|
233
245
|
# whether PODS_ROOT was passed as relative or absolute.
|
|
234
246
|
PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
|
|
235
247
|
|
|
248
|
+
# Resolve react-native. Order:
|
|
249
|
+
# 1. REACT_NATIVE_PATH env var (set by Xcode from the Podfile's build setting)
|
|
250
|
+
# — for hosts that build RN from a non-npm location, e.g. Expo Go which
|
|
251
|
+
# uses the `react-native-lab/react-native` submodule, not node_modules.
|
|
252
|
+
# 2. `node -p require.resolve(...)` so the script works in any node_modules
|
|
253
|
+
# layout (hoisted monorepos, pnpm/yarn workspaces).
|
|
254
|
+
# 3. Relative fallback from PODS_ROOT for when `node` isn't on PATH.
|
|
255
|
+
# Forwarded to Package.swift and the modulemap generator below.
|
|
256
|
+
if [[ -n "${REACT_NATIVE_PATH:-}" && -d "${REACT_NATIVE_PATH}" ]]; then
|
|
257
|
+
RN_ROOT="$(cd "$REACT_NATIVE_PATH" && pwd)"
|
|
258
|
+
else
|
|
259
|
+
RN_ROOT="$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null \
|
|
260
|
+
|| echo "${PODS_ROOT}/../../node_modules/react-native")"
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
mode="$( [[ -d "${PODS_ROOT}/React-Core-prebuilt/React.xcframework" ]] && echo "prebuilt RN" || echo "source-built RN")"
|
|
264
|
+
[[ -f "${PODS_ROOT}/Target Support Files/React-jsi/React-jsi-umbrella.h" ]] && mode="${mode}, static frameworks"
|
|
265
|
+
log "Detected: ${mode} (RN_ROOT=${RN_ROOT})"
|
|
266
|
+
|
|
236
267
|
# React Native version — forces a rebuild after an RN upgrade. The local
|
|
237
268
|
# podspec is regenerated by `pod install` and only changes when the underlying
|
|
238
269
|
# RN version changes.
|
|
@@ -241,17 +272,21 @@ if [[ -f "${PODS_ROOT}/Local Podspecs/React-Core.podspec.json" ]]; then
|
|
|
241
272
|
fi
|
|
242
273
|
|
|
243
274
|
# Generate the module map for the `jsi` Clang module.
|
|
244
|
-
env PODS_ROOT="$PODS_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
|
|
275
|
+
env PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
|
|
245
276
|
GENERATED_MODULE_MAP="${PACKAGE_DIR}/.generated/module.modulemap"
|
|
246
277
|
SOURCE_FILES+=("$GENERATED_MODULE_MAP")
|
|
247
278
|
|
|
248
279
|
if [[ "$CLEAN" == true ]]; then
|
|
249
|
-
rm -rf "$XCFRAMEWORK_PATH" "$
|
|
250
|
-
log "Cleaned existing xcframework,
|
|
280
|
+
rm -rf "$XCFRAMEWORK_PATH" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
|
|
281
|
+
log "Cleaned existing xcframework, DerivedData, and SwiftPM state"
|
|
282
|
+
# Re-stamp stub slices so the post-clean state matches a fresh `pod install`:
|
|
283
|
+
# CocoaPods reads Info.plist before this script runs, and would fail to
|
|
284
|
+
# resolve any slice not declared there.
|
|
285
|
+
"${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
|
|
251
286
|
fi
|
|
252
287
|
|
|
253
288
|
# Determine which platforms to build.
|
|
254
|
-
if [[ -n "$PLATFORM_NAME" ]]; then
|
|
289
|
+
if [[ -n "${PLATFORM_NAME:-}" ]]; then
|
|
255
290
|
PLATFORMS=("$PLATFORM_NAME")
|
|
256
291
|
else
|
|
257
292
|
PLATFORMS=("iphoneos" "iphonesimulator")
|
|
@@ -259,42 +294,31 @@ fi
|
|
|
259
294
|
|
|
260
295
|
current_hash=$(compute_hash)
|
|
261
296
|
|
|
262
|
-
#
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if [[ ! -d "${SLICES_DIR}/${platform}/${PACKAGE_NAME}.framework" ]]; then
|
|
270
|
-
platforms_to_build+=("$platform")
|
|
271
|
-
fi
|
|
272
|
-
done
|
|
273
|
-
|
|
274
|
-
if [[ ${#platforms_to_build[@]} -eq 0 ]]; then
|
|
275
|
-
log "xcframework is up to date, skipping build"
|
|
276
|
-
exit 0
|
|
277
|
-
fi
|
|
278
|
-
|
|
279
|
-
PLATFORMS=("${platforms_to_build[@]}")
|
|
280
|
-
else
|
|
281
|
-
# Sources changed — remove all staged slices so they get rebuilt.
|
|
282
|
-
# Also wipe DerivedData and .build because Swift Package Manager caches
|
|
283
|
-
# the resolved package graph there and Package.swift reads PODS_ROOT at
|
|
284
|
-
# resolve time; without this, switching PODS_ROOT (e.g. between apps)
|
|
285
|
-
# leaks paths from the previous resolution into the new build.
|
|
286
|
-
log "Source files changed, rebuilding all slices"
|
|
287
|
-
rm -rf "$SLICES_DIR" "$XCFRAMEWORK_PATH" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
|
|
297
|
+
# Filter out platforms whose slice is already up to date.
|
|
298
|
+
platforms_to_build=()
|
|
299
|
+
for platform in "${PLATFORMS[@]}"; do
|
|
300
|
+
slice_id=$(platform_slice_id "$platform")
|
|
301
|
+
slice_hash_file="${XCFRAMEWORK_PATH}/${slice_id}/.build-hash"
|
|
302
|
+
if [[ -f "$slice_hash_file" ]] && [[ "$(cat "$slice_hash_file")" == "$current_hash" ]]; then
|
|
303
|
+
continue
|
|
288
304
|
fi
|
|
305
|
+
platforms_to_build+=("$platform")
|
|
306
|
+
done
|
|
307
|
+
|
|
308
|
+
if [[ ${#platforms_to_build[@]} -eq 0 ]]; then
|
|
309
|
+
log "xcframework slices up to date, skipping build"
|
|
310
|
+
exit 0
|
|
289
311
|
fi
|
|
290
312
|
|
|
313
|
+
PLATFORMS=("${platforms_to_build[@]}")
|
|
314
|
+
|
|
291
315
|
SECONDS=0
|
|
292
316
|
|
|
293
317
|
for platform in "${PLATFORMS[@]}"; do
|
|
294
318
|
build_slice "$platform"
|
|
295
319
|
done
|
|
296
320
|
|
|
297
|
-
|
|
321
|
+
write_xcframework_plist "$XCFRAMEWORK_PATH" "$PACKAGE_NAME"
|
|
298
322
|
|
|
299
|
-
SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done |
|
|
323
|
+
SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done | LC_ALL=C sort | tr '\n' ',' | sed 's/,$//;s/,/, /g')
|
|
300
324
|
log "Built xcframework successfully in ${SECONDS}s (${SLICE_NAMES})"
|
|
@@ -1,102 +1,57 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
# CocoaPods detects
|
|
5
|
-
# "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks"
|
|
6
|
-
# The real xcframework is built by
|
|
3
|
+
# Ensures every required slice in ExpoModulesJSI.xcframework exists with at
|
|
4
|
+
# least a stub binary so CocoaPods detects the package as a dynamic framework
|
|
5
|
+
# and generates the "[CP] Copy XCFrameworks" and "[CP] Embed Pods Frameworks"
|
|
6
|
+
# build phases for every supported platform. The real xcframework is built by
|
|
7
|
+
# build-xcframework.sh at build time.
|
|
8
|
+
#
|
|
9
|
+
# Non-destructive: existing slice binaries (real or stub) are kept; only
|
|
10
|
+
# missing required slices are stamped. Info.plist is rewritten from whatever
|
|
11
|
+
# slices are on disk so additional slices built via Xcode survive.
|
|
7
12
|
|
|
8
|
-
set -
|
|
13
|
+
set -euo pipefail
|
|
9
14
|
|
|
10
15
|
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
11
16
|
PACKAGE_NAME="ExpoModulesJSI"
|
|
12
17
|
XCFRAMEWORK_PATH="${PACKAGE_DIR}/Products/${PACKAGE_NAME}.xcframework"
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
source "$(dirname "${BASH_SOURCE[0]}")/xcframework-helpers.sh"
|
|
20
|
+
|
|
15
21
|
BLUE=""; RESET=""
|
|
16
22
|
if [ -t 1 ]; then
|
|
17
23
|
BLUE="\033[34m"
|
|
18
24
|
RESET="\033[0m"
|
|
19
25
|
fi
|
|
20
|
-
echo -e "${BLUE}[Expo]${RESET}
|
|
21
|
-
|
|
22
|
-
# Platform slices the podspec supports. CocoaPods reads Info.plist at
|
|
23
|
-
# `pod install` time to generate per-slice cases in its xcframework copy
|
|
24
|
-
# script, so every SDK we want to build for must appear here.
|
|
25
|
-
#
|
|
26
|
-
# Format: slice_id|platform|variant|archs
|
|
27
|
-
SLICES=(
|
|
28
|
-
"ios-arm64|ios||arm64"
|
|
29
|
-
"ios-arm64_x86_64-simulator|ios|simulator|arm64 x86_64"
|
|
30
|
-
"tvos-arm64|tvos||arm64"
|
|
31
|
-
"tvos-arm64_x86_64-simulator|tvos|simulator|arm64 x86_64"
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
# Always regenerate the Info.plist so it declares every slice. A previous
|
|
35
|
-
# build may have produced an xcframework with only the target platform's
|
|
36
|
-
# slice (e.g. device-only or simulator-only), which would cause CocoaPods
|
|
37
|
-
# to generate a copy script that is missing the other variant.
|
|
26
|
+
echo -e "${BLUE}[Expo]${RESET} Ensuring required slices in ${PACKAGE_NAME}.xcframework"
|
|
38
27
|
|
|
39
28
|
# The stub binary is only inspected by CocoaPods at install time to detect
|
|
40
29
|
# that this is a dynamic framework — it is never linked against (the real
|
|
41
30
|
# xcframework replaces it before the sources compile). Compile it once and
|
|
42
|
-
# reuse the same binary for every slice.
|
|
43
|
-
STUB_SOURCE="${XCFRAMEWORK_PATH}/.stub-binary"
|
|
31
|
+
# reuse the same binary for every slice that needs stamping.
|
|
44
32
|
mkdir -p "$XCFRAMEWORK_PATH"
|
|
33
|
+
STUB_SOURCE="${XCFRAMEWORK_PATH}/.stub-binary"
|
|
45
34
|
echo "" | clang -x c - -dynamiclib \
|
|
46
35
|
-o "$STUB_SOURCE" \
|
|
47
36
|
-install_name "@rpath/${PACKAGE_NAME}.framework/${PACKAGE_NAME}"
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if [[ ! -f "${slice_dir}/${PACKAGE_NAME}" ]]; then
|
|
55
|
-
cp "$STUB_SOURCE" "${slice_dir}/${PACKAGE_NAME}"
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
arch_entries=""
|
|
59
|
-
for arch in $archs; do
|
|
60
|
-
arch_entries+=" <string>${arch}</string>
|
|
61
|
-
"
|
|
62
|
-
done
|
|
38
|
+
for slice_id in "${EXPO_MODULES_JSI_REQUIRED_SLICE_IDS[@]}"; do
|
|
39
|
+
slice_dir="${XCFRAMEWORK_PATH}/${slice_id}"
|
|
40
|
+
framework_dir="${slice_dir}/${PACKAGE_NAME}.framework"
|
|
41
|
+
binary_path="${framework_dir}/${PACKAGE_NAME}"
|
|
42
|
+
hash_file="${slice_dir}/.build-hash"
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
if [[ -
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
44
|
+
mkdir -p "$framework_dir"
|
|
45
|
+
if [[ ! -f "$binary_path" ]]; then
|
|
46
|
+
cp "$STUB_SOURCE" "$binary_path"
|
|
47
|
+
fi
|
|
48
|
+
# Empty hash marks the slice as a stub. build-xcframework.sh treats any
|
|
49
|
+
# mismatch (including stub vs. real source hash) as needing a rebuild.
|
|
50
|
+
if [[ ! -f "$hash_file" ]]; then
|
|
51
|
+
: > "$hash_file"
|
|
69
52
|
fi
|
|
70
|
-
|
|
71
|
-
available_libraries+=" <dict>
|
|
72
|
-
<key>BinaryPath</key>
|
|
73
|
-
<string>${PACKAGE_NAME}.framework/${PACKAGE_NAME}</string>
|
|
74
|
-
<key>LibraryIdentifier</key>
|
|
75
|
-
<string>${slice_id}</string>
|
|
76
|
-
<key>LibraryPath</key>
|
|
77
|
-
<string>${PACKAGE_NAME}.framework</string>
|
|
78
|
-
<key>SupportedArchitectures</key>
|
|
79
|
-
<array>
|
|
80
|
-
${arch_entries} </array>
|
|
81
|
-
<key>SupportedPlatform</key>
|
|
82
|
-
<string>${platform}</string>
|
|
83
|
-
${variant_entry} </dict>
|
|
84
|
-
"
|
|
85
53
|
done
|
|
54
|
+
|
|
86
55
|
rm -f "$STUB_SOURCE"
|
|
87
56
|
|
|
88
|
-
|
|
89
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
90
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
91
|
-
<plist version="1.0">
|
|
92
|
-
<dict>
|
|
93
|
-
<key>AvailableLibraries</key>
|
|
94
|
-
<array>
|
|
95
|
-
${available_libraries} </array>
|
|
96
|
-
<key>CFBundlePackageType</key>
|
|
97
|
-
<string>XFWK</string>
|
|
98
|
-
<key>XCFrameworkFormatVersion</key>
|
|
99
|
-
<string>1.0</string>
|
|
100
|
-
</dict>
|
|
101
|
-
</plist>
|
|
102
|
-
PLIST
|
|
57
|
+
write_xcframework_plist "$XCFRAMEWORK_PATH" "$PACKAGE_NAME"
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
# Writes `.generated/module.modulemap` for the `jsi` Clang module.
|
|
4
4
|
#
|
|
5
5
|
# The umbrella header is referenced by absolute path so the modulemap works
|
|
6
|
-
# regardless of where Pods lives. The
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# regardless of where Pods lives. The `Pods/Headers/Public` path exists in
|
|
7
|
+
# non-frameworks and prebuilt-RN layouts; under static frameworks +
|
|
8
|
+
# source-built RN it doesn't, so we fall back to the canonical RN source.
|
|
9
|
+
# Stored outside `.build/` so SwiftPM state can be wiped without losing this
|
|
10
|
+
# file.
|
|
9
11
|
#
|
|
10
|
-
# Idempotent: re-running with the same
|
|
12
|
+
# Idempotent: re-running with the same inputs rewrites identical content.
|
|
11
13
|
# Switching PODS_ROOT updates the umbrella header path.
|
|
12
14
|
#
|
|
13
15
|
# Used by build-xcframework.sh and test.sh; can also be run manually before
|
|
@@ -34,10 +36,20 @@ GENERATED_DIR="${PACKAGE_DIR}/.generated"
|
|
|
34
36
|
GENERATED_MODULE_MAP="${GENERATED_DIR}/module.modulemap"
|
|
35
37
|
mkdir -p "$GENERATED_DIR"
|
|
36
38
|
|
|
39
|
+
JSI_UMBRELLA="${PODS_ROOT}/Headers/Public/React-jsi/jsi/jsi.h"
|
|
40
|
+
if [[ ! -f "$JSI_UMBRELLA" ]]; then
|
|
41
|
+
# Resolution order matches build-xcframework.sh: RN_ROOT (forwarded by the
|
|
42
|
+
# build script), REACT_NATIVE_PATH (exported by Xcode for hosts like Expo Go
|
|
43
|
+
# that build RN from a submodule), node resolve, then a relative fallback.
|
|
44
|
+
RN="${RN_ROOT:-${REACT_NATIVE_PATH:-$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null || echo "${PODS_ROOT}/../../node_modules/react-native")}}"
|
|
45
|
+
JSI_UMBRELLA="${RN}/ReactCommon/jsi/jsi/jsi.h"
|
|
46
|
+
fi
|
|
47
|
+
[[ -f "$JSI_UMBRELLA" ]] || { echo "error: cannot locate jsi.h" >&2; exit 1; }
|
|
48
|
+
|
|
37
49
|
# Avoid touching the file when contents would be identical, so the xcframework
|
|
38
|
-
# hash cache and Xcode don't see a spurious change when
|
|
50
|
+
# hash cache and Xcode don't see a spurious change when inputs are unchanged.
|
|
39
51
|
NEW_CONTENT="module jsi {
|
|
40
|
-
umbrella header \"${
|
|
52
|
+
umbrella header \"${JSI_UMBRELLA}\"
|
|
41
53
|
|
|
42
54
|
export *
|
|
43
55
|
module * { export * }
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Shared helpers for managing ExpoModulesJSI.xcframework.
|
|
2
|
+
#
|
|
3
|
+
# Sourced by create-stub-xcframework.sh and build-xcframework.sh. Defines the
|
|
4
|
+
# canonical slice metadata and the Info.plist writer used by both scripts so
|
|
5
|
+
# the manifest is produced from a single place.
|
|
6
|
+
|
|
7
|
+
# All slice IDs known to the xcframework, mapped to their plist metadata.
|
|
8
|
+
# CocoaPods reads Info.plist at `pod install` time and generates a per-slice
|
|
9
|
+
# copy script; any slice missing from this table will be skipped by
|
|
10
|
+
# write_xcframework_plist with a warning.
|
|
11
|
+
#
|
|
12
|
+
# Format: slice_id|platform|variant|archs
|
|
13
|
+
EXPO_MODULES_JSI_KNOWN_SLICES=(
|
|
14
|
+
"ios-arm64|ios||arm64"
|
|
15
|
+
"ios-arm64_x86_64-simulator|ios|simulator|arm64 x86_64"
|
|
16
|
+
"tvos-arm64|tvos||arm64"
|
|
17
|
+
"tvos-arm64_x86_64-simulator|tvos|simulator|arm64 x86_64"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Slice IDs the xcframework must always declare, even when only a subset has
|
|
21
|
+
# been built. The stub script materializes empty placeholders for any of these
|
|
22
|
+
# missing on disk so CocoaPods can wire up copy/embed phases for every target
|
|
23
|
+
# the podspec supports.
|
|
24
|
+
EXPO_MODULES_JSI_REQUIRED_SLICE_IDS=(
|
|
25
|
+
"ios-arm64"
|
|
26
|
+
"ios-arm64_x86_64-simulator"
|
|
27
|
+
"tvos-arm64"
|
|
28
|
+
"tvos-arm64_x86_64-simulator"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# xcframework_slice_descriptor SLICE_ID
|
|
32
|
+
# Echoes the descriptor row for SLICE_ID, or returns 1 if SLICE_ID is unknown.
|
|
33
|
+
xcframework_slice_descriptor() {
|
|
34
|
+
local slice_id="$1"
|
|
35
|
+
local entry
|
|
36
|
+
for entry in "${EXPO_MODULES_JSI_KNOWN_SLICES[@]}"; do
|
|
37
|
+
if [[ "${entry%%|*}" == "$slice_id" ]]; then
|
|
38
|
+
echo "$entry"
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# write_xcframework_plist XCFRAMEWORK_PATH PACKAGE_NAME
|
|
46
|
+
# Writes Info.plist describing every slice directory currently inside
|
|
47
|
+
# XCFRAMEWORK_PATH. Slices are sorted by ID for deterministic output. Slice
|
|
48
|
+
# directories not listed in EXPO_MODULES_JSI_KNOWN_SLICES are skipped with a
|
|
49
|
+
# warning so callers can spot missing metadata instead of silently shipping a
|
|
50
|
+
# slice CocoaPods can't describe.
|
|
51
|
+
write_xcframework_plist() {
|
|
52
|
+
local xcframework_path="$1"
|
|
53
|
+
local package_name="$2"
|
|
54
|
+
|
|
55
|
+
local slice_dirs=()
|
|
56
|
+
local entry
|
|
57
|
+
for entry in "${xcframework_path}"/*/; do
|
|
58
|
+
[[ -d "$entry" ]] || continue
|
|
59
|
+
slice_dirs+=("$(basename "$entry")")
|
|
60
|
+
done
|
|
61
|
+
|
|
62
|
+
local sorted_slices=()
|
|
63
|
+
while IFS= read -r line; do
|
|
64
|
+
sorted_slices+=("$line")
|
|
65
|
+
done < <(printf '%s\n' "${slice_dirs[@]}" | LC_ALL=C sort)
|
|
66
|
+
|
|
67
|
+
local available_libraries=""
|
|
68
|
+
local slice_id
|
|
69
|
+
for slice_id in "${sorted_slices[@]}"; do
|
|
70
|
+
local descriptor
|
|
71
|
+
if ! descriptor=$(xcframework_slice_descriptor "$slice_id"); then
|
|
72
|
+
echo "warning: skipping slice '${slice_id}' — not in EXPO_MODULES_JSI_KNOWN_SLICES" >&2
|
|
73
|
+
continue
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
local platform variant archs
|
|
77
|
+
IFS='|' read -r _ platform variant archs <<<"$descriptor"
|
|
78
|
+
|
|
79
|
+
local arch_entries=""
|
|
80
|
+
local arch
|
|
81
|
+
for arch in $archs; do
|
|
82
|
+
arch_entries+=" <string>${arch}</string>
|
|
83
|
+
"
|
|
84
|
+
done
|
|
85
|
+
|
|
86
|
+
local variant_entry=""
|
|
87
|
+
if [[ -n "$variant" ]]; then
|
|
88
|
+
variant_entry=" <key>SupportedPlatformVariant</key>
|
|
89
|
+
<string>${variant}</string>
|
|
90
|
+
"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
available_libraries+=" <dict>
|
|
94
|
+
<key>BinaryPath</key>
|
|
95
|
+
<string>${package_name}.framework/${package_name}</string>
|
|
96
|
+
<key>LibraryIdentifier</key>
|
|
97
|
+
<string>${slice_id}</string>
|
|
98
|
+
<key>LibraryPath</key>
|
|
99
|
+
<string>${package_name}.framework</string>
|
|
100
|
+
<key>SupportedArchitectures</key>
|
|
101
|
+
<array>
|
|
102
|
+
${arch_entries} </array>
|
|
103
|
+
<key>SupportedPlatform</key>
|
|
104
|
+
<string>${platform}</string>
|
|
105
|
+
${variant_entry} </dict>
|
|
106
|
+
"
|
|
107
|
+
done
|
|
108
|
+
|
|
109
|
+
cat > "${xcframework_path}/Info.plist" <<PLIST
|
|
110
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
111
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
112
|
+
<plist version="1.0">
|
|
113
|
+
<dict>
|
|
114
|
+
<key>AvailableLibraries</key>
|
|
115
|
+
<array>
|
|
116
|
+
${available_libraries} </array>
|
|
117
|
+
<key>CFBundlePackageType</key>
|
|
118
|
+
<string>XFWK</string>
|
|
119
|
+
<key>XCFrameworkFormatVersion</key>
|
|
120
|
+
<string>1.0</string>
|
|
121
|
+
</dict>
|
|
122
|
+
</plist>
|
|
123
|
+
PLIST
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-jsi",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.3",
|
|
4
4
|
"description": "The JavaScript Interface for Expo Modules",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"sideEffects": [],
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"./apple/scripts/test.sh"
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "42013232893cb2aa71ab218e9b422d4a8476b3f0",
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "apple/scripts/build-xcframework.sh",
|
|
47
47
|
"test": "apple/scripts/test.sh"
|