first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
# Overrides CocoaPods `Installer`/'Podfile' classes to patch podspecs on the fly
# See: https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/installer.rb
# See: https://github.com/CocoaPods/Core/blob/master/lib/cocoapods-core/podfile.rb#L160
#
# This is necessary to disable `USE_FRAMEWORKS` for specific pods that include
# React Native Core headers in their public headers, which causes issues when
# building them as dynamic frameworks with modular headers enabled.
module Pod
class Podfile
public
def framework_modules_to_patch
@framework_modules_to_patch ||= ['ExpoModulesCore', 'ExpoModulesJSI', 'Expo', 'ReactAppDependencyProvider', 'expo-dev-menu']
end
def expo_add_modules_to_patch(modules)
framework_modules_to_patch.concat(modules)
end
end
class Installer
private
_original_perform_post_install_actions = instance_method(:perform_post_install_actions)
_original_run_podfile_pre_install_hooks = instance_method(:run_podfile_pre_install_hooks)
_script_phase_name = '[Expo Autolinking] Run Codegen with autolinking'
public
define_method(:perform_post_install_actions) do
# Call original implementation first
_original_perform_post_install_actions.bind(self).()
# Next we'll perform an Expo workaround for Codegen in React Native where it uses the wrong output path for
# the generated files. This can be remove when the following PR is merged and released upstream:
# https://github.com/facebook/react-native/pull/54066
# TODO: chrfalch - remove when RN PR is released
# Find the ReactCodegen pod target in the pods project
react_codegen_native_target = self.pods_project.targets.find { |target| target.name == 'ReactCodegen' }
if react_codegen_native_target
# Check if the build phase already exists
already_exists = react_codegen_native_target.build_phases.any? do |phase|
phase.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && phase.name == _script_phase_name
end
if !already_exists
Pod::UI.puts "[Expo] ".blue + "Adding '#{_script_phase_name}' build phase to ReactCodegen"
# Create the new shell script build phase
phase = react_codegen_native_target.new_shell_script_build_phase(_script_phase_name)
phase.shell_path = '/bin/sh'
phase.shell_script = <<~SH
# Remove this step when the fix is merged and released.
# See: https://github.com/facebook/react-native/pull/54066
# This re-runs Codegen without the broken "scripts/react_native_pods_utils/script_phases.sh" script, causing Codegen to run without autolinking.
# Instead of using "script_phases.sh" which always runs inside DerivedData, we run it in the normal "/ios" folder
# See: https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods_utils/script_phases.sh
pushd "$PODS_ROOT/../" > /dev/null
RCT_SCRIPT_POD_INSTALLATION_ROOT="$PODS_ROOT/.."
popd >/dev/null
export RCT_SCRIPT_RN_DIR="$REACT_NATIVE_PATH" # This is set by Expo
export RCT_SCRIPT_APP_PATH="$RCT_SCRIPT_POD_INSTALLATION_ROOT/.."
export RCT_SCRIPT_OUTPUT_DIR="$RCT_SCRIPT_POD_INSTALLATION_ROOT"
export RCT_SCRIPT_TYPE="withCodegenDiscovery"
# This is the broken script that runs inside DerivedData, meaning it can't find the autolinking result in `ios/build/generated/autolinking.json`.
# Resulting in Codegen running with it's own autolinking, not discovering transitive peer dependencies.
# export SCRIPT_PHASES_SCRIPT="$RCT_SCRIPT_RN_DIR/scripts/react_native_pods_utils/script_phases.sh"
export WITH_ENVIRONMENT="$RCT_SCRIPT_RN_DIR/scripts/xcode/with-environment.sh"
# Start of workaround code
# Load the $NODE_BINARY from the "with-environment.sh" script
source "$WITH_ENVIRONMENT"
# Run this script directly in the right folders:
# https://github.com/facebook/react-native/blob/3f7f9d8fb8beb41408d092870a7c7cac58029a4d/packages/react-native/scripts/react_native_pods_utils/script_phases.sh#L96-L101
pushd "$RCT_SCRIPT_RN_DIR" >/dev/null || exit 1
set -x
"$NODE_BINARY" "scripts/generate-codegen-artifacts.js" --path "$RCT_SCRIPT_APP_PATH" --outputPath "$RCT_SCRIPT_OUTPUT_DIR" --targetPlatform "ios"
set +x
popd >/dev/null || exit 1
# End of workaround code
SH
# Find the index of the "Compile Sources" phase (PBXSourcesBuildPhase)
compile_sources_index = react_codegen_native_target.build_phases.find_index do |p|
p.is_a?(Xcodeproj::Project::Object::PBXSourcesBuildPhase)
end
if compile_sources_index
# Remove the phase from its current position (it was added at the end)
react_codegen_native_target.build_phases.delete(phase)
# Insert it before the "Compile Sources" phase
react_codegen_native_target.build_phases.insert(compile_sources_index, phase)
else
Pod::UI.puts "[Expo] ".yellow + "Could not find 'Compile Sources' phase, build phase added at default position"
end
end
else
Pod::UI.puts "[Expo] ".yellow + "ReactCodegen target not found in pods project"
end
end
define_method(:run_podfile_pre_install_hooks) do
# Call original implementation first
_original_run_podfile_pre_install_hooks.bind(self).()
return unless should_disable_use_frameworks_for_core_expo_pods?()
# Disable USE_FRAMEWORKS in core targets when USE_FRAMEWORKS is set
# This method overrides the build_type field to always use static_library for
# the following pod targets:
# - ExpoModulesCore, Expo, ReactAppDependencyProvider, expo-dev-menu
# These are all including files from React Native Core in their public header files,
# which causes their own modular headers to be invalid.
Pod::UI.puts "[Expo] ".blue + "Disabling USE_FRAMEWORKS for modules #{@podfile.framework_modules_to_patch.join(', ')}"
self.pod_targets.each do |t|
if @podfile.framework_modules_to_patch.include?(t.name)
def t.build_type
Pod::BuildType.static_library
end
end
end
end
private
# We should only disable USE_FRAMEWORKS for specific pods when:
# - RCT_USE_PREBUILT_RNCORE is not '1'
# - USE_FRAMEWORKS is not set
def should_disable_use_frameworks_for_core_expo_pods?()
return false if ENV['RCT_USE_PREBUILT_RNCORE'] != '1'
return true if get_linkage?() != nil
false
end
# Returns the linkage type if USE_FRAMEWORKS is set, otherwise returns nil
def get_linkage?()
return nil if ENV["USE_FRAMEWORKS"] == nil
return :dynamic if ENV["USE_FRAMEWORKS"].downcase == 'dynamic'
return :static if ENV["USE_FRAMEWORKS"].downcase == 'static'
nil
end
end
end

View File

@@ -0,0 +1,43 @@
# Overrides CocoaPods `Sandbox` class to patch podspecs on the fly
# See: https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/sandbox.rb
require 'json'
REACT_DEFINE_MODULES_LIST = [
'React-hermes',
'React-jsc',
]
module Pod
class Sandbox
private
_original_store_podspec = instance_method(:store_podspec)
public
define_method(:store_podspec) do |name, podspec, _external_source, json|
spec = _original_store_podspec.bind(self).(name, podspec, _external_source, json)
patched_spec = nil
# Patch podspecs to define module
if REACT_DEFINE_MODULES_LIST.include? name
spec_json = JSON.parse(podspec.to_pretty_json)
spec_json['pod_target_xcconfig'] ||= {}
spec_json['pod_target_xcconfig']['DEFINES_MODULE'] = 'YES'
patched_spec = Specification.from_json(spec_json.to_json)
end
if patched_spec != nil
# Store the patched spec with original checksum and local saved file path
patched_spec.defined_in_file = spec.defined_in_file
patched_spec.instance_variable_set(:@checksum, spec.checksum)
@stored_podspecs[spec.name] = patched_spec
return patched_spec
end
return spec
end # define_method(:store_podspec)
end # class Sandbox
end # module Pod

View File

@@ -0,0 +1,22 @@
# Overrides CocoaPods class as the AutolinkingManager is in fact part of
# the target definitions and we need to refer to it at later steps.
# See: https://github.com/CocoaPods/Core/blob/master/lib/cocoapods-core/podfile/target_definition.rb
module Pod
class Podfile
class TargetDefinition
public
attr_writer :autolinking_manager
def autolinking_manager
if @autolinking_manager.present? || root?
@autolinking_manager
else
parent.autolinking_manager
end
end
end
end
end

View File

@@ -0,0 +1,62 @@
require_relative '../project_integrator'
# Unfortunately there is no good and official place that we could use to generate module providers
# and integrate them with user targets by operating on the PBXProj kept and saved by CocoaPods.
# So we have to hook into the private method called `integrate_user_targets`
# where we have public access to everything that we need.
# Original implementation: https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/installer/user_project_integrator.rb
module Pod
class Installer
class UserProjectIntegrator
include Expo::ProjectIntegrator
private
_original_integrate_user_targets = instance_method(:integrate_user_targets)
# Integrates the targets of the user projects with the libraries
# generated from the {Podfile}.
#
# @note {TargetDefinition} without dependencies are skipped to prevent
# creating empty libraries for target definitions which are only
# wrappers for others.
#
# @return [void]
#
define_method(:integrate_user_targets) do
# Call original method first
results = _original_integrate_user_targets.bind(self).()
UI.message '- Integrating Expo modules providers' do
# All user targets mapped to user projects.
all_projects = targets.map { |target| target.user_project }.uniq
# Array of projects to integrate is usually a subset of `all_projects`,
# and it might be empty subsequent installs after the first install.
# CocoaPods integrates only these ones.
projects_to_integrate = user_projects_to_integrate()
# However, we need to make sure that all projects are integrated,
# regardless of the CocoaPods cache.
all_projects.each do |project|
project_targets = targets.select { |target| target.user_project.equal?(project) }
Expo::ProjectIntegrator::integrate_targets_in_project(project_targets, project)
Expo::ProjectIntegrator::remove_nils_from_source_files(project)
Expo::ProjectIntegrator::set_autolinking_configuration(project)
# CocoaPods saves the projects to integrate at the next step,
# but in some cases we're modifying other projects as well.
# Below we make sure the project will be saved and no more than once!
if project.dirty? && !projects_to_integrate.include?(project)
save_projects([project])
end
end
end
results
end
end # class UserProjectIntegrator
end # class Installer
end # module Pod