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
109 changes: 109 additions & 0 deletions packages/react-native/React-Core-prebuilt.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# 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"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))
version = package['version']

source = ReactNativeCoreUtils.resolve_podspec_source()

header_search_paths = [
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt",
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt/RCTDeprecation",
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt/ReactCommon/yoga",
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt/ReactCommon",
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt/Libraries/AppDelegate",
"${PODS_ROOT}/Headers/Private/React-Core-prebuilt/Libraries",

"$(REACT_NATIVE_PATH)/React/Base",
"$(REACT_NATIVE_PATH)/ReactCommon",
"$(REACT_NATIVE_PATH)/Libraries",
"$(REACT_NATIVE_PATH)/ReactApple",
"$(REACT_NATIVE_PATH)/ReactCxxPlatform",
"$(REACT_NATIVE_PATH)/ReactCommon/react/runtime/platform/ios",
"${REACT_NATIVE_PATH}/ReactCommon/jsi",
"$(REACT_NATIVE_PATH)/ReactCommon/jsiexecutor/",
"$(REACT_NATIVE_PATH)/ReactCommon/react/nativemodule/samples/platform/ios",
"$(REACT_NATIVE_PATH)/ReactCommon/react/nativemodule/samples",
]

Pod::Spec.new do |spec|
spec.name = 'React-Core-prebuilt'
spec.version = version
spec.summary = "Prebuilt core of React Native."
spec.homepage = "https://reactnative.dev/"
spec.description = 'Prebuilt React Native Core libraries and headers'
spec.homepage = 'https://github.com/facebook/react-native'
spec.license = package['license']
spec.authors = 'meta'
spec.platforms = min_supported_versions
spec.source = source

spec.vendored_frameworks = "React.xcframework"

spec.preserve_paths = '**/*.*'
spec.header_mappings_dir = 'React.xcframework/Headers'
spec.source_files = 'React.xcframework/Headers/**/*.{h,hpp}'

spec.module_name = 'React'
spec.module_map = 'React.xcframework/Modules/module.modulemap'
spec.public_header_files = 'React.xcframework/Headers/**/*.h'

# Setup the consuming project's search paths
spec.user_target_xcconfig = {
"HEADER_SEARCH_PATHS" => header_search_paths,
"SWIFT_INCLUDE_PATHS" => "${PODS_ROOT}/Headers/Private/React-Core-prebuilt",
'DEFINES_MODULE' => 'YES',
'CLANG_ENABLE_MODULES' => 'YES',
}

spec.pod_target_xcconfig = {
'WARNING_CFLAGS' => '-Wno-comma -Wno-shorten-64-to-32',
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
'DEFINES_MODULE' => 'YES',
'CLANG_ENABLE_MODULES' => 'YES',
}

# We need to make sure that the React.xcframework is copied correctly - in the downloaded tarball
# the root directory is the framework, but when using it we need to have it in a subdirectory
# called React.xcframework, so we need to move the contents of the tarball into that directory.
# This is done in the prepare_command.
spec.prepare_command = <<~'CMD'
CURRENT_PATH=$(pwd)
XCFRAMEWORK_PATH="${CURRENT_PATH}/React.xcframework"
mkdir -p "${XCFRAMEWORK_PATH}"
find "$CURRENT_PATH" -mindepth 1 -maxdepth 1 ! -name "$(basename "$XCFRAMEWORK_PATH")" -exec mv {} "$XCFRAMEWORK_PATH" \;
CMD

# If we are passing a local tarball, we don't want to switch between Debug and Release
if !ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]
script_phase = {
:name => "[RNDeps] Replace React Native Core for the right configuration, if needed",
:execution_position => :before_compile,
:script => <<-EOS
. "$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh"

CONFIG="Release"
if echo $GCC_PREPROCESSOR_DEFINITIONS | grep -q "DEBUG=1"; then
CONFIG="Debug"
fi

# TODO(T228219721): Add this for React Native Core as well
##### "$NODE_BINARY" "$REACT_NATIVE_PATH/third-party-podspecs/replace_dependencies_version.js" -c "$CONFIG" -r "#{version}" -p "$PODS_ROOT"
EOS
}


# :always_out_of_date is only available in CocoaPods 1.13.0 and later
if Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.13.0')
# always run the script without warning
script_phase[:always_out_of_date] = "1"
end

spec.script_phase = script_phase
end

end
220 changes: 220 additions & 0 deletions packages/react-native/scripts/cocoapods/rncore.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# 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'

## - 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.

class ReactNativeCoreUtils
@@build_from_source = true
@@react_native_path = ""
@@react_native_version = ""
@@use_nightly = 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"]

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}")
rncore_log("Source: #{self.resolve_podspec_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 => "file://#{ENV["RCT_TESTONLY_RNCORE_TARBALL_PATH"]}" }
end

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

url = stable_tarball_url(@@react_native_version, :debug)
rncore_log("Using tarball from URL: #{url}")
download_stable_rndeps(@@react_native_path, @@react_native_version, :debug)
download_stable_rndeps(@@react_native_path, @@react_native_version, :release)
return {:http => url}
end

def self.stable_tarball_url(version, build_type)
maven_repo_url = "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-#{build_type.to_s}.tar.gz"
end

def self.nightly_tarball_url(version)
artefact_coordinate = "react-native-artifacts"
artefact_name = "reactnative-core-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(URI(xml_url))
if response.kind_of? Net::HTTPSuccess
xml = REXML::Document.new(response)
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_rndeps(react_native_path, version, configuration)
tarball_url = stable_tarball_url(version, configuration)
download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
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}
end

def self.download_rndeps_tarball(react_native_path, tarball_url, version, configuration)
destination_path = configuration == nil ?
"#{artifacts_dir()}/reactnative-core-debug.tar.gz-#{version}.tar.gz" :
"#{artifacts_dir()}/reactnative-core-debug.tar.gz-#{version}-#{configuration}.tar.gz"

unless File.exist?(destination_path)
# Download to a temporary file first so we don't cache incomplete downloads.
tmp_file = "#{artifacts_dir()}/reactnative-core-debug.tar.gz.download"
`mkdir -p "#{artifacts_dir()}" && curl "#{tarball_url}" -Lo "#{tmp_file}" && mv "#{tmp_file}" "#{destination_path}"`
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).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] ' + message
case level
when :info
Pod::UI.puts log_message.green
when :error
Pod::UI.puts log_message.red
else
Pod::UI.puts log_message.yellow
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

end
Loading
Loading