Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 229 additions & 27 deletions packages/react-native/scripts/cocoapods/rncore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ def add_rncore_dependency(s)
## - 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
Expand All @@ -40,6 +42,7 @@ def self.setup_rncore(react_native_path, 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
Expand All @@ -66,7 +69,6 @@ def self.setup_rncore(react_native_path, react_native_version)
rncore_log("No prebuilt artifacts found, reverting to building from source.")
end
rncore_log("Building from source: #{@@build_from_source}")
rncore_log("Source: #{self.resolve_podspec_source()}")
end
end

Expand All @@ -89,9 +91,8 @@ def self.resolve_podspec_source()

if ENV["RCT_USE_PREBUILT_RNCORE"] == "1"
if @@use_nightly
rncore_log("Using nightly tarball")
begin
return self.podspec_source_download_prebuilt_nightly_tarball(@@react_native_version)
return self.podspec_source_download_prebuilt_nightly_tarball()
rescue => e
rncore_log("Failed to download nightly tarball: #{e.message}", :error)
return
Expand Down Expand Up @@ -123,14 +124,213 @@ def self.podspec_source_download_prebuild_stable_tarball()
return
end

url = stable_tarball_url(@@react_native_version, :debug)
rncore_log("Using tarball from URL: #{url}")
download_stable_rncore(@@react_native_path, @@react_native_version, :debug)
download_stable_rncore(@@react_native_path, @@react_native_version, :release)
return {:http => url}
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 => URI::File.build(path: destinationDebug).to_s }
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 => URI::File.build(path: destinationDebug).to_s }
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")
`(cd "$(dirname "#{dsyms_tmp_dir}")" && mkdir -p React.xcframework && cp -r "$(basename "#{dsyms_tmp_dir}")" React.xcframework/dSYMs && tar -rf "#{frameworkTarPath}" React.xcframework/dSYMs && rm -rf React.xcframework)`

# 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")

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.stable_tarball_url(version, build_type)
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
mappings = [
# Make sure to make react_native_path absolute
["/Users/runner/work/react-native/react-native/packages/react-native", "#{File.expand_path(@@react_native_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|
" <key>#{from}</key><string>#{to}</string>"
end.join("\n")

# Use the first mapping for legacy keys
first_from, first_to = mappings.first

return <<~PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DBGVersion</key><string>3</string>
<key>DBGBuildSourcePath</key><string>#{first_from}</string>
<key>DBGSourcePath</key><string>#{first_to}</string>
<key>DBGSourcePathRemapping</key>
<dict>
#{remapping_entries}
</dict>
</dict>
</plist>
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 =
Expand All @@ -140,12 +340,12 @@ def self.stable_tarball_url(version, build_type)
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-#{build_type.to_s}.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)
def self.nightly_tarball_url(version, configuration, dsyms = false)
artefact_coordinate = "react-native-artifacts"
artefact_name = "reactnative-core-debug.tar.gz"
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))
Expand All @@ -162,26 +362,28 @@ def self.nightly_tarball_url(version)
end
end

def self.download_stable_rncore(react_native_path, version, configuration)
tarball_url = stable_tarball_url(version, configuration)
download_rncore_tarball(react_native_path, tarball_url, version, configuration)
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.podspec_source_download_prebuilt_nightly_tarball(version)
url = nightly_tarball_url(version)
rncore_log("Using nightly tarball from URL: #{url}")
return {:http => url}
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)
def self.download_rncore_tarball(react_native_path, tarball_url, version, configuration, dsyms = false)
destination_path = configuration == nil ?
"#{artifacts_dir()}/reactnative-core-#{version}.tar.gz" :
"#{artifacts_dir()}/reactnative-core-#{version}-#{configuration}.tar.gz"
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}.tar.gz" :
"#{artifacts_dir()}/reactnative-core-#{version}#{dsyms ? "-dSYM" : ""}-#{configuration}.tar.gz"

unless File.exist?(destination_path)
# Download to a temporary file first so we don't cache incomplete downloads.
rncore_log("Downloading ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball from #{tarball_url} to #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
tmp_file = "#{artifacts_dir()}/reactnative-core.download"
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
else
rncore_log("Using downloaded ReactNativeCore-prebuilt #{dsyms ? "dSYMs " : ""}#{configuration ? configuration.to_s : ""} tarball at #{Pathname.new(destination_path).relative_path_from(Pathname.pwd).to_s}")
end

return destination_path
Expand All @@ -192,7 +394,7 @@ def self.release_artifact_exists(version)
end

def self.nightly_artifact_exists(version)
return artifact_exists(nightly_tarball_url(version).gsub("\\", ""))
return artifact_exists(nightly_tarball_url(version, :debug).gsub("\\", ""))
end

def self.artifacts_dir()
Expand All @@ -210,14 +412,14 @@ def self.rncore_log(message, level = :info)
if !Object.const_defined?("Pod::UI")
return
end
log_message = '[ReactNativeCore] ' + message
log_message = '[ReactNativeCore] '
case level
when :info
Pod::UI.puts log_message.green
Pod::UI.puts log_message.green + message
when :error
Pod::UI.puts log_message.red
Pod::UI.puts log_message.red + message
else
Pod::UI.puts log_message.yellow
Pod::UI.puts log_message.yellow + message
end
end

Expand Down
15 changes: 6 additions & 9 deletions packages/react-native/scripts/cocoapods/rndependencies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ def self.resolve_podspec_source()

if ENV["RCT_USE_RN_DEP"] && ENV["RCT_USE_RN_DEP"] == "1"
if @@use_nightly
rndeps_log("Using nightly tarball")
begin
return self.podspec_source_download_prebuilt_nightly_tarball(@@react_native_version)
rescue => e
Expand All @@ -84,7 +83,6 @@ def self.resolve_podspec_source()
end
end

rndeps_log("Using release tarball")
begin
return self.podspec_source_download_prebuild_release_tarball()
rescue => e
Expand Down Expand Up @@ -160,9 +158,10 @@ def self.podspec_source_download_prebuild_release_tarball()

url = release_tarball_url(@@react_native_version, :debug)
rndeps_log("Using tarball from URL: #{url}")
download_stable_rndeps(@@react_native_path, @@react_native_version, :debug)
destinationDebug = download_stable_rndeps(@@react_native_path, @@react_native_version, :debug)
download_stable_rndeps(@@react_native_path, @@react_native_version, :release)
return {:http => url}

return {:http => URI::File.build(path: destinationDebug).to_s }
end

def self.release_tarball_url(version, build_type)
Expand Down Expand Up @@ -204,7 +203,6 @@ def self.download_stable_rndeps(react_native_path, version, configuration)

def self.podspec_source_download_prebuilt_nightly_tarball(version)
url = nightly_tarball_url(version)
rndeps_log("Using nightly tarball from URL: #{url}")
return {:http => url}
end

Expand Down Expand Up @@ -245,14 +243,13 @@ def self.rndeps_log(message, level = :info)
if !Object.const_defined?("Pod::UI")
return
end
log_message = '[ReactNativeDependencies] ' + message
case level
when :info
Pod::UI.puts log_message.green
Pod::UI.puts '[ReactNativeDependencies] '.green + message
when :error
Pod::UI.puts log_message.red
Pod::UI.puts '[ReactNativeDependencies] '.red + message
else
Pod::UI.puts log_message.yellow
Pod::UI.puts '[ReactNativeDependencies] '.yellow + message
end
end

Expand Down
Loading