# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require 'json'
require 'net/http'
require 'rexml/document'
require_relative './utils.rb'
### Adds ReactNativeCore-prebuilt as a dependency to the given podspec if we're not
### building ReactNativeCore from source (then this function does nothing).
def add_rncore_dependency(s)
if !ReactNativeCoreUtils.build_rncore_from_source()
# Add the dependency
s.dependency "React-Core-prebuilt"
current_pod_target_xcconfig = s.to_hash["pod_target_xcconfig"] || {}
current_pod_target_xcconfig = current_pod_target_xcconfig.to_h unless current_pod_target_xcconfig.is_a?(Hash)
# Add VFS overlay flags for both Objective-C and Swift
# The VFS overlay file is pre-resolved at pod install time for each platform slice.
# We reference it directly in the xcframework using the React-VFS.yaml file that
# is written to the React-Core-prebuilt folder during setup_vfs_overlay.
# See scripts/ios-prebuild/__docs__/README.md for more details on VFS overlays.
vfs_overlay_flag = "-ivfsoverlay $(PODS_ROOT)/React-Core-prebuilt/React-VFS.yaml"
current_pod_target_xcconfig["OTHER_CFLAGS"] ||= "$(inherited)"
current_pod_target_xcconfig["OTHER_CFLAGS"] += " #{vfs_overlay_flag}"
current_pod_target_xcconfig["OTHER_CPLUSPLUSFLAGS"] ||= "$(inherited)"
current_pod_target_xcconfig["OTHER_CPLUSPLUSFLAGS"] += " #{vfs_overlay_flag}"
# For Swift, we need to use -Xcc to pass flags to the underlying Clang compiler
# Both the flag and its argument need separate -Xcc prefixes
current_pod_target_xcconfig["OTHER_SWIFT_FLAGS"] ||= "$(inherited)"
current_pod_target_xcconfig["OTHER_SWIFT_FLAGS"] += " -Xcc -ivfsoverlay -Xcc $(PODS_ROOT)/React-Core-prebuilt/React-VFS.yaml"
s.pod_target_xcconfig = current_pod_target_xcconfig
end
end
## - RCT_USE_PREBUILT_RNCORE: If set to 1, it will use the release tarball from Maven instead of building from source.
## - RCT_TESTONLY_RNCORE_TARBALL_PATH: **TEST ONLY** If set, it will use a local tarball of RNCore if it exists.
## - RCT_TESTONLY_RNCORE_VERSION: **TEST ONLY** If set, it will override the version of RNCore to be used.
## - RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS: If set to 1, it will download the dSYMs for the prebuilt RNCore frameworks and install these in the framework folders
class ReactNativeCoreUtils
@@build_from_source = true
@@react_native_path = ""
@@react_native_version = ""
@@use_nightly = false
@@download_dsyms = false
## Sets up wether ReactNative Core should be built from source or not.
## If RCT_USE_PREBUILT_RNCORE is set to 1 and the artifacts exists on Maven, it will
## not build from source. Otherwise, it will build from source.
def self.setup_rncore(react_native_path, react_native_version)
# We don't want setup to be called multiple times, so we check if the variables are already set.
if @@react_native_version == ""
rncore_log("Setting up ReactNativeCore...")
@@react_native_path = react_native_path
@@react_native_version = ENV["RCT_TESTONLY_RNCORE_VERSION"] == nil ? react_native_version : ENV["RCT_TESTONLY_RNCORE_VERSION"]
@@download_dsyms = ENV["RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS"] == "1"
if @@react_native_version.include? "nightly"
@@use_nightly = true
if ENV["RCT_TESTONLY_RNCORE_VERSION"] == "nightly"
@@react_native_version = ReactNativeDependenciesUtils.get_nightly_npm_version()
rncore_log("Using nightly version from npm: #{@@react_native_version}")
else
rncore_log("Using nightly build #{@@react_native_version}")
end
end
if ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]
abort_if_use_local_rncore_with_no_file()
end
use_local_xcframework = ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"] && File.exist?(ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"])
artifacts_exists = ENV["RCT_USE_PREBUILT_RNCORE"] == "1" && (@@use_nightly ? nightly_artifact_exists(@@react_native_version) : release_artifact_exists(@@react_native_version))
@@build_from_source = !use_local_xcframework && !artifacts_exists
if @@build_from_source && ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"] && !use_local_xcframework
rncore_log("No local xcframework found, reverting to building from source.")
end
if @@build_from_source && ENV["RCT_USE_PREBUILT_RNCORE"] && !artifacts_exists
rncore_log("No prebuilt artifacts found, reverting to building from source.")
end
rncore_log("Building from source: #{@@build_from_source}")
end
end
def self.abort_if_use_local_rncore_with_no_file()
if !File.exist?(ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"])
abort("RCT_TESTONLY_RNCORE_TARBALL_PATH is set to #{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]} but the file does not exist!")
end
end
def self.build_rncore_from_source()
return @@build_from_source
end
def self.resolve_podspec_source()
if ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]
abort_if_use_local_rncore_with_no_file()
rncore_log("Using local xcframework at #{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]}")
return {:http => ReactNativePodsUtils.local_file_uri(ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]) }
end
if ENV["RCT_USE_PREBUILT_RNCORE"] == "1"
if @@use_nightly
begin
return self.podspec_source_download_prebuilt_nightly_tarball()
rescue => e
rncore_log("Failed to download nightly tarball: #{e.message}", :error)
return
end
end
begin
return self.podspec_source_download_prebuild_stable_tarball()
rescue => e
rncore_log("Failed to download release tarball: #{e.message}", :error)
return
end
end
end
def self.podspec_source_download_prebuild_stable_tarball()
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
destinationDebug = download_stable_rncore(@@react_native_path, @@react_native_version, :debug)
destinationRelease = download_stable_rncore(@@react_native_path, @@react_native_version, :release)
if @@download_dsyms
dSymsDebug = download_stable_rncore(@@react_native_path, @@react_native_version, :debug, true)
dSymsRelease = download_stable_rncore(@@react_native_path, @@react_native_version, :release, true)
rncore_log("Resolved stable dSYMs")
rncore_log(" #{Pathname.new(dSymsDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(dSymsRelease).relative_path_from(Pathname.pwd).to_s}")
# Make sure that the dSYMs are processed
process_dsyms(destinationDebug, dSymsDebug)
process_dsyms(destinationRelease, dSymsRelease)
end
rncore_log("Resolved stable ReactNativeCore-prebuilt version:")
rncore_log(" #{Pathname.new(destinationDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(destinationRelease).relative_path_from(Pathname.pwd).to_s}")
return {:http => stable_tarball_url(@@react_native_version, :debug) } unless @@download_dsyms
return {:http => ReactNativePodsUtils.local_file_uri(destinationDebug) }
end
def self.podspec_source_download_prebuilt_nightly_tarball()
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
destinationDebug = download_nightly_rncore(@@react_native_path, @@react_native_version, :debug)
destinationRelease = download_nightly_rncore(@@react_native_path, @@react_native_version, :release)
if @@download_dsyms
dSymsDebug = download_nightly_rncore(@@react_native_path, @@react_native_version, :debug, true)
dSymsRelease = download_nightly_rncore(@@react_native_path, @@react_native_version, :release, true)
rncore_log("Resolved nightly dSYMs")
rncore_log(" #{Pathname.new(dSymsDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(dSymsRelease).relative_path_from(Pathname.pwd).to_s}")
# Make sure that the dSYMs are processed
process_dsyms(destinationDebug, dSymsDebug)
process_dsyms(destinationRelease, dSymsRelease)
end
rncore_log("Resolved nightly ReactNativeCore-prebuilt version:")
rncore_log(" #{Pathname.new(destinationDebug).relative_path_from(Pathname.pwd).to_s}")
rncore_log(" #{Pathname.new(destinationRelease).relative_path_from(Pathname.pwd).to_s}")
return {:http => nightly_tarball_url(@@react_native_version, :debug) } unless @@download_dsyms
return {:http => ReactNativePodsUtils.local_file_uri(destinationDebug) }
end
def self.process_dsyms(frameworkTarball, dSymsTarball)
if !@@download_dsyms
return
end
if @@react_native_path == ""
rncore_log("react_native_path is not set", :error)
return
end
if @@react_native_version == ""
rncore_log("react_native_version is not set", :error)
return
end
if @@build_from_source
return
end
# gunzip the dSymsTarball and the frameworkTarball into a temporary folder
# and then copy the dSYMs into the framework folder and then tar/gz the framework folder again
# into the same location as the original frameworkTarball
rncore_log("Adding symbols #{Pathname.new(dSymsTarball).relative_path_from(Pathname.pwd).to_s} to framework tarball #{Pathname.new(frameworkTarball).relative_path_from(Pathname.pwd).to_s}")
FileUtils.mkdir_p(File.dirname(frameworkTarball))
FileUtils.cp(frameworkTarball, "#{frameworkTarball}.orig")
rncore_log(" Backed up original tarballs")
begin
# Now let's gunzip the framework tarball into a .tar file
# Get filename and foldername from the tarball path
frameworkFolder = File.dirname(frameworkTarball)
frameworkFilename = File.basename(frameworkTarball, ".tar.gz")
frameworkTarPath = File.join(frameworkFolder, frameworkFilename + ".tar")
# Now gunzip the tarball into the frameworkFolder - this will remove the .gz file and leave us with a .tar file
rncore_log(" Unpacking framework tarball")
`gunzip "#{frameworkTarball}"`
# Now let's untar the dSyms tarball into a temporary folder / dSYMs subfolder
dsyms_tmp_dir = "#{artifacts_dir}/dSYMs"
rncore_log(" Unpacking dSYMs to temporary folder")
`mkdir -p "#{dsyms_tmp_dir}" && tar -xzf "#{dSymsTarball}" -C "#{dsyms_tmp_dir}"`
# Now we need to remap the symbol files to be relative to the framework folder
remap_sourcemaps_for_symbols(dsyms_tmp_dir)
# Add the dSYMs folder to the framework folder
rncore_log(" Adding dSYMs to framework tarball")
# Move symbol bundles into each of the slices in the xcframework
# Example:
# move dSYMs/ios-arm64/. into React.xcframework/ios-arm64/React.framework/dSYMs/.
Dir.glob(File.join(dsyms_tmp_dir, "*")).each do |dsym_path|
slice_name = File.basename(dsym_path)
slice_dsym_dest = File.join("React.xcframework", slice_name, "React.framework", "dSYMs")
rncore_log(" Adding dSYM slice #{slice_name} into tarball at #{slice_dsym_dest}")
`(cd "#{File.dirname(frameworkTarPath)}" && mkdir -p "#{slice_dsym_dest}" && cp -R "#{dsym_path}/." "#{slice_dsym_dest}" && tar -rf "#{frameworkTarPath}" "#{slice_dsym_dest}")`
end
# Now gzip the framework tarball again - remember to use the .tar file and not the .gz file
rncore_log(" Packing #{Pathname.new(frameworkTarPath).relative_path_from(Pathname.pwd).to_s}")
`gzip -1 "#{frameworkTarPath}"`
# Clean up the temporary folder
FileUtils.remove_entry(dsyms_tmp_dir)
rncore_log(" Processed dSYMs into framework tarball #{Pathname.new(frameworkTarball).relative_path_from(Pathname.pwd).to_s}")
# Remove backup of original tarballs
FileUtils.rm_f("#{frameworkTarball}.orig")
# Remove temp dSYMs folder and the temp Framework folder
FileUtils.rm_rf(dsyms_tmp_dir)
FileUtils.rm_rf(File.join(artifacts_dir, "React.xcframework"))
rescue => e
rncore_log("Failed to process dSYMs: #{e.message}", :error)
# Restore the original tarballs
FileUtils.mv("#{frameworkTarball}.orig", frameworkTarball) if File.exist?("#{frameworkTarball}.orig")
rncore_log("Restored original tarballs", :error)
abort "Couldn't process dSYMs: #{e.message}"
end
end
def self.remap_sourcemaps_for_symbols(symbolsPath)
rncore_log(" Remapping dSYMs to be relative to framework folder")
# Find all .dSYM bundles in the symbols path
dsym_bundles = []
Dir.glob(File.join(symbolsPath, "**", "*.dSYM")).each do |path|
if File.directory?(path)
# Check if it's a valid dSYM bundle with Info.plist
info_plist = File.join(path, 'Contents', 'Info.plist')
dsym_bundles << path if File.exist?(info_plist)
end
end
return if dsym_bundles.empty?
# Define source path mappings - from absolute build paths to relative framework paths
# Expand the path relative to the installation root (project root, parent of ios/)
react_native_absolute_path = File.expand_path(@@react_native_path, Pod::Config.instance.installation_root)
mappings = [
["/Users/runner/work/react-native/react-native/packages/react-native", react_native_absolute_path],
]
dsym_bundles.each do |dsym_path| begin
# Get UUIDs for this dSYM bundle
uuid_output = `dwarfdump --uuid "#{dsym_path}" 2>/dev/null`
uuids = uuid_output.scan(/UUID:\s+([0-9A-F-]{36})/i).flatten
next if uuids.empty?
# Create Resources directory if it doesn't exist
resources_dir = File.join(dsym_path, 'Contents', 'Resources')
FileUtils.mkdir_p(resources_dir)
# Generate plist content with path mappings
plist_content = generate_plist_content(mappings)
# Write plist for each UUID
uuids.each do |uuid|
plist_path = File.join(resources_dir, "#{uuid}.plist")
File.write(plist_path, plist_content)
end
rescue => e
rncore_log(" Failed to process dSYM #{dsym_path}: #{e.message}", :error)
end
end
rncore_log(" Completed dSYM remapping for #{dsym_bundles.length} bundles")
end
def self.generate_plist_content(mappings)
# Generate the source path remapping entries
remapping_entries = mappings.map do |from, to|
" #{from}#{to}"
end.join("\n")
# Use the first mapping for legacy keys
first_from, first_to = mappings.first
return <<~PLIST
DBGVersion3
DBGBuildSourcePath#{first_from}
DBGSourcePath#{first_to}
DBGSourcePathRemapping
#{remapping_entries}
PLIST
end
def self.stable_tarball_url(version, build_type, dsyms = false)
## You can use the `ENTERPRISE_REPOSITORY` ariable to customise the base url from which artifacts will be downloaded.
## The mirror's structure must be the same of the Maven repo the react-native core team publishes on Maven Central.
maven_repo_url =
ENV['ENTERPRISE_REPOSITORY'] != nil && ENV['ENTERPRISE_REPOSITORY'] != "" ?
ENV['ENTERPRISE_REPOSITORY'] :
"https://repo1.maven.org/maven2"
group = "com/facebook/react"
# Sample url from Maven:
# https://repo1.maven.org/maven2/com/facebook/react/react-native-artifacts/0.81.0/react-native-artifacts-0.81.0-reactnative-core-debug.tar.gz
return "#{maven_repo_url}/#{group}/react-native-artifacts/#{version}/react-native-artifacts-#{version}-reactnative-core-#{dsyms ? "dSYM-" : ""}#{build_type.to_s}.tar.gz"
end
def self.nightly_tarball_url(version, configuration, dsyms = false)
artefact_coordinate = "react-native-artifacts"
artefact_name = "reactnative-core-#{dsyms ? "dSYM-" : ""}#{configuration ? configuration : "debug"}.tar.gz"
xml_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/maven-metadata.xml"
response = Net::HTTP.get_response(URI(xml_url))
if response.is_a?(Net::HTTPSuccess)
xml = REXML::Document.new(response.body)
timestamp = xml.elements['metadata/versioning/snapshot/timestamp'].text
build_number = xml.elements['metadata/versioning/snapshot/buildNumber'].text
full_version = "#{version}-#{timestamp}-#{build_number}"
final_url = "https://central.sonatype.com/repository/maven-snapshots/com/facebook/react/#{artefact_coordinate}/#{version}-SNAPSHOT/#{artefact_coordinate}-#{full_version}-#{artefact_name}"
return final_url
else
return ""
end
end
def self.download_stable_rncore(react_native_path, version, configuration, dsyms = false)
tarball_url = stable_tarball_url(version, configuration, dsyms)
download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms)
end
def self.download_nightly_rncore(react_native_path, version, configuration, dsyms = false)
tarball_url = nightly_tarball_url(version, configuration, dsyms)
download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms)
end
def self.download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms = false)
filename = configuration == nil ?
"reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}.tar.gz" :
"reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}-#{configuration}.tar.gz"
destination_path = "#{artifacts_dir()}/#{filename}"
if File.exist?(destination_path)
rncore_log("Tarball #{filename} already exists in Pods. Skipping download.")
return destination_path
end
`mkdir -p "#{artifacts_dir()}"`
if ReactNativePodsUtils.skip_caches?
rncore_log("RCT_SKIP_CACHES is set. Downloading #{filename} directly (bypassing shared cache).")
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
unless File.exist?(destination_path)
abort("[ReactNativeCore] Failed to download #{filename} from #{tarball_url}. Aborting.")
end
return destination_path
end
cached_path = File.join(ReactNativePodsUtils.shared_cache_dir(), filename)
if File.exist?(cached_path)
rncore_log("Verifying checksum for cached #{filename}...")
if ReactNativePodsUtils.validate_tarball(cached_path, tarball_url)
rncore_log("Cache hit: copying #{filename} from shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
FileUtils.cp(cached_path, destination_path)
else
rncore_log("Shared cache file #{filename} failed SHA verification. Re-downloading.")
File.delete(cached_path)
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
unless File.exist?(destination_path)
abort("[ReactNativeCore] Failed to download #{filename} from #{tarball_url}. Aborting.")
end
rncore_log("Verifying checksum for downloaded #{filename}...")
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
FileUtils.cp(destination_path, cached_path)
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
else
File.delete(destination_path) if File.exist?(destination_path)
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
end
end
else
rncore_log("Cache miss: downloading #{filename} from #{tarball_url}")
# Download to a temporary file first so we don't cache incomplete downloads.
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
`curl -A "react-native-#{version}" "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
unless File.exist?(destination_path)
abort("[ReactNativeCore] Failed to download #{filename} from #{tarball_url}. Aborting.")
end
rncore_log("Verifying checksum for downloaded #{filename}...")
if ReactNativePodsUtils.validate_tarball(destination_path, tarball_url)
# Save to shared cache for future use
`mkdir -p "#{ReactNativePodsUtils.shared_cache_dir()}"`
FileUtils.cp(destination_path, cached_path)
rncore_log("Saved #{filename} to shared cache (#{ReactNativePodsUtils.shared_cache_dir()})")
else
File.delete(destination_path) if File.exist?(destination_path)
abort("[ReactNativeCore] Downloaded file #{filename} failed SHA verification. Aborting.")
end
end
return destination_path
end
def self.release_artifact_exists(version)
return artifact_exists(stable_tarball_url(version, :debug))
end
def self.nightly_artifact_exists(version)
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
end
def self.artifacts_dir()
return File.join(Pod::Config.instance.project_pods_root, "ReactNativeCore-artifacts")
end
# This function checks that ReactNativeCore artifact exists on the maven repo
def self.artifact_exists(tarball_url)
# -L is used to follow redirects, useful for the nightlies
# I also needed to wrap the url in quotes to avoid escaping & and ?.
return (`curl -o /dev/null --silent -Iw '%{http_code}' -L "#{tarball_url}"` == "200")
end
def self.rncore_log(message, level = :info)
if !Object.const_defined?("Pod::UI")
return
end
log_message = '[ReactNativeCore] '
case level
when :info
Pod::UI.puts log_message.green + message
when :error
Pod::UI.puts log_message.red + message
else
Pod::UI.puts log_message.yellow + message
end
end
def self.get_nightly_npm_version()
uri = URI('https://registry.npmjs.org/react-native/nightly')
response = Net::HTTP.get_response(uri)
unless response.is_a?(Net::HTTPSuccess)
raise "Couldn't get an answer from NPM: #{response.code} #{response.message}"
end
json = JSON.parse(response.body)
latest_nightly = json['version']
return latest_nightly
end
# Processes the VFS overlay file from the React.xcframework to resolve the ${ROOT_PATH} placeholder.
# This method should be called from react_native_post_install after pod install completes.
#
# The VFS overlay file maps header import paths to their actual locations within the xcframework.
# Since the xcframework contains platform-specific slices, we generate a resolved VFS file for each
# slice and also create a default VFS file that can be used immediately (before script phases run).
def self.process_vfs_overlay()
return if @@build_from_source
prebuilt_path = File.join(Pod::Config.instance.project_pods_root, "React-Core-prebuilt")
xcframework_path = File.join(prebuilt_path, "React.xcframework")
vfs_template_path = File.join(xcframework_path, "React-VFS-template.yaml")
unless File.exist?(vfs_template_path)
rncore_log("VFS overlay template not found at #{vfs_template_path}", :error)
exit 1
end
rncore_log("Processing VFS overlay file...")
# Read the template content
vfs_template_content = File.read(vfs_template_path)
# Write the VFS file - use the top-level xcframework path
# so that ${ROOT_PATH}/Headers points to the xcframework's Headers folder
resolved_vfs_content = vfs_template_content.gsub('${ROOT_PATH}', xcframework_path)
resolved_vfs_path = File.join(prebuilt_path, "React-VFS.yaml")
File.write(resolved_vfs_path, resolved_vfs_content)
rncore_log(" Created VFS overlay at #{resolved_vfs_path}")
rncore_log("VFS overlay setup complete")
end
# Configures the xcconfig files for aggregate (main app) targets to enable VFS overlay for React Native Core.
# This is needed because the main app target does not go through podspec processing,
# so it won't get the VFS overlay flags from add_rncore_dependency.
#
# Parameters:
# - installer: The CocoaPods installer object
def self.configure_aggregate_xcconfig(installer)
return if @@build_from_source
prebuilt_path = File.join(Pod::Config.instance.project_pods_root, "React-Core-prebuilt")
vfs_overlay_path = File.join(prebuilt_path, "React-VFS.yaml")
unless File.exist?(vfs_overlay_path)
rncore_log("VFS overlay not found at #{vfs_overlay_path}, skipping prebuilt xcconfig configuration", :error)
exit 1
end
rncore_log("Configuring xcconfig for prebuilt React Native Core...")
vfs_overlay_flag = " -ivfsoverlay \"#{vfs_overlay_path}\""
swift_vfs_overlay_flag = " -Xcc -ivfsoverlay -Xcc \"#{vfs_overlay_path}\""
# Add flags to aggregate target xcconfigs (these are used by the main app target)
installer.aggregate_targets.each do |aggregate_target|
aggregate_target.xcconfigs.each do |config_name, config_file|
add_vfs_overlay_flags(config_file.attributes, vfs_overlay_flag, swift_vfs_overlay_flag)
xcconfig_path = aggregate_target.xcconfig_path(config_name)
config_file.save_as(xcconfig_path)
end
end
# Add flags to ALL pod targets (for third-party pods that don't call add_rncore_dependency)
installer.pod_targets.each do |pod_target|
pod_target.build_settings.each do |config_name, build_settings|
xcconfig_path = pod_target.xcconfig_path(config_name)
next unless File.exist?(xcconfig_path)
xcconfig = Xcodeproj::Config.new(xcconfig_path)
# Check if VFS overlay is already present
other_cflags = xcconfig.attributes["OTHER_CFLAGS"] || ""
next if other_cflags.include?("ivfsoverlay")
add_vfs_overlay_flags(xcconfig.attributes, vfs_overlay_flag, swift_vfs_overlay_flag)
xcconfig.save_as(xcconfig_path)
end
end
rncore_log("Prebuilt xcconfig configuration complete")
end
# Helper method to add VFS overlay flags to an xcconfig attributes map
def self.add_vfs_overlay_flags(attributes, vfs_overlay_flag, swift_vfs_overlay_flag)
ReactNativePodsUtils.add_flag_to_map_with_inheritance(attributes, "OTHER_CFLAGS", vfs_overlay_flag)
ReactNativePodsUtils.add_flag_to_map_with_inheritance(attributes, "OTHER_CPLUSPLUSFLAGS", vfs_overlay_flag)
ReactNativePodsUtils.add_flag_to_map_with_inheritance(attributes, "OTHER_SWIFT_FLAGS", swift_vfs_overlay_flag)
# Suppress incomplete umbrella warnings for the prebuilt frameworks (it is expected, as our umbrella headers do not include all headers)
ReactNativePodsUtils.add_flag_to_map_with_inheritance(attributes, "OTHER_SWIFT_FLAGS", " -Xcc -Wno-incomplete-umbrella")
end
end