expo-modules-jsi 56.0.1 → 56.0.2
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 +7 -0
- package/apple/Package.swift +17 -8
- package/apple/scripts/build-xcframework.sh +88 -74
- package/apple/scripts/create-stub-xcframework.sh +30 -75
- package/apple/scripts/generate-modulemap.sh +15 -6
- package/apple/scripts/xcframework-helpers.sh +124 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.2 — 2026-05-08
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [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))
|
|
18
|
+
- [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))
|
|
19
|
+
|
|
13
20
|
## 56.0.1 — 2026-05-06
|
|
14
21
|
|
|
15
22
|
_This version does not introduce any user-facing changes._
|
package/apple/Package.swift
CHANGED
|
@@ -7,15 +7,16 @@ 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).
|
|
13
17
|
let publicHeaders = "\(podsRoot)/Headers/Public"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// ReactNativeDependencies. Some pods nest their headers (e.g. RCT-Folly/folly/),
|
|
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.
|
|
18
|
+
let reactNative = ProcessInfo.processInfo.environment["RN_ROOT"]
|
|
19
|
+
?? "\(podsRoot)/../../node_modules/react-native"
|
|
19
20
|
let headerSearchPaths = [
|
|
20
21
|
publicHeaders,
|
|
21
22
|
"\(publicHeaders)/React-jsi",
|
|
@@ -33,6 +34,14 @@ let headerSearchPaths = [
|
|
|
33
34
|
"\(publicHeaders)/DoubleConversion",
|
|
34
35
|
"\(publicHeaders)/fmt",
|
|
35
36
|
"\(publicHeaders)/fast_float",
|
|
37
|
+
"\(reactNative)/ReactCommon",
|
|
38
|
+
"\(reactNative)/ReactCommon/jsi",
|
|
39
|
+
"\(reactNative)/ReactCommon/runtimeexecutor",
|
|
40
|
+
"\(reactNative)/ReactCommon/callinvoker",
|
|
41
|
+
"\(podsRoot)/RCT-Folly",
|
|
42
|
+
"\(podsRoot)/fmt/include",
|
|
43
|
+
"\(podsRoot)/glog/src",
|
|
44
|
+
"\(podsRoot)/DoubleConversion",
|
|
36
45
|
]
|
|
37
46
|
|
|
38
47
|
// Path to the generated module map for the `jsi` Clang module. The
|
|
@@ -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=$(
|
|
@@ -111,11 +113,30 @@ platform_destination() {
|
|
|
111
113
|
esac
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
#
|
|
116
|
+
# Resolves the xcframework slice ID that a built platform should land in.
|
|
117
|
+
# Mirrors the slice IDs xcodebuild -create-xcframework would have assigned for
|
|
118
|
+
# a single-arch device build / dual-arch simulator build.
|
|
119
|
+
platform_slice_id() {
|
|
120
|
+
case "$1" in
|
|
121
|
+
iphoneos) echo "ios-arm64" ;;
|
|
122
|
+
iphonesimulator) echo "ios-arm64_x86_64-simulator" ;;
|
|
123
|
+
appletvos) echo "tvos-arm64" ;;
|
|
124
|
+
appletvsimulator) echo "tvos-arm64_x86_64-simulator" ;;
|
|
125
|
+
*)
|
|
126
|
+
log "error: No slice mapping for platform: $1"
|
|
127
|
+
exit 1
|
|
128
|
+
;;
|
|
129
|
+
esac
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Builds a single framework slice for the given platform and replaces the
|
|
133
|
+
# matching slice inside XCFRAMEWORK_PATH. Other slices on disk are untouched.
|
|
115
134
|
build_slice() {
|
|
116
135
|
local platform="$1"
|
|
117
136
|
local destination
|
|
118
137
|
destination=$(platform_destination "$platform")
|
|
138
|
+
local slice_id
|
|
139
|
+
slice_id=$(platform_slice_id "$platform")
|
|
119
140
|
local build_dir_name="${CONFIGURATION}-${platform}"
|
|
120
141
|
|
|
121
142
|
log "Building framework slice for ${platform}..."
|
|
@@ -124,10 +145,10 @@ build_slice() {
|
|
|
124
145
|
|
|
125
146
|
# Use env -i to clear inherited Xcode environment variables from the parent build.
|
|
126
147
|
# Without this, the nested xcodebuild inherits SDKROOT, PLATFORM_NAME, etc.
|
|
127
|
-
# which causes SDK/platform mismatches. PODS_ROOT
|
|
128
|
-
# because Package.swift reads
|
|
148
|
+
# which causes SDK/platform mismatches. PODS_ROOT and RN_ROOT are forwarded
|
|
149
|
+
# explicitly because Package.swift reads them to resolve header search paths.
|
|
129
150
|
# 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" \
|
|
151
|
+
(cd "$PACKAGE_DIR" && env -i PATH="$PATH" HOME="$HOME" PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" \
|
|
131
152
|
xcodebuild \
|
|
132
153
|
build \
|
|
133
154
|
-scheme "$PACKAGE_NAME" \
|
|
@@ -148,22 +169,29 @@ build_slice() {
|
|
|
148
169
|
)
|
|
149
170
|
|
|
150
171
|
local product_path="${BUILD_PRODUCTS_PATH}/${build_dir_name}"
|
|
151
|
-
local
|
|
172
|
+
local framework_src="${product_path}/PackageFrameworks/${PACKAGE_NAME}.framework"
|
|
152
173
|
local swiftmodule_src="${product_path}/${PACKAGE_NAME}.swiftmodule"
|
|
153
174
|
local generated_maps="${DERIVED_DATA_PATH}/Build/Intermediates.noindex/GeneratedModuleMaps-${platform}"
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
176
|
+
if [[ ! -d "$framework_src" ]]; then
|
|
177
|
+
log "error: xcodebuild did not produce ${framework_src}"
|
|
178
|
+
exit 1
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# Replace the slice in place. Stage to a temp location first so a partial
|
|
182
|
+
# write can't leave the xcframework in a broken state.
|
|
183
|
+
local slice_dir="${XCFRAMEWORK_PATH}/${slice_id}"
|
|
184
|
+
local staging_dir="${XCFRAMEWORK_PATH}/.${slice_id}.new"
|
|
185
|
+
rm -rf "$staging_dir"
|
|
186
|
+
mkdir -p "$staging_dir"
|
|
159
187
|
|
|
160
|
-
cp -r "$
|
|
188
|
+
cp -r "$framework_src" "$staging_dir/"
|
|
161
189
|
if [[ -d "${product_path}/${PACKAGE_NAME}.framework.dSYM" ]]; then
|
|
162
|
-
cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$
|
|
190
|
+
cp -r "${product_path}/${PACKAGE_NAME}.framework.dSYM" "$staging_dir/"
|
|
163
191
|
fi
|
|
164
192
|
|
|
165
193
|
# Copy Swift module interfaces and generated headers into the staged framework.
|
|
166
|
-
local modules_dir="${
|
|
194
|
+
local modules_dir="${staging_dir}/${PACKAGE_NAME}.framework/Modules"
|
|
167
195
|
mkdir -p "$modules_dir"
|
|
168
196
|
cp -r "$swiftmodule_src/" "${modules_dir}/${PACKAGE_NAME}.swiftmodule"
|
|
169
197
|
rm -rf "${modules_dir}/${PACKAGE_NAME}.swiftmodule/Project"
|
|
@@ -185,33 +213,15 @@ build_slice() {
|
|
|
185
213
|
find "${modules_dir}/${PACKAGE_NAME}.swiftmodule" -name '*.swiftinterface' \
|
|
186
214
|
-exec sed -i '' '/^extension __ObjC\./,/^}/d;/^@usableFromInline$/{N;/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d;};/_ConstraintThatIsNotPartOfTheAPIOfThisLibrary/d' {} +
|
|
187
215
|
|
|
188
|
-
local headers_dir="${
|
|
216
|
+
local headers_dir="${staging_dir}/${PACKAGE_NAME}.framework/Headers"
|
|
189
217
|
mkdir -p "$headers_dir"
|
|
190
218
|
cp "${generated_maps}/${PACKAGE_NAME}-Swift.h" "$headers_dir/"
|
|
191
219
|
cp "${generated_maps}/${PACKAGE_NAME}.modulemap" "$headers_dir/module.modulemap"
|
|
192
|
-
}
|
|
193
220
|
|
|
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"
|
|
221
|
+
echo "$current_hash" > "${staging_dir}/.build-hash"
|
|
222
|
+
|
|
223
|
+
rm -rf "$slice_dir"
|
|
224
|
+
mv "$staging_dir" "$slice_dir"
|
|
215
225
|
}
|
|
216
226
|
|
|
217
227
|
# --- Main ---
|
|
@@ -233,6 +243,17 @@ fi
|
|
|
233
243
|
# whether PODS_ROOT was passed as relative or absolute.
|
|
234
244
|
PODS_ROOT="$(cd "$PODS_ROOT" && pwd)"
|
|
235
245
|
|
|
246
|
+
# Resolve react-native via Node so the build works in any node_modules layout
|
|
247
|
+
# (hoisted monorepos, yarn/pnpm workspaces). Falls back to the relative path
|
|
248
|
+
# when `node` is unavailable. Forwarded to Package.swift and the modulemap
|
|
249
|
+
# generator below.
|
|
250
|
+
RN_ROOT="$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null \
|
|
251
|
+
|| echo "${PODS_ROOT}/../../node_modules/react-native")"
|
|
252
|
+
|
|
253
|
+
mode="$( [[ -d "${PODS_ROOT}/React-Core-prebuilt/React.xcframework" ]] && echo "prebuilt RN" || echo "source-built RN")"
|
|
254
|
+
[[ -f "${PODS_ROOT}/Target Support Files/React-jsi/React-jsi-umbrella.h" ]] && mode="${mode}, static frameworks"
|
|
255
|
+
log "Detected: ${mode} (RN_ROOT=${RN_ROOT})"
|
|
256
|
+
|
|
236
257
|
# React Native version — forces a rebuild after an RN upgrade. The local
|
|
237
258
|
# podspec is regenerated by `pod install` and only changes when the underlying
|
|
238
259
|
# RN version changes.
|
|
@@ -241,17 +262,21 @@ if [[ -f "${PODS_ROOT}/Local Podspecs/React-Core.podspec.json" ]]; then
|
|
|
241
262
|
fi
|
|
242
263
|
|
|
243
264
|
# Generate the module map for the `jsi` Clang module.
|
|
244
|
-
env PODS_ROOT="$PODS_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
|
|
265
|
+
env PODS_ROOT="$PODS_ROOT" RN_ROOT="$RN_ROOT" "${PACKAGE_DIR}/scripts/generate-modulemap.sh"
|
|
245
266
|
GENERATED_MODULE_MAP="${PACKAGE_DIR}/.generated/module.modulemap"
|
|
246
267
|
SOURCE_FILES+=("$GENERATED_MODULE_MAP")
|
|
247
268
|
|
|
248
269
|
if [[ "$CLEAN" == true ]]; then
|
|
249
|
-
rm -rf "$XCFRAMEWORK_PATH" "$
|
|
250
|
-
log "Cleaned existing xcframework,
|
|
270
|
+
rm -rf "$XCFRAMEWORK_PATH" "$DERIVED_DATA_PATH" "$SPM_BUILD_PATH" "$SPM_WORKSPACE_PATH"
|
|
271
|
+
log "Cleaned existing xcframework, DerivedData, and SwiftPM state"
|
|
272
|
+
# Re-stamp stub slices so the post-clean state matches a fresh `pod install`:
|
|
273
|
+
# CocoaPods reads Info.plist before this script runs, and would fail to
|
|
274
|
+
# resolve any slice not declared there.
|
|
275
|
+
"${PACKAGE_DIR}/scripts/create-stub-xcframework.sh"
|
|
251
276
|
fi
|
|
252
277
|
|
|
253
278
|
# Determine which platforms to build.
|
|
254
|
-
if [[ -n "$PLATFORM_NAME" ]]; then
|
|
279
|
+
if [[ -n "${PLATFORM_NAME:-}" ]]; then
|
|
255
280
|
PLATFORMS=("$PLATFORM_NAME")
|
|
256
281
|
else
|
|
257
282
|
PLATFORMS=("iphoneos" "iphonesimulator")
|
|
@@ -259,42 +284,31 @@ fi
|
|
|
259
284
|
|
|
260
285
|
current_hash=$(compute_hash)
|
|
261
286
|
|
|
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"
|
|
287
|
+
# Filter out platforms whose slice is already up to date.
|
|
288
|
+
platforms_to_build=()
|
|
289
|
+
for platform in "${PLATFORMS[@]}"; do
|
|
290
|
+
slice_id=$(platform_slice_id "$platform")
|
|
291
|
+
slice_hash_file="${XCFRAMEWORK_PATH}/${slice_id}/.build-hash"
|
|
292
|
+
if [[ -f "$slice_hash_file" ]] && [[ "$(cat "$slice_hash_file")" == "$current_hash" ]]; then
|
|
293
|
+
continue
|
|
288
294
|
fi
|
|
295
|
+
platforms_to_build+=("$platform")
|
|
296
|
+
done
|
|
297
|
+
|
|
298
|
+
if [[ ${#platforms_to_build[@]} -eq 0 ]]; then
|
|
299
|
+
log "xcframework slices up to date, skipping build"
|
|
300
|
+
exit 0
|
|
289
301
|
fi
|
|
290
302
|
|
|
303
|
+
PLATFORMS=("${platforms_to_build[@]}")
|
|
304
|
+
|
|
291
305
|
SECONDS=0
|
|
292
306
|
|
|
293
307
|
for platform in "${PLATFORMS[@]}"; do
|
|
294
308
|
build_slice "$platform"
|
|
295
309
|
done
|
|
296
310
|
|
|
297
|
-
|
|
311
|
+
write_xcframework_plist "$XCFRAMEWORK_PATH" "$PACKAGE_NAME"
|
|
298
312
|
|
|
299
|
-
SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done |
|
|
313
|
+
SLICE_NAMES=$(for d in "${XCFRAMEWORK_PATH}"/*/; do basename "$d"; done | LC_ALL=C sort | tr '\n' ',' | sed 's/,$//;s/,/, /g')
|
|
300
314
|
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,17 @@ 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
|
+
RN="${RN_ROOT:-$(node -p 'require("path").dirname(require.resolve("react-native/package.json"))' 2>/dev/null || echo "${PODS_ROOT}/../../node_modules/react-native")}"
|
|
42
|
+
JSI_UMBRELLA="${RN}/ReactCommon/jsi/jsi/jsi.h"
|
|
43
|
+
fi
|
|
44
|
+
[[ -f "$JSI_UMBRELLA" ]] || { echo "error: cannot locate jsi.h" >&2; exit 1; }
|
|
45
|
+
|
|
37
46
|
# Avoid touching the file when contents would be identical, so the xcframework
|
|
38
|
-
# hash cache and Xcode don't see a spurious change when
|
|
47
|
+
# hash cache and Xcode don't see a spurious change when inputs are unchanged.
|
|
39
48
|
NEW_CONTENT="module jsi {
|
|
40
|
-
umbrella header \"${
|
|
49
|
+
umbrella header \"${JSI_UMBRELLA}\"
|
|
41
50
|
|
|
42
51
|
export *
|
|
43
52
|
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.2",
|
|
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": "a30353e69ca0d72b9fac5830abc631feda1ba3ae",
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "apple/scripts/build-xcframework.sh",
|
|
47
47
|
"test": "apple/scripts/test.sh"
|