expo-modules-autolinking 56.0.9 → 56.0.11
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
CHANGED
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 56.0.11 — 2026-05-21
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [iOS] Update `@shopify/react-native-skia` precompile config for the 2.6.x source layout ([#46081](https://github.com/expo/expo/pull/46081) by [@chrfalch](https://github.com/chrfalch))
|
|
18
|
+
|
|
19
|
+
## 56.0.10 — 2026-05-21
|
|
20
|
+
|
|
21
|
+
### 🎉 New features
|
|
22
|
+
|
|
23
|
+
- [iOS] Include and consume shared SPM dependencies in the precompiled pod / npm publish pipeline. ([#46069](https://github.com/expo/expo/pull/46069) by [@chrfalch](https://github.com/chrfalch))
|
|
24
|
+
|
|
13
25
|
## 56.0.9 — 2026-05-20
|
|
14
26
|
|
|
15
27
|
### 🐛 Bug fixes
|
|
@@ -1,209 +1,199 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"$schema": "../../../../../../tools/src/prebuilds/schemas/spm.config.schema.json",
|
|
3
|
+
"products": [
|
|
4
|
+
{
|
|
5
|
+
"name": "RNSkia",
|
|
6
|
+
"podName": "react-native-skia",
|
|
7
|
+
"codegenName": "rnskia",
|
|
8
|
+
"platforms": ["iOS(.v16)"],
|
|
9
|
+
"externalDependencies": ["ReactNativeDependencies", "React", "Hermes"],
|
|
10
|
+
"targets": [
|
|
4
11
|
{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"AVKit",
|
|
189
|
-
"CoreMedia",
|
|
190
|
-
"QuartzCore",
|
|
191
|
-
"CoreGraphics",
|
|
192
|
-
"CoreText",
|
|
193
|
-
"CoreVideo",
|
|
194
|
-
"CoreImage",
|
|
195
|
-
"IOSurface"
|
|
196
|
-
],
|
|
197
|
-
"compilerFlags": [
|
|
198
|
-
"-include", "Foundation/Foundation.h",
|
|
199
|
-
"-include", "UIKit/UIKit.h",
|
|
200
|
-
"-DSK_METAL=1",
|
|
201
|
-
"-DSK_GANESH=1",
|
|
202
|
-
"-DSK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1",
|
|
203
|
-
"-DSK_DISABLE_LEGACY_SHAPER_FACTORY=1"
|
|
204
|
-
]
|
|
205
|
-
}
|
|
206
|
-
]
|
|
12
|
+
"type": "framework",
|
|
13
|
+
"name": "libskia",
|
|
14
|
+
"path": "libs/ios/libskia.xcframework"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "framework",
|
|
18
|
+
"name": "libsvg",
|
|
19
|
+
"path": "libs/ios/libsvg.xcframework"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "framework",
|
|
23
|
+
"name": "libskshaper",
|
|
24
|
+
"path": "libs/ios/libskshaper.xcframework"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "framework",
|
|
28
|
+
"name": "libskparagraph",
|
|
29
|
+
"path": "libs/ios/libskparagraph.xcframework"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "framework",
|
|
33
|
+
"name": "libskunicode_core",
|
|
34
|
+
"path": "libs/ios/libskunicode_core.xcframework"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "framework",
|
|
38
|
+
"name": "libskunicode_libgrapheme",
|
|
39
|
+
"path": "libs/ios/libskunicode_libgrapheme.xcframework"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "framework",
|
|
43
|
+
"name": "libskottie",
|
|
44
|
+
"path": "libs/ios/libskottie.xcframework"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "framework",
|
|
48
|
+
"name": "libsksg",
|
|
49
|
+
"path": "libs/ios/libsksg.xcframework"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "objc",
|
|
53
|
+
"name": "rnskia_codegen_modules",
|
|
54
|
+
"moduleName": "rnskia",
|
|
55
|
+
"path": ".build/codegen/build/generated/ios/ReactCodegen/rnskia",
|
|
56
|
+
"pattern": "**/*.mm",
|
|
57
|
+
"headerPattern": "**/*.h",
|
|
58
|
+
"dependencies": ["React", "ReactNativeDependencies"],
|
|
59
|
+
"includeDirectories": [".."]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "cpp",
|
|
63
|
+
"name": "rnskia_codegen_components",
|
|
64
|
+
"moduleName": "rnskia",
|
|
65
|
+
"path": ".build/codegen/build/generated/ios/ReactCodegen/react/renderer/components/rnskia",
|
|
66
|
+
"pattern": "**/*.cpp",
|
|
67
|
+
"headerPattern": "**/*.h",
|
|
68
|
+
"dependencies": ["React", "ReactNativeDependencies"],
|
|
69
|
+
"includeDirectories": ["../../../.."],
|
|
70
|
+
"compilerFlags": {
|
|
71
|
+
"common": {
|
|
72
|
+
"cxx": ["-fno-cxx-modules"]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "cpp",
|
|
78
|
+
"name": "RNSkia_cpp",
|
|
79
|
+
"moduleName": "rnskia",
|
|
80
|
+
"path": "cpp",
|
|
81
|
+
"pattern": "**/*.cpp",
|
|
82
|
+
"headerPattern": "**/*.h",
|
|
83
|
+
"exclude": [
|
|
84
|
+
"rnskia/RNDawnContext.h",
|
|
85
|
+
"rnskia/RNDawnUtils.h",
|
|
86
|
+
"rnskia/RNDawnWindowContext.h",
|
|
87
|
+
"rnskia/RNDawnWindowContext.cpp",
|
|
88
|
+
"rnskia/RNImageProvider.h",
|
|
89
|
+
"rnwgpu/**",
|
|
90
|
+
"skia/include/**",
|
|
91
|
+
"skia/modules/skottie/**",
|
|
92
|
+
"skia/modules/skparagraph/**",
|
|
93
|
+
"skia/modules/sksg/**",
|
|
94
|
+
"skia/modules/skshaper/**",
|
|
95
|
+
"skia/modules/skunicode/**",
|
|
96
|
+
"skia/modules/svg/**",
|
|
97
|
+
"skia/src/**"
|
|
98
|
+
],
|
|
99
|
+
"dependencies": [
|
|
100
|
+
"React",
|
|
101
|
+
"ReactNativeDependencies",
|
|
102
|
+
"Hermes",
|
|
103
|
+
"libskia",
|
|
104
|
+
"libsvg",
|
|
105
|
+
"libskshaper",
|
|
106
|
+
"libskparagraph",
|
|
107
|
+
"libskunicode_core",
|
|
108
|
+
"libskunicode_libgrapheme",
|
|
109
|
+
"libskottie",
|
|
110
|
+
"libsksg"
|
|
111
|
+
],
|
|
112
|
+
"includeDirectories": [
|
|
113
|
+
".",
|
|
114
|
+
"api",
|
|
115
|
+
"skia",
|
|
116
|
+
"../.build/codegen/build/generated/ios/ReactCodegen"
|
|
117
|
+
],
|
|
118
|
+
"compilerFlags": {
|
|
119
|
+
"common": {
|
|
120
|
+
"c": [
|
|
121
|
+
"-DSK_METAL=1",
|
|
122
|
+
"-DSK_GANESH=1",
|
|
123
|
+
"-DSK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1",
|
|
124
|
+
"-DSK_DISABLE_LEGACY_SHAPER_FACTORY=1"
|
|
125
|
+
],
|
|
126
|
+
"cxx": [
|
|
127
|
+
"-fno-cxx-modules",
|
|
128
|
+
"-DSK_METAL=1",
|
|
129
|
+
"-DSK_GANESH=1",
|
|
130
|
+
"-DSK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1",
|
|
131
|
+
"-DSK_DISABLE_LEGACY_SHAPER_FACTORY=1"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"debug": ["-DHERMES_ENABLE_DEBUGGER=1"]
|
|
135
|
+
},
|
|
136
|
+
"publicHeaders": false
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"type": "objc",
|
|
140
|
+
"name": "RNSkia",
|
|
141
|
+
"path": "apple",
|
|
142
|
+
"pattern": "**/*.{m,mm}",
|
|
143
|
+
"headerPattern": "**/*.h",
|
|
144
|
+
"dependencies": [
|
|
145
|
+
"Hermes",
|
|
146
|
+
"React",
|
|
147
|
+
"ReactNativeDependencies",
|
|
148
|
+
"libskia",
|
|
149
|
+
"libsvg",
|
|
150
|
+
"libskshaper",
|
|
151
|
+
"libskparagraph",
|
|
152
|
+
"libskunicode_core",
|
|
153
|
+
"libskunicode_libgrapheme",
|
|
154
|
+
"libskottie",
|
|
155
|
+
"libsksg",
|
|
156
|
+
"rnskia_codegen_modules",
|
|
157
|
+
"rnskia_codegen_components",
|
|
158
|
+
"RNSkia_cpp"
|
|
159
|
+
],
|
|
160
|
+
"includeDirectories": [
|
|
161
|
+
".",
|
|
162
|
+
"../cpp",
|
|
163
|
+
"../cpp/rnskia",
|
|
164
|
+
"../cpp/jsi",
|
|
165
|
+
"../cpp/api",
|
|
166
|
+
"../cpp/utils",
|
|
167
|
+
"../cpp/skia",
|
|
168
|
+
"../.build/codegen/build/generated/ios/ReactCodegen"
|
|
169
|
+
],
|
|
170
|
+
"linkedFrameworks": [
|
|
171
|
+
"Foundation",
|
|
172
|
+
"UIKit",
|
|
173
|
+
"Metal",
|
|
174
|
+
"MetalKit",
|
|
175
|
+
"AVFoundation",
|
|
176
|
+
"AVKit",
|
|
177
|
+
"CoreMedia",
|
|
178
|
+
"QuartzCore",
|
|
179
|
+
"CoreGraphics",
|
|
180
|
+
"CoreText",
|
|
181
|
+
"CoreVideo",
|
|
182
|
+
"CoreImage",
|
|
183
|
+
"IOSurface"
|
|
184
|
+
],
|
|
185
|
+
"compilerFlags": [
|
|
186
|
+
"-include",
|
|
187
|
+
"Foundation/Foundation.h",
|
|
188
|
+
"-include",
|
|
189
|
+
"UIKit/UIKit.h",
|
|
190
|
+
"-DSK_METAL=1",
|
|
191
|
+
"-DSK_GANESH=1",
|
|
192
|
+
"-DSK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1",
|
|
193
|
+
"-DSK_DISABLE_LEGACY_SHAPER_FACTORY=1"
|
|
194
|
+
]
|
|
207
195
|
}
|
|
208
|
-
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
209
199
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-autolinking",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.11",
|
|
4
4
|
"description": "Scripts that autolink Expo modules.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"chalk": "^4.1.0",
|
|
50
50
|
"commander": "^7.2.0"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "125e8225bf36a4b9b2a159441d9ea724bcf1110f",
|
|
53
53
|
"scripts": {
|
|
54
54
|
"build": "expo-module build",
|
|
55
55
|
"clean": "expo-module clean",
|
|
@@ -54,6 +54,11 @@ module Expo
|
|
|
54
54
|
# Centralized build output directory under packages/precompile/
|
|
55
55
|
PRECOMPILE_BUILD_DIR = '.build'.freeze
|
|
56
56
|
|
|
57
|
+
# Subdir under packages/precompile/.build/ holding <Name>/<flavor>/<Name>.xcframework (monorepo source).
|
|
58
|
+
SHARED_SPM_DEPS_SOURCE_DIR = '.spm-deps'.freeze
|
|
59
|
+
# Subdir inside an npm package that bundles shared SPM xcframeworks for standalone consumers.
|
|
60
|
+
BUNDLED_SHARED_SPM_DEPS_SUBPATH = File.join('prebuilds', 'spm-deps').freeze
|
|
61
|
+
|
|
57
62
|
# Apple platforms supported by CocoaPods podspecs
|
|
58
63
|
APPLE_PLATFORMS = %w[ios osx tvos watchos visionos].freeze
|
|
59
64
|
|
|
@@ -188,10 +193,14 @@ module Expo
|
|
|
188
193
|
# - Pods that vendor xcframeworks (already precompiled)
|
|
189
194
|
# - Source-built pods that depend on React-Core (non-modular includes)
|
|
190
195
|
#
|
|
196
|
+
# Also stages shared SPM dep xcframework symlinks inside their owner pod's
|
|
197
|
+
# directory — must run before `generate_pods_project` reads each xcframework's
|
|
198
|
+
# Info.plist to slice it.
|
|
199
|
+
#
|
|
191
200
|
# @param installer [Pod::Installer] The CocoaPods installer instance
|
|
192
201
|
def perform_pre_install(installer)
|
|
193
202
|
return unless enabled?
|
|
194
|
-
|
|
203
|
+
ensure_shared_spm_deps(installer)
|
|
195
204
|
return if linkage(installer).nil?
|
|
196
205
|
|
|
197
206
|
pods_to_downgrade = Set.new(installer.podfile.framework_modules_to_patch)
|
|
@@ -215,6 +224,63 @@ module Expo
|
|
|
215
224
|
end
|
|
216
225
|
end
|
|
217
226
|
|
|
227
|
+
# Symlinks each shared SPM dependency xcframework (e.g. SDWebImage) into the
|
|
228
|
+
# pod directory of its owner. Ownership is whatever `build_vendored_paths` set
|
|
229
|
+
# in `@framework_owner_map` during `store_podspec` (resolution-first);
|
|
230
|
+
# falls back to alphabetical-first only if the map has no entry. Must run
|
|
231
|
+
# before `generate_pods_project` so CocoaPods sees the symlinks when reading
|
|
232
|
+
# each xcframework's Info.plist.
|
|
233
|
+
def ensure_shared_spm_deps(installer)
|
|
234
|
+
return unless enabled?
|
|
235
|
+
|
|
236
|
+
consumers_by_dep = collect_shared_spm_deps(installer)
|
|
237
|
+
return if consumers_by_dep.empty?
|
|
238
|
+
|
|
239
|
+
@framework_owner_map ||= {}
|
|
240
|
+
@claimed_vendored_frameworks ||= Set.new
|
|
241
|
+
|
|
242
|
+
unresolved = []
|
|
243
|
+
staged = 0
|
|
244
|
+
|
|
245
|
+
consumers_by_dep.each do |dep_name, consumers|
|
|
246
|
+
existing = @framework_owner_map[dep_name]
|
|
247
|
+
owner_name = (existing && consumers.key?(existing)) ? existing : consumers.keys.sort.first
|
|
248
|
+
owner_info = consumers[owner_name]
|
|
249
|
+
@framework_owner_map[dep_name] ||= owner_name
|
|
250
|
+
@claimed_vendored_frameworks.add(dep_name)
|
|
251
|
+
|
|
252
|
+
source_path = shared_spm_dep_xcframework_path(dep_name, owner_info, build_flavor)
|
|
253
|
+
owner_pod_dir = File.join(installer.sandbox.root, owner_name)
|
|
254
|
+
unless source_path && File.directory?(owner_pod_dir)
|
|
255
|
+
unresolved << dep_name
|
|
256
|
+
next
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
FileUtils.rm_rf(File.join(owner_pod_dir, "#{dep_name}.xcframework"))
|
|
260
|
+
File.symlink(source_path, File.join(owner_pod_dir, "#{dep_name}.xcframework"))
|
|
261
|
+
staged += 1
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
if unresolved.any?
|
|
265
|
+
Pod::UI.warn "[Expo-precompiled] Shared SPM xcframeworks not found for: #{unresolved.join(', ')} (flavor: #{build_flavor}). The Expo modules that depend on them will fail at runtime with dyld 'Library not loaded: @rpath/<Name>.framework/<Name>'. Run the precompile prebuild pipeline, or ensure each consuming npm package ships prebuilds/spm-deps/<Name>/<flavor>/<Name>.xcframework."
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
Pod::UI.puts "[Expo] ".blue + "Staged #{staged}/#{consumers_by_dep.size} shared SPM xcframework(s) (#{build_flavor})" if staged > 0
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# dep_name => { pod_name => pod_info } for shared SPM deps consumed by enabled prebuilt pods in this install.
|
|
272
|
+
def collect_shared_spm_deps(installer)
|
|
273
|
+
by_dep = {}
|
|
274
|
+
installer.pod_targets.each do |pod_target|
|
|
275
|
+
info = pod_lookup_map[pod_target.name]
|
|
276
|
+
next unless info && has_prebuilt_xcframework?(pod_target.name)
|
|
277
|
+
(info[:spm_dependency_frameworks] || []).each do |dep_name|
|
|
278
|
+
(by_dep[dep_name] ||= {})[pod_target.name] = info
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
by_dep
|
|
282
|
+
end
|
|
283
|
+
|
|
218
284
|
# ──────────────────────────────────────────────────────────────────────
|
|
219
285
|
# Cache management
|
|
220
286
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -1108,15 +1174,10 @@ module Expo
|
|
|
1108
1174
|
end
|
|
1109
1175
|
end
|
|
1110
1176
|
|
|
1111
|
-
#
|
|
1112
|
-
#
|
|
1113
|
-
#
|
|
1114
|
-
#
|
|
1115
|
-
#
|
|
1116
|
-
# @param product_name [String] The product/module name
|
|
1117
|
-
# @param pod_info [Hash] Package info from spm.config.json lookup
|
|
1118
|
-
# @param pod_name [String] The pod name (for summary tracking)
|
|
1119
|
-
# @return [Array<String>] vendored framework paths
|
|
1177
|
+
# Returns vendored_frameworks paths for a prebuilt pod: the product's own
|
|
1178
|
+
# xcframework plus any shared SPM deps this pod owns (first to claim wins;
|
|
1179
|
+
# non-owners get FRAMEWORK_SEARCH_PATHS instead). Shared dep entries are
|
|
1180
|
+
# symlinks staged by `ensure_shared_spm_deps` inside the owner's pod dir.
|
|
1120
1181
|
def build_vendored_paths(product_name, pod_info, pod_name)
|
|
1121
1182
|
@claimed_vendored_frameworks ||= Set.new
|
|
1122
1183
|
@framework_owner_map ||= {}
|
|
@@ -1126,38 +1187,27 @@ module Expo
|
|
|
1126
1187
|
@framework_owner_map[product_name] = pod_name
|
|
1127
1188
|
|
|
1128
1189
|
(pod_info[:spm_dependency_frameworks] || []).each do |dep_name|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
Pod::UI.puts "#{'[Expo-precompiled] '.blue}Skipping #{dep_name}.xcframework from #{pod_name} — already vendored by #{owner}"
|
|
1132
|
-
else
|
|
1190
|
+
owner = (@framework_owner_map[dep_name] ||= pod_name)
|
|
1191
|
+
if owner == pod_name
|
|
1133
1192
|
paths << "#{dep_name}.xcframework"
|
|
1134
|
-
|
|
1135
|
-
|
|
1193
|
+
else
|
|
1194
|
+
Pod::UI.puts "#{'[Expo-precompiled] '.blue}Skipping #{dep_name}.xcframework from #{pod_name} — already vendored by #{owner}"
|
|
1136
1195
|
end
|
|
1137
1196
|
log_spm_dependency(pod_name, dep_name)
|
|
1138
1197
|
end
|
|
1139
1198
|
paths
|
|
1140
1199
|
end
|
|
1141
1200
|
|
|
1142
|
-
#
|
|
1143
|
-
#
|
|
1144
|
-
#
|
|
1145
|
-
#
|
|
1146
|
-
# @param pod_name [String] The pod name
|
|
1147
|
-
# @param pod_info [Hash] Package info from spm.config.json lookup
|
|
1148
|
-
# @return [Array<String>] framework search path entries
|
|
1201
|
+
# FRAMEWORK_SEARCH_PATHS entries for shared SPM deps claimed by another pod.
|
|
1202
|
+
# CocoaPods slices each xcframework into `${PODS_XCFRAMEWORKS_BUILD_DIR}/<owner>/`,
|
|
1203
|
+
# which is where the linker resolves the framework.
|
|
1149
1204
|
def framework_search_paths_for_skipped_deps(pod_name, pod_info)
|
|
1150
|
-
@claimed_vendored_frameworks ||= Set.new
|
|
1151
1205
|
@framework_owner_map ||= {}
|
|
1152
|
-
|
|
1153
|
-
paths = []
|
|
1154
|
-
(pod_info[:spm_dependency_frameworks] || []).each do |dep_name|
|
|
1206
|
+
owners = (pod_info[:spm_dependency_frameworks] || []).filter_map do |dep_name|
|
|
1155
1207
|
owner = @framework_owner_map[dep_name]
|
|
1156
|
-
if owner && owner != pod_name
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
end
|
|
1160
|
-
paths.uniq
|
|
1208
|
+
owner if owner && owner != pod_name
|
|
1209
|
+
end.uniq
|
|
1210
|
+
owners.flat_map { |owner| [%("${PODS_XCFRAMEWORKS_BUILD_DIR}/#{owner}"), %("${PODS_ROOT}/#{owner}")] }
|
|
1161
1211
|
end
|
|
1162
1212
|
|
|
1163
1213
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -1184,11 +1234,13 @@ module Expo
|
|
|
1184
1234
|
package_root_var = "#{pods_parent}/#{package_root_rel}"
|
|
1185
1235
|
dsym_stamp = "$(DERIVED_FILE_DIR)/expo-dsym-resolve-#{product_name}-$(CONFIGURATION).stamp"
|
|
1186
1236
|
|
|
1237
|
+
shared_deps = shared_dep_switch_args(spec_name, pod_info)
|
|
1238
|
+
|
|
1187
1239
|
switch_phase = {
|
|
1188
1240
|
'name' => "[Expo] Switch #{spec_name} XCFramework for build configuration",
|
|
1189
1241
|
'execution_position' => 'before_compile',
|
|
1190
1242
|
'input_files' => ["#{pods_parent}/#{switch_script_rel}"],
|
|
1191
|
-
'script' => xcframework_switch_script(product_name, xcframeworks_dir_var, switch_script_path),
|
|
1243
|
+
'script' => xcframework_switch_script(product_name, xcframeworks_dir_var, switch_script_path, shared_deps),
|
|
1192
1244
|
}
|
|
1193
1245
|
|
|
1194
1246
|
if Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.13.0')
|
|
@@ -1231,32 +1283,55 @@ module Expo
|
|
|
1231
1283
|
SH
|
|
1232
1284
|
end
|
|
1233
1285
|
|
|
1234
|
-
#
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1286
|
+
# Shell script for the xcframework switch phase. With no shared deps the
|
|
1287
|
+
# script short-circuits in shell when the per-pod state file matches; with
|
|
1288
|
+
# shared deps Node is always invoked so each dep's symlink can be repointed
|
|
1289
|
+
# (it has its own per-dep state file inside replace-xcframework.js).
|
|
1290
|
+
def xcframework_switch_script(product_name, xcframeworks_dir, script_path, shared_deps = [])
|
|
1291
|
+
config_detect = <<~SH.chomp
|
|
1240
1292
|
CONFIG="release"
|
|
1241
1293
|
if echo "$GCC_PREPROCESSOR_DEFINITIONS" | grep -q "DEBUG=1"; then
|
|
1242
1294
|
CONFIG="debug"
|
|
1243
1295
|
fi
|
|
1244
|
-
|
|
1245
|
-
# Early exit: Skip Node.js invocation if configuration hasn't changed
|
|
1246
|
-
# This optimization avoids ~100-200ms overhead per module on incremental builds
|
|
1247
|
-
LAST_CONFIG_FILE="#{xcframeworks_dir}/artifacts/.last_build_configuration"
|
|
1248
|
-
if [ -f "$LAST_CONFIG_FILE" ] && [ "$(cat "$LAST_CONFIG_FILE")" = "$CONFIG" ]; then
|
|
1249
|
-
exit 0
|
|
1250
|
-
fi
|
|
1251
|
-
|
|
1252
|
-
# Configuration changed or first build - invoke Node.js to extract tarball
|
|
1253
|
-
. "$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
|
|
1254
|
-
|
|
1255
|
-
"$NODE_BINARY" "#{script_path}" \\
|
|
1256
|
-
-c "$CONFIG" \\
|
|
1257
|
-
-m "#{product_name}" \\
|
|
1258
|
-
-x "#{xcframeworks_dir}"
|
|
1259
1296
|
SH
|
|
1297
|
+
|
|
1298
|
+
if shared_deps.empty?
|
|
1299
|
+
<<~SH
|
|
1300
|
+
# Auto-generated by expo-modules-autolinking
|
|
1301
|
+
#{config_detect}
|
|
1302
|
+
LAST_CONFIG_FILE="#{xcframeworks_dir}/artifacts/.last_build_configuration"
|
|
1303
|
+
if [ -f "$LAST_CONFIG_FILE" ] && [ "$(cat "$LAST_CONFIG_FILE")" = "$CONFIG" ]; then
|
|
1304
|
+
exit 0
|
|
1305
|
+
fi
|
|
1306
|
+
. "$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
|
|
1307
|
+
"$NODE_BINARY" "#{script_path}" -c "$CONFIG" -m "#{product_name}" -x "#{xcframeworks_dir}"
|
|
1308
|
+
SH
|
|
1309
|
+
else
|
|
1310
|
+
shared_args = shared_deps.map { |arg| " #{arg}" }.join(" \\\n")
|
|
1311
|
+
<<~SH
|
|
1312
|
+
# Auto-generated by expo-modules-autolinking
|
|
1313
|
+
#{config_detect}
|
|
1314
|
+
. "$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"
|
|
1315
|
+
"$NODE_BINARY" "#{script_path}" \\
|
|
1316
|
+
-c "$CONFIG" \\
|
|
1317
|
+
-m "#{product_name}" \\
|
|
1318
|
+
-x "#{xcframeworks_dir}" \\
|
|
1319
|
+
#{shared_args}
|
|
1320
|
+
SH
|
|
1321
|
+
end
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1324
|
+
# '--shared "<Name>:<source_base>"' tokens for shared SPM deps this pod owns.
|
|
1325
|
+
# Non-owners reach the framework via FRAMEWORK_SEARCH_PATHS at link time.
|
|
1326
|
+
def shared_dep_switch_args(pod_name, pod_info)
|
|
1327
|
+
return [] unless pod_info && pod_info[:spm_dependency_frameworks]
|
|
1328
|
+
@framework_owner_map ||= {}
|
|
1329
|
+
pod_info[:spm_dependency_frameworks].filter_map do |dep_name|
|
|
1330
|
+
next nil unless @framework_owner_map[dep_name] == pod_name
|
|
1331
|
+
source_base = shared_spm_dep_source_base(dep_name, pod_info)
|
|
1332
|
+
next nil unless source_base
|
|
1333
|
+
%(--shared "#{dep_name}:#{source_base.gsub(/[\\"$`]/) { |c| "\\#{c}" }}")
|
|
1334
|
+
end
|
|
1260
1335
|
end
|
|
1261
1336
|
|
|
1262
1337
|
# Returns the shell script for the dSYM source map resolution phase.
|
|
@@ -1853,6 +1928,43 @@ module Expo
|
|
|
1853
1928
|
own_resolution
|
|
1854
1929
|
end
|
|
1855
1930
|
|
|
1931
|
+
# Candidate parent dirs (each holds <flavor>/<Name>.xcframework subtrees) for
|
|
1932
|
+
# a shared SPM dep. Ordered: EXPO_PRECOMPILED_MODULES_PATH override, monorepo
|
|
1933
|
+
# .spm-deps, then the consumer-side npm-bundled location.
|
|
1934
|
+
def shared_spm_dep_source_base_candidates(dep_name, pod_info)
|
|
1935
|
+
candidates = []
|
|
1936
|
+
candidates << File.join(custom_modules_path, SHARED_SPM_DEPS_SOURCE_DIR, dep_name) if custom_modules_path
|
|
1937
|
+
candidates << File.join(memoized_repo_root, 'packages', 'precompile', PRECOMPILE_BUILD_DIR, SHARED_SPM_DEPS_SOURCE_DIR, dep_name) if memoized_repo_root
|
|
1938
|
+
candidates << File.join(pod_info[:package_root], BUNDLED_SHARED_SPM_DEPS_SUBPATH, dep_name) if pod_info && pod_info[:package_root]
|
|
1939
|
+
candidates
|
|
1940
|
+
end
|
|
1941
|
+
|
|
1942
|
+
# First candidate base that has at least one flavor on disk (used to build switch-script source_base args).
|
|
1943
|
+
def shared_spm_dep_source_base(dep_name, pod_info)
|
|
1944
|
+
shared_spm_dep_source_base_candidates(dep_name, pod_info).find do |base|
|
|
1945
|
+
%w[debug release].any? { |f| File.directory?(File.join(base, f, "#{dep_name}.xcframework")) }
|
|
1946
|
+
end
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
# First candidate that has the requested flavor on disk (walks all candidates so a partial monorepo doesn't shadow a complete npm bundle).
|
|
1950
|
+
def shared_spm_dep_xcframework_path(dep_name, pod_info, flavor)
|
|
1951
|
+
shared_spm_dep_source_base_candidates(dep_name, pod_info).each do |base|
|
|
1952
|
+
path = File.join(base, flavor, "#{dep_name}.xcframework")
|
|
1953
|
+
return path if File.directory?(path)
|
|
1954
|
+
end
|
|
1955
|
+
nil
|
|
1956
|
+
end
|
|
1957
|
+
|
|
1958
|
+
def memoized_repo_root
|
|
1959
|
+
return @repo_root if @memoized_repo_root_set
|
|
1960
|
+
begin
|
|
1961
|
+
@repo_root = find_repo_root
|
|
1962
|
+
ensure
|
|
1963
|
+
@memoized_repo_root_set = true
|
|
1964
|
+
end
|
|
1965
|
+
@repo_root
|
|
1966
|
+
end
|
|
1967
|
+
|
|
1856
1968
|
def resolve_prebuilt_tarball(pod_info, product_name, flavor, pod_name = nil)
|
|
1857
1969
|
tarball = File.join(pod_info[:build_output_dir], flavor, 'xcframeworks', "#{product_name}.tar.gz")
|
|
1858
1970
|
return tarball if File.exist?(tarball)
|
|
@@ -2,52 +2,32 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Replace XCFramework for Debug/Release Configuration
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Per-pod product swap: extracts <xcframeworksDir>/artifacts/<module>-<config>.tar.gz
|
|
6
|
+
* over <Product>.xcframework, gated on <xcframeworksDir>/artifacts/.last_build_configuration.
|
|
7
|
+
* Sibling shared-dep symlinks under <xcframeworksDir>/ are preserved (only the product
|
|
8
|
+
* xcframework is wiped before re-extracting).
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* <Product>-release.tar.gz (tarball, source of truth)
|
|
14
|
-
* .last_build_configuration
|
|
15
|
-
* <Product>.xcframework/ (real dir, extracted from tarball)
|
|
16
|
-
* <Dependency>.xcframework/ (real dir, if any, extracted from same tarball)
|
|
10
|
+
* Shared-dep repoint (each --shared entry): atomically replaces
|
|
11
|
+
* <xcframeworksDir>/<Name>.xcframework with a symlink to <source_base>/<config>/<Name>.xcframework
|
|
12
|
+
* and writes <xcframeworksDir>/artifacts/<Name>.last_config. The owner pod (decided at pod
|
|
13
|
+
* install time by ensure_shared_spm_deps) receives --shared args for each dep it owns.
|
|
17
14
|
*
|
|
18
15
|
* Usage:
|
|
19
|
-
* node replace-xcframework.js -c <CONFIG> -m <
|
|
20
|
-
*
|
|
21
|
-
* Arguments:
|
|
22
|
-
* -c, --config Build configuration: "debug" or "release"
|
|
23
|
-
* -m, --module Module/product name (used for tarball lookup and logging)
|
|
24
|
-
* -x, --xcframeworks Path to the pod directory (Pods/<PodName>/)
|
|
25
|
-
*
|
|
26
|
-
* The script:
|
|
27
|
-
* 1. Finds the tarball: <xcframeworksDir>/artifacts/<module>-<config>.tar.gz
|
|
28
|
-
* 2. Checks artifacts/.last_build_configuration — skips if unchanged
|
|
29
|
-
* 3. Removes all *.xcframework directories in xcframeworksDir
|
|
30
|
-
* 4. Extracts the tarball: tar -xzf ... -C <xcframeworksDir>
|
|
31
|
-
* 5. Writes the new config to artifacts/.last_build_configuration
|
|
16
|
+
* node replace-xcframework.js -c <CONFIG> -m <MODULE> -x <XCFRAMEWORKS_DIR>
|
|
17
|
+
* [--shared <Name>:<source_base>]...
|
|
32
18
|
*
|
|
33
19
|
* Based on React Native's replace-rncore-version.js pattern.
|
|
34
20
|
*/
|
|
35
21
|
|
|
22
|
+
const { spawnSync } = require('child_process');
|
|
36
23
|
const fs = require('fs');
|
|
37
24
|
const path = require('path');
|
|
38
|
-
const { spawnSync } = require('child_process');
|
|
39
25
|
|
|
40
26
|
const LOG_PREFIX = '[Expo XCFramework]';
|
|
41
27
|
|
|
42
|
-
// Parse command line arguments
|
|
43
28
|
function parseArgs() {
|
|
44
29
|
const args = process.argv.slice(2);
|
|
45
|
-
const result = {
|
|
46
|
-
config: null,
|
|
47
|
-
module: null,
|
|
48
|
-
xcframeworksDir: null,
|
|
49
|
-
};
|
|
50
|
-
|
|
30
|
+
const result = { config: null, module: null, xcframeworksDir: null, sharedDeps: [] };
|
|
51
31
|
for (let i = 0; i < args.length; i++) {
|
|
52
32
|
switch (args[i]) {
|
|
53
33
|
case '-c':
|
|
@@ -62,116 +42,120 @@ function parseArgs() {
|
|
|
62
42
|
case '--xcframeworks':
|
|
63
43
|
result.xcframeworksDir = args[++i];
|
|
64
44
|
break;
|
|
45
|
+
case '-s':
|
|
46
|
+
case '--shared': {
|
|
47
|
+
const spec = args[++i] || '';
|
|
48
|
+
const colon = spec.indexOf(':');
|
|
49
|
+
if (colon === -1) {
|
|
50
|
+
console.error(`${LOG_PREFIX} Invalid --shared (expected "<Name>:<source_base>"): ${spec}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
result.sharedDeps.push({ name: spec.slice(0, colon), sourceBase: spec.slice(colon + 1) });
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
|
-
|
|
68
58
|
return result;
|
|
69
59
|
}
|
|
70
60
|
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.error(
|
|
77
|
-
'Usage: replace-xcframework.js -c <CONFIG> -m <MODULE_NAME> -x <XCFRAMEWORKS_DIR>'
|
|
78
|
-
);
|
|
79
|
-
console.error(' -c, --config Build configuration: "debug" or "release"');
|
|
80
|
-
console.error(' -m, --module Module/product name');
|
|
81
|
-
console.error(' -x, --xcframeworks Path to the xcframeworks directory');
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Normalize config to lowercase
|
|
86
|
-
const configLower = args.config.toLowerCase();
|
|
87
|
-
if (configLower !== 'debug' && configLower !== 'release') {
|
|
88
|
-
console.error(
|
|
89
|
-
`${LOG_PREFIX} Invalid configuration: ${args.config}. Must be "debug" or "release".`
|
|
90
|
-
);
|
|
91
|
-
process.exit(1);
|
|
61
|
+
function readState(file) {
|
|
62
|
+
try {
|
|
63
|
+
return fs.existsSync(file) ? fs.readFileSync(file, 'utf8').trim() : null;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
92
66
|
}
|
|
67
|
+
}
|
|
93
68
|
|
|
94
|
-
|
|
95
|
-
const moduleName = args
|
|
96
|
-
|
|
97
|
-
// Validate xcframeworksDir exists
|
|
69
|
+
function processPerPodSwap(args, configLower) {
|
|
70
|
+
const { xcframeworksDir, module: moduleName } = args;
|
|
98
71
|
if (!fs.existsSync(xcframeworksDir) || !fs.statSync(xcframeworksDir).isDirectory()) {
|
|
99
72
|
console.error(`${LOG_PREFIX} ${moduleName}: Directory not found: ${xcframeworksDir}`);
|
|
100
73
|
process.exit(1);
|
|
101
74
|
}
|
|
102
75
|
|
|
103
|
-
// Ensure artifacts directory exists
|
|
104
76
|
const artifactsDir = path.join(xcframeworksDir, 'artifacts');
|
|
105
77
|
fs.mkdirSync(artifactsDir, { recursive: true });
|
|
106
|
-
|
|
107
|
-
// Find the tarball for the requested configuration (stored in artifacts/)
|
|
108
78
|
const tarballPath = path.join(artifactsDir, `${moduleName}-${configLower}.tar.gz`);
|
|
109
79
|
const lastConfigFile = path.join(artifactsDir, '.last_build_configuration');
|
|
110
80
|
|
|
111
|
-
// Check if tarball exists
|
|
112
81
|
if (!fs.existsSync(tarballPath)) {
|
|
113
|
-
console.error(
|
|
114
|
-
`${LOG_PREFIX} ${moduleName}: Tarball not found at ${tarballPath}, skipping.`
|
|
115
|
-
);
|
|
82
|
+
console.error(`${LOG_PREFIX} ${moduleName}: Tarball not found at ${tarballPath}, skipping.`);
|
|
116
83
|
return;
|
|
117
84
|
}
|
|
118
85
|
|
|
119
|
-
|
|
120
|
-
let lastConfig = null;
|
|
121
|
-
if (fs.existsSync(lastConfigFile)) {
|
|
122
|
-
try {
|
|
123
|
-
lastConfig = fs.readFileSync(lastConfigFile, 'utf8').trim();
|
|
124
|
-
} catch (e) {
|
|
125
|
-
// Ignore read errors — will proceed with extraction
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Check if configuration has changed
|
|
86
|
+
const lastConfig = readState(lastConfigFile);
|
|
130
87
|
if (lastConfig === configLower) {
|
|
131
88
|
console.log(`${LOG_PREFIX} ${moduleName}: Already extracted ${configLower}, skipping.`);
|
|
132
89
|
return;
|
|
133
90
|
}
|
|
134
91
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
|
144
|
-
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
145
|
-
}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
console.error(`${LOG_PREFIX} ${moduleName}: Warning: failed to remove ${entry}: ${e.message}`);
|
|
92
|
+
// Only remove the product xcframework — shared-dep symlinks staged by
|
|
93
|
+
// ensure_shared_spm_deps are repointed separately below via --shared.
|
|
94
|
+
const productXcfw = path.join(xcframeworksDir, `${moduleName}.xcframework`);
|
|
95
|
+
try {
|
|
96
|
+
fs.rmSync(productXcfw, { recursive: true, force: true });
|
|
97
|
+
} catch (e) {
|
|
98
|
+
if (e.code !== 'ENOENT') {
|
|
99
|
+
console.error(`${LOG_PREFIX} ${moduleName}: failed to remove product xcframework: ${e.message}`);
|
|
148
100
|
}
|
|
149
101
|
}
|
|
150
102
|
|
|
151
|
-
|
|
152
|
-
const result = spawnSync('tar', ['-xzf', tarballPath, '-C', xcframeworksDir], {
|
|
153
|
-
stdio: 'pipe',
|
|
154
|
-
});
|
|
155
|
-
|
|
103
|
+
const result = spawnSync('tar', ['-xzf', tarballPath, '-C', xcframeworksDir], { stdio: 'pipe' });
|
|
156
104
|
if (result.status !== 0) {
|
|
157
|
-
|
|
158
|
-
console.error(`${LOG_PREFIX} ${moduleName}: Failed to extract tarball: ${stderr}`);
|
|
105
|
+
console.error(`${LOG_PREFIX} ${moduleName}: tar failed: ${result.stderr?.toString().trim()}`);
|
|
159
106
|
process.exit(1);
|
|
160
107
|
}
|
|
161
108
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
109
|
+
fs.writeFileSync(lastConfigFile, configLower);
|
|
110
|
+
console.log(
|
|
111
|
+
lastConfig
|
|
112
|
+
? `${LOG_PREFIX} ${moduleName}: Switched from ${lastConfig} to ${configLower}.`
|
|
113
|
+
: `${LOG_PREFIX} ${moduleName}: Extracted ${configLower} tarball.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function repointSharedDep(xcframeworksDir, name, sourceBase, configLower) {
|
|
118
|
+
const artifactsDir = path.join(xcframeworksDir, 'artifacts');
|
|
119
|
+
fs.mkdirSync(artifactsDir, { recursive: true });
|
|
120
|
+
const stateFile = path.join(artifactsDir, `${name}.last_config`);
|
|
121
|
+
const linkPath = path.join(xcframeworksDir, `${name}.xcframework`);
|
|
122
|
+
|
|
123
|
+
// Trust the state file only when the symlink is still present — an externally
|
|
124
|
+
// deleted symlink (e.g. clear_cocoapods_cache wiping the pod dir) must trigger
|
|
125
|
+
// a re-link even if the state file claims the right config.
|
|
126
|
+
if (readState(stateFile) === configLower && fs.existsSync(linkPath)) return;
|
|
127
|
+
|
|
128
|
+
const target = path.join(sourceBase, configLower, `${name}.xcframework`);
|
|
129
|
+
if (!fs.existsSync(target)) {
|
|
130
|
+
console.error(
|
|
131
|
+
`${LOG_PREFIX} Shared dep ${name}: target not found at ${target}. Run the precompile prebuild pipeline for the ${configLower} flavor, or ensure prebuilds/spm-deps/${name}/${configLower}/${name}.xcframework ships with the consuming package.`
|
|
132
|
+
);
|
|
133
|
+
process.exit(1);
|
|
167
134
|
}
|
|
168
135
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
136
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
137
|
+
fs.symlinkSync(target, linkPath);
|
|
138
|
+
fs.writeFileSync(stateFile, configLower);
|
|
139
|
+
console.log(`${LOG_PREFIX} Shared dep ${name}: repointed to ${configLower} (${target}).`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function main() {
|
|
143
|
+
const args = parseArgs();
|
|
144
|
+
if (!args.config || !args.module || !args.xcframeworksDir) {
|
|
145
|
+
console.error(
|
|
146
|
+
'Usage: replace-xcframework.js -c <CONFIG> -m <MODULE> -x <XCFRAMEWORKS_DIR> [--shared <Name>:<source_base>]...'
|
|
172
147
|
);
|
|
173
|
-
|
|
174
|
-
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
const configLower = args.config.toLowerCase();
|
|
151
|
+
if (configLower !== 'debug' && configLower !== 'release') {
|
|
152
|
+
console.error(`${LOG_PREFIX} Invalid configuration: ${args.config}. Must be "debug" or "release".`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
processPerPodSwap(args, configLower);
|
|
157
|
+
for (const dep of args.sharedDeps) {
|
|
158
|
+
repointSharedDep(args.xcframeworksDir, dep.name, dep.sourceBase, configLower);
|
|
175
159
|
}
|
|
176
160
|
}
|
|
177
161
|
|