react-native 0.87.0-rc.0 → 0.87.0-rc.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/Libraries/Core/InitializeCore.js +9 -1
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/ReactNative/AppRegistry.flow.js +1 -0
- package/Libraries/ReactNative/AppRegistryImpl.js +2 -3
- package/Libraries/ReactPrivate/ReactNativePrivateInterface.js +5 -116
- package/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow +5 -31
- package/React/Base/RCTVersion.m +1 -1
- package/React/I18n/RCTLocalizedString.mm +38 -2
- package/React-Core-prebuilt.podspec +45 -17
- package/React-Core.podspec +9 -2
- package/ReactAndroid/external-artifacts/build.gradle.kts +49 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SynchronousMountItem.kt +8 -2
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/index.js +15 -3
- package/index.js.flow +0 -2
- package/package.json +27 -29
- package/react-native.config.js +81 -0
- package/scripts/cocoapods/fabric.rb +1 -1
- package/scripts/cocoapods/rncore.rb +35 -78
- package/scripts/cocoapods/rncore_facades.rb +232 -0
- package/scripts/cocoapods/rndependencies.rb +64 -3
- package/scripts/cocoapods/rndeps_facades.rb +193 -0
- package/scripts/cocoapods/spm.rb +78 -11
- package/scripts/codegen/templates/Package.swift.spm-template +97 -0
- package/scripts/react-native-xcode.sh +20 -0
- package/scripts/react_native_pods.rb +65 -16
- package/scripts/replace-rncore-version.js +53 -6
- package/scripts/setup-apple-spm.js +1165 -0
- package/scripts/spm/__doc__/rfc-spm-xcframework.md +707 -0
- package/scripts/spm/__doc__/spm-autolinking-plugins.md +244 -0
- package/scripts/spm/__doc__/spm-header-paths-contract.md +97 -0
- package/scripts/spm/__doc__/spm-plugins-assessment.md +128 -0
- package/scripts/spm/__doc__/spm-scripts.md +451 -0
- package/scripts/spm/autolinking-plugins.js +331 -0
- package/scripts/spm/download-spm-artifacts.js +1409 -0
- package/scripts/spm/expand-spm-dependencies.js +216 -0
- package/scripts/spm/flavored-frameworks.js +1008 -0
- package/scripts/spm/generate-spm-autolinking-config.js +161 -0
- package/scripts/spm/generate-spm-autolinking.js +1888 -0
- package/scripts/spm/generate-spm-package.js +302 -0
- package/scripts/spm/generate-spm-xcodeproj.js +2224 -0
- package/scripts/spm/read-podspec.js +695 -0
- package/scripts/spm/scaffold-package-swift.js +1206 -0
- package/scripts/spm/spm-pbxproj.js +654 -0
- package/scripts/spm/spm-types.js +517 -0
- package/scripts/spm/spm-utils.js +645 -0
- package/scripts/spm/sync-spm-autolinking.js +160 -0
- package/sdks/.hermesv1version +1 -0
- package/sdks/hermes-engine/utils/replace_hermes_version.js +18 -4
- package/sdks/hermes-engine/version.properties +1 -1
- package/src/asset-registry.js +1 -1
- package/src/react-private-interface.js +145 -0
- package/src/react-private-interface.js.flow +48 -0
- package/src/setup-env.js +22 -0
- package/src/unstable-internals-do-not-use.d.ts +214 -0
- package/src/unstable-internals-do-not-use.js +76 -0
- package/third-party-podspecs/ReactNativeDependencies.podspec +2 -2
- package/types_generated/Libraries/ReactNative/AppRegistry.flow.d.ts +2 -2
- package/types_generated/Libraries/ReactPrivate/ReactNativePrivateInterface.d.ts +6 -21
- package/types_generated/index.d.ts +1 -2
- package/types_generated/src/private/renderer/events/dispatchNativeEvent.d.ts +26 -0
- package/types_generated/src/react-private-interface.d.ts +33 -0
- package/Libraries/Utilities/SceneTracker.js +0 -42
- package/jest-preset.js +0 -26
- package/rn-get-polyfills.js +0 -13
- package/types/tsconfig.json +0 -17
- package/types_generated/Libraries/Components/Touchable/Touchable.d.ts +0 -261
- package/types_generated/tsconfig.test.json +0 -17
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
require "json"
|
|
7
7
|
require 'net/http'
|
|
8
8
|
require 'rexml/document'
|
|
9
|
+
require 'shellwords'
|
|
9
10
|
|
|
10
11
|
require_relative './utils.rb'
|
|
11
12
|
|
|
@@ -36,7 +37,7 @@ def add_rn_third_party_dependencies(s)
|
|
|
36
37
|
header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
|
|
37
38
|
|
|
38
39
|
if header_search_paths.is_a?(String)
|
|
39
|
-
header_search_paths =
|
|
40
|
+
header_search_paths = Shellwords.shellsplit(header_search_paths)
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
header_search_paths << "$(PODS_ROOT)/glog"
|
|
@@ -47,10 +48,24 @@ def add_rn_third_party_dependencies(s)
|
|
|
47
48
|
header_search_paths << "$(PODS_ROOT)/SocketRocket"
|
|
48
49
|
header_search_paths << "$(PODS_ROOT)/RCT-Folly"
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
# uniq so a second call on the same spec can't duplicate entries.
|
|
52
|
+
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
|
|
51
53
|
else
|
|
54
|
+
# Prebuilt-deps mode: this pod SELF-SERVES the third-party headers from its
|
|
55
|
+
# own xcframework (incl. SocketRocket - sole supplier in this mode). See
|
|
56
|
+
# scripts/cocoapods/__docs__/prebuilt-deps.md for the full contract.
|
|
52
57
|
s.dependency "ReactNativeDependencies"
|
|
53
|
-
|
|
58
|
+
|
|
59
|
+
header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
|
|
60
|
+
if header_search_paths.is_a?(String)
|
|
61
|
+
header_search_paths = Shellwords.shellsplit(header_search_paths)
|
|
62
|
+
end
|
|
63
|
+
# Artifact headers are flattened into the pod-local Headers/ by the podspec
|
|
64
|
+
# prepare_command (see __docs__/prebuilt-deps.md).
|
|
65
|
+
header_search_paths << "$(PODS_ROOT)/ReactNativeDependencies/Headers"
|
|
66
|
+
|
|
67
|
+
# uniq so a second call on the same spec can't duplicate entries.
|
|
68
|
+
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
|
|
54
69
|
end
|
|
55
70
|
|
|
56
71
|
s.pod_target_xcconfig = current_pod_target_xcconfig
|
|
@@ -139,6 +154,52 @@ class ReactNativeDependenciesUtils
|
|
|
139
154
|
end
|
|
140
155
|
end
|
|
141
156
|
|
|
157
|
+
# Single post-install injection site for the prebuilt deps header resolution.
|
|
158
|
+
# Adds the flattened ReactNativeDependencies/Headers search path to the
|
|
159
|
+
# aggregate (main app) target AND every pod target, mirroring
|
|
160
|
+
# ReactNativeCoreUtils.configure_aggregate_xcconfig. ReactNativeHeaders is
|
|
161
|
+
# pure-RN, so this path is the single global home of the third-party
|
|
162
|
+
# namespaces (folly/glog/boost/fmt/double-conversion/fast_float/
|
|
163
|
+
# SocketRocket): pods that never call add_rn_third_party_dependencies (nor
|
|
164
|
+
# depend on a facade) still compile RN headers that textually reach
|
|
165
|
+
# <folly/...>. No module-map activation needed — the deps headers are
|
|
166
|
+
# served textually; modules come from the ReactNativeDependencies pod.
|
|
167
|
+
def self.configure_aggregate_xcconfig(installer)
|
|
168
|
+
return if @@build_from_source
|
|
169
|
+
|
|
170
|
+
rndeps_log("Configuring xcconfig for prebuilt React Native Dependencies...")
|
|
171
|
+
headers_search_path = " \"$(PODS_ROOT)/ReactNativeDependencies/Headers\""
|
|
172
|
+
|
|
173
|
+
# Add the header search path to aggregate target xcconfigs (used by the main app target)
|
|
174
|
+
installer.aggregate_targets.each do |aggregate_target|
|
|
175
|
+
aggregate_target.xcconfigs.each do |config_name, config_file|
|
|
176
|
+
ReactNativePodsUtils.add_flag_to_map_with_inheritance(config_file.attributes, "HEADER_SEARCH_PATHS", headers_search_path)
|
|
177
|
+
xcconfig_path = aggregate_target.xcconfig_path(config_name)
|
|
178
|
+
config_file.save_as(xcconfig_path)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Add the header search path to ALL pod targets (for pods that don't go
|
|
183
|
+
# through add_rn_third_party_dependencies)
|
|
184
|
+
installer.pod_targets.each do |pod_target|
|
|
185
|
+
pod_target.build_settings.each do |config_name, build_settings|
|
|
186
|
+
xcconfig_path = pod_target.xcconfig_path(config_name)
|
|
187
|
+
next unless File.exist?(xcconfig_path)
|
|
188
|
+
|
|
189
|
+
xcconfig = Xcodeproj::Config.new(xcconfig_path)
|
|
190
|
+
|
|
191
|
+
# Skip if the deps header search path is already present
|
|
192
|
+
header_search_paths = xcconfig.attributes["HEADER_SEARCH_PATHS"] || ""
|
|
193
|
+
next if header_search_paths.include?("ReactNativeDependencies/Headers")
|
|
194
|
+
|
|
195
|
+
ReactNativePodsUtils.add_flag_to_map_with_inheritance(xcconfig.attributes, "HEADER_SEARCH_PATHS", headers_search_path)
|
|
196
|
+
xcconfig.save_as(xcconfig_path)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
rndeps_log("Prebuilt deps xcconfig configuration complete")
|
|
201
|
+
end
|
|
202
|
+
|
|
142
203
|
def self.podspec_source_download_prebuild_release_tarball()
|
|
143
204
|
# Warn if @@react_native_path is not set
|
|
144
205
|
if @@react_native_path == ""
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
#
|
|
3
|
+
# This source code is licensed under the MIT license found in the
|
|
4
|
+
# LICENSE file in the root directory of this source tree.
|
|
5
|
+
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
# Self-contained against require ordering: this module reads
|
|
9
|
+
# Helpers::Constants.socket_rocket_config. react_native_pods.rb normally loads
|
|
10
|
+
# helpers.rb first, but requiring it here (idempotent) removes that implicit
|
|
11
|
+
# dependency. The defined? guard at the use site stays as a backstop.
|
|
12
|
+
require_relative './helpers'
|
|
13
|
+
|
|
14
|
+
# Dependency-only facade podspecs for the third-party deps in prebuilt-deps
|
|
15
|
+
# mode (deps-side analogue of RNCoreFacades). Design + rationale:
|
|
16
|
+
# scripts/cocoapods/__docs__/prebuilt-deps.md
|
|
17
|
+
module RNDepsFacades
|
|
18
|
+
# The name of the umbrella prebuilt-deps pod every facade depends on. Its pod
|
|
19
|
+
# self-serves the third-party headers + carries the binary (see
|
|
20
|
+
# rndependencies.rb, ReactNativeDependencies.podspec).
|
|
21
|
+
DEPS_POD = "ReactNativeDependencies"
|
|
22
|
+
|
|
23
|
+
# pod name => podspec path (relative to the react-native package root), or
|
|
24
|
+
# :synthesized for a pod with no local podspec (SocketRocket). Version +
|
|
25
|
+
# subspecs + default_subspecs are DERIVED from the real podspec where one
|
|
26
|
+
# exists; synthesized entries derive their version from a constant.
|
|
27
|
+
FACADE_PODS = {
|
|
28
|
+
"RCT-Folly" => "third-party-podspecs/RCT-Folly.podspec",
|
|
29
|
+
"glog" => "third-party-podspecs/glog.podspec",
|
|
30
|
+
"boost" => "third-party-podspecs/boost.podspec",
|
|
31
|
+
"DoubleConversion" => "third-party-podspecs/DoubleConversion.podspec",
|
|
32
|
+
"fmt" => "third-party-podspecs/fmt.podspec",
|
|
33
|
+
"fast_float" => "third-party-podspecs/fast_float.podspec",
|
|
34
|
+
"SocketRocket" => :synthesized,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Sub-directory (relative to the install root) that holds the generated
|
|
38
|
+
# deps facades. Kept separate from RNCoreFacades' `build/rncore-facades` so
|
|
39
|
+
# the two families never collide.
|
|
40
|
+
FACADE_RELDIR = File.join("build", "rndeps-facades")
|
|
41
|
+
|
|
42
|
+
# Generates the facade podspecs and returns the base directory holding them.
|
|
43
|
+
# Each facade gets its OWN sub-directory containing a single
|
|
44
|
+
# `<Name>.podspec.json`, so it can be installed as a LOCAL pod via
|
|
45
|
+
# `:path => <dir>` (PathSource uses the spec in place and never downloads
|
|
46
|
+
# `spec.source`). Idempotent; safe to call once per `pod install`.
|
|
47
|
+
#
|
|
48
|
+
# `react_native_path` locates the real third-party podspecs we mirror.
|
|
49
|
+
# version + subspecs + default_subspecs are DERIVED from the real spec (or,
|
|
50
|
+
# for SocketRocket, synthesized from the socket_rocket_config version) so the
|
|
51
|
+
# facade matches the source pod's spec/subspec SHAPE. It is not fully
|
|
52
|
+
# graph-equivalent: every derived subspec depends only on
|
|
53
|
+
# ReactNativeDependencies, so intra-pod subspec deps (e.g. RCT-Folly/Fabric
|
|
54
|
+
# -> RCT-Folly/Default) are not reproduced — harmless here because the deps
|
|
55
|
+
# are all declared explicitly in react_native_pods.rb. NO source_files and
|
|
56
|
+
# NO headers are emitted — the ReactNativeDependencies pod supplies both. A
|
|
57
|
+
# facaded pod whose real podspec can't be read is a hard error (see
|
|
58
|
+
# load_real_spec) — silently shipping an empty facade would hide drift.
|
|
59
|
+
def self.generate(react_native_path, install_root, ios_version)
|
|
60
|
+
abs_base = File.join(install_root.to_s, FACADE_RELDIR)
|
|
61
|
+
FileUtils.mkdir_p(abs_base)
|
|
62
|
+
FACADE_PODS.each do |name, podspec_rel_path|
|
|
63
|
+
dir = File.join(abs_base, name)
|
|
64
|
+
FileUtils.mkdir_p(dir)
|
|
65
|
+
|
|
66
|
+
if podspec_rel_path == :synthesized
|
|
67
|
+
spec = synthesized_spec(name, ios_version)
|
|
68
|
+
else
|
|
69
|
+
podspec_path = File.join(react_native_path.to_s, podspec_rel_path)
|
|
70
|
+
real = load_real_spec(podspec_path, name)
|
|
71
|
+
spec = derived_spec(name, real, ios_version)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
File.write(File.join(dir, "#{name}.podspec.json"), JSON.pretty_generate(spec))
|
|
75
|
+
end
|
|
76
|
+
abs_base
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Facade dir for `<name>`, RELATIVE to the install root — pass to `pod :path =>`.
|
|
80
|
+
# Relative (not absolute) so the path CocoaPods records in Podfile.lock is
|
|
81
|
+
# portable rather than machine-specific.
|
|
82
|
+
def self.facade_path(name)
|
|
83
|
+
File.join(FACADE_RELDIR, name)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Base spec skeleton shared by derived + synthesized facades: dependency-only,
|
|
87
|
+
# no source_files, no headers. Depends solely on ReactNativeDependencies.
|
|
88
|
+
def self.base_spec(name, version, ios_version)
|
|
89
|
+
{
|
|
90
|
+
"name" => name,
|
|
91
|
+
"version" => version,
|
|
92
|
+
"summary" => "Prebuilt facade for #{name} (code + headers live in #{DEPS_POD}).",
|
|
93
|
+
"homepage" => "https://reactnative.dev/",
|
|
94
|
+
"license" => "MIT",
|
|
95
|
+
"authors" => "Meta Platforms, Inc. and its affiliates",
|
|
96
|
+
"platforms" => { "ios" => ios_version },
|
|
97
|
+
# Required podspec attribute, but never fetched: installed as a LOCAL
|
|
98
|
+
# pod (`:path => <dir>`), which uses this spec in place and ships no
|
|
99
|
+
# source_files. Placeholder only.
|
|
100
|
+
"source" => { "git" => "https://github.com/facebook/react-native.git" },
|
|
101
|
+
"dependencies" => { DEPS_POD => [] },
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
private_class_method :base_spec
|
|
105
|
+
|
|
106
|
+
# Facade derived from a real third-party podspec: version + subspecs +
|
|
107
|
+
# default_subspecs mirror the real spec so a bare `pod '<Name>'` and any
|
|
108
|
+
# `pod '<Name>/<Subspec>'` resolve to the SAME graph (e.g. RCT-Folly's
|
|
109
|
+
# bare + /Default + /Fabric). Each subspec is also dependency-only and
|
|
110
|
+
# depends on ReactNativeDependencies.
|
|
111
|
+
#
|
|
112
|
+
# NOTE: resources (e.g. RCT-Folly's PrivacyInfo.xcprivacy) are intentionally
|
|
113
|
+
# NOT carried. In prebuilt-deps mode the third-party code — and its privacy
|
|
114
|
+
# manifest — is embedded in the ReactNativeDependencies artifact; the facade
|
|
115
|
+
# only needs to declare the dependency (see the design note in the PR).
|
|
116
|
+
def self.derived_spec(name, real, ios_version)
|
|
117
|
+
spec = base_spec(name, real.version.to_s, ios_version)
|
|
118
|
+
|
|
119
|
+
defaults = Array(real.default_subspecs)
|
|
120
|
+
spec["default_subspecs"] = defaults unless defaults.empty?
|
|
121
|
+
|
|
122
|
+
subspecs = derive_subspecs(real)
|
|
123
|
+
unless subspecs.empty?
|
|
124
|
+
spec["subspecs"] = subspecs.map do |ss|
|
|
125
|
+
{ "name" => ss, "dependencies" => { DEPS_POD => [] } }
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
spec
|
|
130
|
+
end
|
|
131
|
+
private_class_method :derived_spec
|
|
132
|
+
|
|
133
|
+
# Facade synthesized for a pod with NO local podspec (SocketRocket, a trunk
|
|
134
|
+
# pod). Version comes from Helpers::Constants::socket_rocket_config; no
|
|
135
|
+
# subspecs. A missing/blank constant is a hard error rather than a silent
|
|
136
|
+
# versionless facade — a bare `pod 'SocketRocket'` in the source path is
|
|
137
|
+
# `"~> #{socket_rocket_config[:version]}"`, so the facade MUST carry a version
|
|
138
|
+
# that satisfies that constraint.
|
|
139
|
+
def self.synthesized_spec(name, ios_version)
|
|
140
|
+
version = synthesized_version(name)
|
|
141
|
+
base_spec(name, version, ios_version)
|
|
142
|
+
end
|
|
143
|
+
private_class_method :synthesized_spec
|
|
144
|
+
|
|
145
|
+
# Resolves the synthesized version for a no-podspec facade. Fail-closed on a
|
|
146
|
+
# missing constant/version. Only SocketRocket is synthesized today.
|
|
147
|
+
def self.synthesized_version(name)
|
|
148
|
+
case name
|
|
149
|
+
when "SocketRocket"
|
|
150
|
+
unless defined?(Helpers::Constants) && Helpers::Constants.respond_to?(:socket_rocket_config)
|
|
151
|
+
raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
|
|
152
|
+
"Helpers::Constants.socket_rocket_config is unavailable."
|
|
153
|
+
end
|
|
154
|
+
version = Helpers::Constants.socket_rocket_config[:version]
|
|
155
|
+
if version.nil? || version.to_s.strip.empty?
|
|
156
|
+
raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
|
|
157
|
+
"socket_rocket_config[:version] is missing or empty."
|
|
158
|
+
end
|
|
159
|
+
version.to_s
|
|
160
|
+
else
|
|
161
|
+
raise "[RNDepsFacades] No synthesized version rule for facaded pod '#{name}'. " \
|
|
162
|
+
"Add one to synthesized_version or give it a real podspec in FACADE_PODS."
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
private_class_method :synthesized_version
|
|
166
|
+
|
|
167
|
+
# Loads the real podspec so we can mirror its structure. A facaded pod with a
|
|
168
|
+
# declared podspec path MUST have a readable real podspec — if it's missing or
|
|
169
|
+
# unparseable we raise rather than ship an empty facade (which would silently
|
|
170
|
+
# drop subspecs / the version, the very drift this mechanism prevents).
|
|
171
|
+
def self.load_real_spec(path, name)
|
|
172
|
+
unless File.exist?(path)
|
|
173
|
+
raise "[RNDepsFacades] Real podspec for facaded pod '#{name}' not found at #{path}. " \
|
|
174
|
+
"Update FACADE_PODS in rndeps_facades.rb if the podspec moved."
|
|
175
|
+
end
|
|
176
|
+
begin
|
|
177
|
+
Pod::Specification.from_file(path)
|
|
178
|
+
rescue => e
|
|
179
|
+
raise "[RNDepsFacades] Failed to read real podspec for facaded pod '#{name}' at #{path}: #{e.message}"
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
private_class_method :load_real_spec
|
|
183
|
+
|
|
184
|
+
# Library (non-test, non-app) subspec names of the real spec, so third-party
|
|
185
|
+
# libs depending on `<pod>/<subspec>` (e.g. `RCT-Folly/Fabric`) keep
|
|
186
|
+
# resolving. Derived, never hand-listed.
|
|
187
|
+
def self.derive_subspecs(real)
|
|
188
|
+
real.subspecs
|
|
189
|
+
.reject { |ss| ss.test_specification? || (ss.respond_to?(:app_specification?) && ss.app_specification?) }
|
|
190
|
+
.map(&:base_name)
|
|
191
|
+
end
|
|
192
|
+
private_class_method :derive_subspecs
|
|
193
|
+
end
|
package/scripts/cocoapods/spm.rb
CHANGED
|
@@ -19,28 +19,46 @@ class SPMManager
|
|
|
19
19
|
log 'Cleaning old SPM dependencies from Pods project'
|
|
20
20
|
clean_spm_dependencies_from_target(project, @dependencies_by_pod)
|
|
21
21
|
log 'Adding SPM dependencies to Pods project'
|
|
22
|
+
flattened_pod_names = []
|
|
22
23
|
@dependencies_by_pod.each do |pod_name, dependencies|
|
|
24
|
+
target = project.targets.find { |t| t.name == pod_name}
|
|
23
25
|
dependencies.each do |spm_spec|
|
|
24
26
|
log "Adding SPM dependency on product #{spm_spec[:products]}"
|
|
25
27
|
add_spm_to_target(
|
|
26
28
|
project,
|
|
27
|
-
|
|
29
|
+
target,
|
|
28
30
|
spm_spec[:url],
|
|
29
31
|
spm_spec[:requirement],
|
|
30
32
|
spm_spec[:products]
|
|
31
33
|
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
if target.product_type == 'com.apple.product-type.library.static'
|
|
35
|
+
# Xcode emits a package C-target's module map both into the shared
|
|
36
|
+
# products dir and into this pod's CONFIGURATION_BUILD_DIR, and both
|
|
37
|
+
# copies are unavoidably visible (the package swiftmodule serializes
|
|
38
|
+
# the products-root path) — clang hard-errors with "redefinition of
|
|
39
|
+
# module" on Xcode 26.3. Building the pod straight into the products
|
|
40
|
+
# root makes both paths the same file, and puts the package
|
|
41
|
+
# swiftmodule on the pod's default search path.
|
|
42
|
+
log " Building #{pod_name} into the shared products dir to avoid duplicate module maps"
|
|
43
|
+
target.build_configurations.each do |config|
|
|
44
|
+
target.build_settings(config.name)['CONFIGURATION_BUILD_DIR'] = '${PODS_CONFIGURATION_BUILD_DIR}'
|
|
45
|
+
end
|
|
46
|
+
flattened_pod_names << pod_name unless flattened_pod_names.include?(pod_name)
|
|
47
|
+
else
|
|
48
|
+
log " Adding workaround for Swift package not found issue"
|
|
49
|
+
target.build_configurations.each do |config|
|
|
50
|
+
target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
|
|
51
|
+
search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
|
|
52
|
+
unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
|
|
53
|
+
target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
|
|
54
|
+
end
|
|
39
55
|
end
|
|
40
56
|
end
|
|
41
57
|
end
|
|
42
58
|
end
|
|
43
59
|
|
|
60
|
+
rewrite_aggregate_modulemap_references(installer, flattened_pod_names) unless flattened_pod_names.empty?
|
|
61
|
+
|
|
44
62
|
unless @dependencies_by_pod.empty?
|
|
45
63
|
log_warning "If you're using Xcode 15 or earlier you might need to close and reopen the Xcode workspace"
|
|
46
64
|
unless ENV["USE_FRAMEWORKS"] == "dynamic"
|
|
@@ -53,6 +71,55 @@ class SPMManager
|
|
|
53
71
|
|
|
54
72
|
private
|
|
55
73
|
|
|
74
|
+
# Flattening a pod's build dir moves its generated modulemap from
|
|
75
|
+
# "<Pod>/<Pod>.modulemap" to "<Pod>.modulemap"; the aggregate xcconfigs
|
|
76
|
+
# reference the old path via -fmodule-map-file. Must mutate the in-memory
|
|
77
|
+
# Config attributes — later post_install steps (NewArchitectureHelper) re-save
|
|
78
|
+
# the same Config objects and would clobber a plain file edit.
|
|
79
|
+
def rewrite_aggregate_modulemap_references(installer, pod_names)
|
|
80
|
+
installer.aggregate_targets.each do |aggregate_target|
|
|
81
|
+
aggregate_target.xcconfigs.each do |config_name, config_file|
|
|
82
|
+
%w[OTHER_CFLAGS OTHER_SWIFT_FLAGS].each do |key|
|
|
83
|
+
value = config_file.attributes[key]
|
|
84
|
+
next unless value
|
|
85
|
+
|
|
86
|
+
updated_value = pod_names.reduce(value) do |acc, pod_name|
|
|
87
|
+
acc.gsub(
|
|
88
|
+
"${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}/#{pod_name}.modulemap",
|
|
89
|
+
"${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}.modulemap"
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
config_file.attributes[key] = updated_value if updated_value != value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
config_file.save_as(aggregate_target.xcconfig_path(config_name))
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Creates a new object in the project with a UUID guaranteed not to collide
|
|
101
|
+
# with any UUID already present in the project.
|
|
102
|
+
#
|
|
103
|
+
# `Pod::Project` overrides `generate_available_uuid_list` with a fast,
|
|
104
|
+
# counter-based scheme (`<sha prefix><counter>0`) that deliberately skips
|
|
105
|
+
# collision checks, on the assumption that the whole Pods project is generated
|
|
106
|
+
# in a single pass. That assumption does not hold here: we run in a
|
|
107
|
+
# `post_install` hook, and the generator's counter can be out of sync with the
|
|
108
|
+
# UUIDs already assigned to existing objects (e.g. when the project has been
|
|
109
|
+
# reloaded from disk during an incremental install, the counter restarts at 0
|
|
110
|
+
# while the root object still occupies `<prefix>00000000`). Using `project.new`
|
|
111
|
+
# directly can therefore hand back a UUID that is already in use and overwrite
|
|
112
|
+
# an existing object (notably the root `PBXProject`), producing a Pods project
|
|
113
|
+
# Xcode refuses to load. We keep the deterministic scheme but probe forward
|
|
114
|
+
# until we find a UUID that is actually free.
|
|
115
|
+
def new_object(project, klass)
|
|
116
|
+
uuid = project.generate_uuid
|
|
117
|
+
uuid = project.generate_uuid while project.objects_by_uuid.key?(uuid)
|
|
118
|
+
object = klass.new(project, uuid)
|
|
119
|
+
object.initialize_defaults
|
|
120
|
+
object
|
|
121
|
+
end
|
|
122
|
+
|
|
56
123
|
def log(msg)
|
|
57
124
|
::Pod::UI.puts "[SPM] #{msg}"
|
|
58
125
|
end
|
|
@@ -76,7 +143,7 @@ class SPMManager
|
|
|
76
143
|
pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
|
|
77
144
|
pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.relative_path == url }
|
|
78
145
|
if !pkg
|
|
79
|
-
pkg = project
|
|
146
|
+
pkg = new_object(project, pkg_class)
|
|
80
147
|
pkg.relative_path = url
|
|
81
148
|
log(" Adding local package to workspace: #{pkg.inspect}")
|
|
82
149
|
project.root_object.package_references << pkg
|
|
@@ -85,7 +152,7 @@ class SPMManager
|
|
|
85
152
|
pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
|
|
86
153
|
pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url }
|
|
87
154
|
if !pkg
|
|
88
|
-
pkg = project
|
|
155
|
+
pkg = new_object(project, pkg_class)
|
|
89
156
|
pkg.repositoryURL = url
|
|
90
157
|
pkg.requirement = requirement
|
|
91
158
|
log(" Adding remote package to workspace: #{pkg.inspect}")
|
|
@@ -101,7 +168,7 @@ class SPMManager
|
|
|
101
168
|
next if ref
|
|
102
169
|
|
|
103
170
|
log(" Adding product dependency #{product_name} to #{target.name}")
|
|
104
|
-
ref = project
|
|
171
|
+
ref = new_object(project, ref_class)
|
|
105
172
|
ref.package = pkg
|
|
106
173
|
ref.product_name = product_name
|
|
107
174
|
target.package_product_dependencies << ref
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// swift-tools-version: 6.0
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// AUTO-GENERATED by scripts/setup-apple-spm.js – do not edit manually.
|
|
10
|
+
// This SPM-specific template replaces the CocoaPods codegen template
|
|
11
|
+
// with xcframework configuration built in.
|
|
12
|
+
|
|
13
|
+
import PackageDescription
|
|
14
|
+
|
|
15
|
+
// React headers need NO search paths: the React module comes from the
|
|
16
|
+
// invariant ReactHeaders Clang target,
|
|
17
|
+
// every other RN namespace from the ReactNativeHeaders binaryTarget
|
|
18
|
+
// (pure-RN), the third-party deps namespaces (folly/glog/boost/...) from the
|
|
19
|
+
// ReactNativeDependenciesHeaders sidecar, and the per-app generated headers
|
|
20
|
+
// from the ReactAppHeaders target below — all auto-served by SPM through
|
|
21
|
+
// product/target dependencies.
|
|
22
|
+
let headersDep: [Target.Dependency] = [
|
|
23
|
+
.product(name: "ReactHeaders", package: "ReactNative"),
|
|
24
|
+
.product(name: "ReactNativeHeaders", package: "ReactNative"),
|
|
25
|
+
.product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
|
|
26
|
+
"ReactAppHeaders",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
let package = Package(
|
|
30
|
+
name: "React-GeneratedCode",
|
|
31
|
+
platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)],
|
|
32
|
+
products: [
|
|
33
|
+
.library(
|
|
34
|
+
name: "ReactCodegen",
|
|
35
|
+
targets: ["ReactCodegen"]),
|
|
36
|
+
.library(
|
|
37
|
+
name: "ReactAppDependencyProvider",
|
|
38
|
+
targets: ["ReactAppDependencyProvider"]),
|
|
39
|
+
.library(
|
|
40
|
+
name: "ReactAppHeaders",
|
|
41
|
+
targets: ["ReactAppHeaders"]),
|
|
42
|
+
],
|
|
43
|
+
dependencies: [
|
|
44
|
+
.package(name: "ReactNative", path: "../../xcframeworks"),
|
|
45
|
+
],
|
|
46
|
+
targets: [
|
|
47
|
+
.target(
|
|
48
|
+
name: "ReactCodegen",
|
|
49
|
+
dependencies: headersDep,
|
|
50
|
+
path: "ReactCodegen",
|
|
51
|
+
exclude: ["ReactCodegen.podspec"],
|
|
52
|
+
publicHeadersPath: ".",
|
|
53
|
+
cSettings: [.headerSearchPath("headers")],
|
|
54
|
+
// DEBUG/NDEBUG match the prebuilt React.framework's config-gated C++
|
|
55
|
+
// ABI: NDEBUG (Release) strips DebugStringConvertible's vtable and
|
|
56
|
+
// shifts the ShadowNode layout that codegen'd Props inherit, so this
|
|
57
|
+
// target must compile with the same NDEBUG state or the Release link
|
|
58
|
+
// fails (undefined "vtable for DebugStringConvertible"). Mirrors
|
|
59
|
+
// packages/react-native/Package.swift.
|
|
60
|
+
cxxSettings: [
|
|
61
|
+
.headerSearchPath("headers"),
|
|
62
|
+
.define("DEBUG", .when(configuration: .debug)),
|
|
63
|
+
.define("NDEBUG", .when(configuration: .release)),
|
|
64
|
+
],
|
|
65
|
+
linkerSettings: [
|
|
66
|
+
.linkedFramework("Foundation")
|
|
67
|
+
]
|
|
68
|
+
),
|
|
69
|
+
.target(
|
|
70
|
+
name: "ReactAppDependencyProvider",
|
|
71
|
+
dependencies: ["ReactCodegen"] + headersDep,
|
|
72
|
+
path: "ReactAppDependencyProvider",
|
|
73
|
+
exclude: ["ReactAppDependencyProvider.podspec"],
|
|
74
|
+
publicHeadersPath: ".",
|
|
75
|
+
cSettings: [.headerSearchPath(".."), .headerSearchPath("headers")],
|
|
76
|
+
cxxSettings: [
|
|
77
|
+
.headerSearchPath(".."),
|
|
78
|
+
.headerSearchPath("headers"),
|
|
79
|
+
// Match the prebuilt React.framework NDEBUG-gated C++ ABI (see above).
|
|
80
|
+
.define("DEBUG", .when(configuration: .debug)),
|
|
81
|
+
.define("NDEBUG", .when(configuration: .release)),
|
|
82
|
+
],
|
|
83
|
+
linkerSettings: [
|
|
84
|
+
.linkedFramework("Foundation")
|
|
85
|
+
]
|
|
86
|
+
),
|
|
87
|
+
// The per-app generated-headers farm (built by spm sync), vended as a
|
|
88
|
+
// normal SPM headers target.
|
|
89
|
+
.target(
|
|
90
|
+
name: "ReactAppHeaders",
|
|
91
|
+
path: "ReactAppHeaders",
|
|
92
|
+
publicHeadersPath: "."
|
|
93
|
+
),
|
|
94
|
+
],
|
|
95
|
+
// React Native headers require C++20 (concepts, std::optional, etc.)
|
|
96
|
+
cxxLanguageStandard: .cxx20
|
|
97
|
+
)
|
|
@@ -80,6 +80,26 @@ source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
|
|
|
80
80
|
HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
|
|
81
81
|
[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
|
|
82
82
|
|
|
83
|
+
# SwiftPM consumers have no Pods directory to provide hermesc. When there is
|
|
84
|
+
# no Pods installation at all and the current HERMES_CLI_PATH does not exist
|
|
85
|
+
# (unset, or an injector-baked path gone stale after a package-store move),
|
|
86
|
+
# resolve the hermes-compiler npm package THROUGH react-native's own dependency
|
|
87
|
+
# graph — react-native pins the hermes-compiler version, so the compiler's
|
|
88
|
+
# bytecode version always matches the prebuilt hermes VM artifacts (a
|
|
89
|
+
# mismatched pair crashes at launch with "Wrong bytecode version").
|
|
90
|
+
# STRICTLY SwiftPM-scoped: in a CocoaPods build the hermes-engine pod owns both
|
|
91
|
+
# the VM and hermesc, and its hermes train may legitimately differ from the npm
|
|
92
|
+
# hermes-compiler — falling back there would compile bundles the podded VM
|
|
93
|
+
# rejects.
|
|
94
|
+
if [[ ( -z "$PODS_ROOT" || ! -d "$PODS_ROOT" ) && ! -f "$HERMES_CLI_PATH" ]]; then
|
|
95
|
+
NODE_HERMESC=$(RN_DIR="$REACT_NATIVE_DIR" "$NODE_BINARY" --print \
|
|
96
|
+
"require('path').join(require('path').dirname(require.resolve('hermes-compiler/package.json', {paths: [process.env.RN_DIR]})), 'hermesc', 'osx-bin', 'hermesc')" \
|
|
97
|
+
2>/dev/null || true)
|
|
98
|
+
if [[ -n "$NODE_HERMESC" && -f "$NODE_HERMESC" ]]; then
|
|
99
|
+
HERMES_CLI_PATH="$NODE_HERMESC"
|
|
100
|
+
fi
|
|
101
|
+
fi
|
|
102
|
+
|
|
83
103
|
# If hermesc is not available and USE_HERMES is not set to false, show error.
|
|
84
104
|
if [[ $USE_HERMES != false && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
|
|
85
105
|
echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \
|