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
6require 'json'
7require 'open3'
8require 'pathname'
9require_relative './react_native_pods_utils/script_phases.rb'
10require_relative './cocoapods/hermes.rb'
11require_relative './cocoapods/flipper.rb'
12require_relative './cocoapods/fabric.rb'
13require_relative './cocoapods/codegen.rb'
14require_relative './cocoapods/codegen_utils.rb'
15require_relative './cocoapods/utils.rb'
16require_relative './cocoapods/new_architecture.rb'
17require_relative './cocoapods/local_podspec_patch.rb'
18
19$ABI47_0_0CODEGEN_OUTPUT_DIR = 'versioned-react-native/ABI47_0_0/ReactNative/codegen/ios'
20$CODEGEN_COMPONENT_DIR = 'react/renderer/components'
21$CODEGEN_MODULE_DIR = '.'
22
23$START_TIME = Time.now.to_i
24
25# Function that setup all the react native dependencies
26
27# Parameters
28# - path: path to react_native installation.
29# - fabric_enabled: whether fabric should be enabled or not.
30# - new_arch_enabled: whether the new architecture should be enabled or not.
31# - production: whether the dependencies must be installed to target a Debug or a Release build.
32# - hermes_enabled: whether Hermes should be enabled or not.
33# - flipper_configuration: The configuration to use for flipper.
34# - app_path: path to the React Native app. Required by the New Architecture.
35# - config_file_dir: directory of the `package.json` file, required by the New Architecture.
36def use_react_native_ABI47_0_0! (
37  path: "../node_modules/react-native",
38  fabric_enabled: false,
39  new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == '1',
40  production: ENV['PRODUCTION'] == '1',
41  hermes_enabled: true,
42  flipper_configuration: FlipperConfiguration.disabled,
43  app_path: '..',
44  config_file_dir: '')
45
46  prefix = path
47
48  # The version of folly that must be used
49  folly_version = '2021.07.22.00'
50
51  ReactNativePodsUtils.warn_if_not_on_arm64()
52
53  # The Pods which should be included in all projects
54  pod 'ABI47_0_0FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector", :project_name => 'ABI47_0_0'
55  pod 'ABI47_0_0FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec", :project_name => 'ABI47_0_0'
56  pod 'ABI47_0_0RCTRequired', :path => "#{prefix}/Libraries/RCTRequired", :project_name => 'ABI47_0_0'
57  pod 'ABI47_0_0RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :project_name => 'ABI47_0_0', :modular_headers => true
58  pod 'ABI47_0_0React', :path => "#{prefix}/", :project_name => 'ABI47_0_0'
59  pod 'ABI47_0_0React-Core', :path => "#{prefix}/", :project_name => 'ABI47_0_0'
60  pod 'ABI47_0_0React-CoreModules', :path => "#{prefix}/React/CoreModules", :project_name => 'ABI47_0_0'
61  pod 'ABI47_0_0React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS", :project_name => 'ABI47_0_0'
62  pod 'ABI47_0_0React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation", :project_name => 'ABI47_0_0'
63  pod 'ABI47_0_0React-RCTBlob', :path => "#{prefix}/Libraries/Blob", :project_name => 'ABI47_0_0'
64  pod 'ABI47_0_0React-RCTImage', :path => "#{prefix}/Libraries/Image", :project_name => 'ABI47_0_0'
65  pod 'ABI47_0_0React-RCTLinking', :path => "#{prefix}/Libraries/LinkingIOS", :project_name => 'ABI47_0_0'
66  pod 'ABI47_0_0React-RCTNetwork', :path => "#{prefix}/Libraries/Network", :project_name => 'ABI47_0_0'
67  pod 'ABI47_0_0React-RCTSettings', :path => "#{prefix}/Libraries/Settings", :project_name => 'ABI47_0_0'
68  pod 'ABI47_0_0React-RCTText', :path => "#{prefix}/Libraries/Text", :project_name => 'ABI47_0_0'
69  pod 'ABI47_0_0React-RCTVibration', :path => "#{prefix}/Libraries/Vibration", :project_name => 'ABI47_0_0'
70  pod 'ABI47_0_0React-Core/RCTWebSocket', :path => "#{prefix}/", :project_name => 'ABI47_0_0'
71
72  pod 'ABI47_0_0React-bridging', :path => "#{prefix}/ReactCommon", :project_name => 'ABI47_0_0'
73  pod 'ABI47_0_0React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact", :project_name => 'ABI47_0_0'
74  pod 'ABI47_0_0React-jsi', :path => "#{prefix}/ReactCommon/jsi", :project_name => 'ABI47_0_0'
75  pod 'ABI47_0_0React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor", :project_name => 'ABI47_0_0'
76  pod 'ABI47_0_0React-jsinspector', :path => "#{prefix}/ReactCommon/jsinspector", :project_name => 'ABI47_0_0'
77  pod 'ABI47_0_0React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker", :project_name => 'ABI47_0_0'
78  pod 'ABI47_0_0React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor", :project_name => 'ABI47_0_0'
79  pod 'ABI47_0_0React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger", :project_name => 'ABI47_0_0'
80  pod 'ABI47_0_0React-logger', :path => "#{prefix}/ReactCommon/logger", :project_name => 'ABI47_0_0'
81  pod 'ABI47_0_0ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :project_name => 'ABI47_0_0', :modular_headers => true
82  pod 'ABI47_0_0Yoga', :path => "#{prefix}/ReactCommon/yoga", :project_name => 'ABI47_0_0', :modular_headers => true
83
84  # pod 'ABI47_0_0DoubleConversion', :podspec => "#{prefix}/third-party-podspecs/DoubleConversion.podspec"
85  # pod 'ABI47_0_0glog', :podspec => "#{prefix}/third-party-podspecs/glog.podspec"
86  # pod 'ABI47_0_0boost', :podspec => "#{prefix}/third-party-podspecs/boost.podspec"
87  # pod 'ABI47_0_0RCT-Folly', :podspec => "#{prefix}/third-party-podspecs/RCT-Folly.podspec", :modular_headers => true
88
89  run_codegen!(
90    app_path,
91    config_file_dir,
92    :new_arch_enabled => new_arch_enabled,
93    :disable_codegen => ENV['DISABLE_CODEGEN'] == '1',
94    :react_native_path => prefix,
95    :fabric_enabled => fabric_enabled,
96    :codegen_output_dir => $ABI47_0_0CODEGEN_OUTPUT_DIR,
97    :package_json_file => File.join(__dir__, "..", "package.json"),
98    :folly_version => folly_version
99  )
100
101  pod 'ABI47_0_0React-Codegen', :path => $ABI47_0_0CODEGEN_OUTPUT_DIR, :modular_headers => true
102
103  if fabric_enabled
104    checkAndGenerateEmptyThirdPartyProvider!(prefix, new_arch_enabled, $ABI47_0_0CODEGEN_OUTPUT_DIR)
105    setup_fabric!(prefix)
106  end
107
108  install_hermes_if_enabled(hermes_enabled, prefix)
109
110  # CocoaPods `configurations` option ensures that the target is copied only for the specified configurations,
111  # but those dependencies are still built.
112  # Flipper doesn't currently compile for release https://github.com/facebook/react-native/issues/33764
113  # Setting the production flag to true when build for production make sure that we don't install Flipper in the app in the first place.
114  if flipper_configuration.flipper_enabled && !production
115    install_flipper_dependencies(prefix)
116    use_flipper_pods(flipper_configuration.versions, :configurations => flipper_configuration.configurations)
117  end
118
119  pods_to_update = LocalPodspecPatch.pods_to_update(:react_native_path => prefix)
120  if !pods_to_update.empty?
121    if Pod::Lockfile.public_instance_methods.include?(:detect_changes_with_podfile)
122      Pod::Lockfile.prepend(LocalPodspecPatch)
123    else
124      Pod::UI.warn "Automatically updating #{pods_to_update.join(", ")} has failed, please run `pod update #{pods_to_update.join(" ")} --no-repo-update` manually to fix the issue."
125    end
126  end
127end
128
129# It returns the default flags.
130def get_default_flags()
131  return ReactNativePodsUtils.get_default_flags()
132end
133
134# It installs the flipper dependencies into the project.
135#
136# Parameters
137# - versions: a dictionary of Flipper Library -> Versions that can be used to customize which version of Flipper to install.
138# - configurations: an array of configuration where to install the dependencies.
139def use_flipper!(versions = {}, configurations: ['Debug'])
140  Pod::UI.warn "use_flipper is deprecated, use the flipper_configuration option in the use_react_native function"
141  use_flipper_pods(versions, :configurations => configurations)
142end
143
144# Function that executes after React Native has been installed to configure some flags and build settings.
145#
146# Parameters
147# - installer: the Cocoapod object that allows to customize the project.
148# - react_native_path: path to React Native.
149# - mac_catalyst_enabled: whether we are running the Pod on a Mac Catalyst project or not.
150def react_native_post_install(installer, react_native_path = "../node_modules/react-native", mac_catalyst_enabled: false)
151  ReactNativePodsUtils.turn_off_resource_bundle_react_core(installer)
152
153  ReactNativePodsUtils.apply_mac_catalyst_patches(installer) if mac_catalyst_enabled
154
155  if ReactNativePodsUtils.has_pod(installer, 'Flipper')
156    flipper_post_install(installer)
157  end
158
159  ReactNativePodsUtils.exclude_i386_architecture_while_using_hermes(installer)
160  ReactNativePodsUtils.fix_library_search_paths(installer)
161  ReactNativePodsUtils.fix_react_bridging_header_search_paths(installer)
162  ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path)
163
164  NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer)
165  is_new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
166  NewArchitectureHelper.modify_flags_for_new_architecture(installer, is_new_arch_enabled)
167
168  Pod::UI.puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run".green
169end
170
171# === LEGACY METHOD ===
172# We need to keep this while we continue to support the old architecture.
173# =====================
174def use_react_native_codegen_ABI47_0_0!(spec, options={})
175  return if ENV['RCT_NEW_ARCH_ENABLED'] == '1'
176  # TODO: Once the new codegen approach is ready for use, we should output a warning here to let folks know to migrate.
177
178  # The prefix to react-native
179  react_native_path = options[:react_native_path] ||= "../.."
180
181  # Library name (e.g. FBReactNativeSpec)
182  library_name = options[:library_name] ||= "#{spec.name.gsub('_','-').split('-').collect(&:capitalize).join}Spec"
183  Pod::UI.puts "[Codegen] Found #{library_name}"
184
185  relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
186  output_dir = options[:output_dir] ||= $ABI47_0_0CODEGEN_OUTPUT_DIR
187  output_dir_module = "#{output_dir}/#{$CODEGEN_MODULE_DIR}"
188  output_dir_component = "#{output_dir}/#{$CODEGEN_COMPONENT_DIR}"
189
190  codegen_config = {
191    "modules" => {
192      :js_srcs_pattern => "Native*.js",
193      :generated_dir => "#{relative_installation_root}/#{output_dir_module}/#{library_name}",
194      :generated_files => [
195        "#{library_name}.h",
196        "#{library_name}-generated.mm"
197      ]
198    },
199    "components" => {
200      :js_srcs_pattern => "*NativeComponent.js",
201      :generated_dir => "#{relative_installation_root}/#{output_dir_component}/#{library_name}",
202      :generated_files => [
203        "ComponentDescriptors.h",
204        "EventEmitters.cpp",
205        "EventEmitters.h",
206        "Props.cpp",
207        "Props.h",
208        "RCTComponentViewHelpers.h",
209        "ShadowNodes.cpp",
210        "ShadowNodes.h"
211      ]
212    }
213  }
214
215  # The path to JavaScript files
216  js_srcs_dir = options[:js_srcs_dir] ||= "./"
217  library_type = options[:library_type]
218
219  if library_type
220    if !codegen_config[library_type]
221      raise "[Codegen] invalid library_type: #{library_type}. Check your podspec to make sure it's set to 'modules' or 'components'. Removing the option will generate files for both"
222    end
223    js_srcs_pattern = codegen_config[library_type][:js_srcs_pattern]
224  end
225
226  if library_type
227    generated_dirs = [ codegen_config[library_type][:generated_dir] ]
228    generated_files = codegen_config[library_type][:generated_files].map { |filename| "#{codegen_config[library_type][:generated_dir]}/#{filename}" }
229  else
230    generated_dirs = [ codegen_config["modules"][:generated_dir], codegen_config["components"][:generated_dir] ]
231    generated_files = codegen_config["modules"][:generated_files].map { |filename| "#{codegen_config["modules"][:generated_dir]}/#{filename}" }
232    generated_files = generated_files.concat(codegen_config["components"][:generated_files].map { |filename| "#{codegen_config["components"][:generated_dir]}/#{filename}" })
233  end
234
235  if js_srcs_pattern
236    file_list = `find #{js_srcs_dir} -type f -name #{js_srcs_pattern}`.split("\n").sort
237    input_files = file_list.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}" }
238  else
239    input_files = [ js_srcs_dir ]
240  end
241
242  # Prepare filesystem by creating empty files that will be picked up as references by CocoaPods.
243  prepare_command = "mkdir -p #{generated_dirs.join(" ")} && touch -a #{generated_files.join(" ")}"
244  system(prepare_command) # Always run prepare_command when a podspec uses the codegen, as CocoaPods may skip invoking this command in certain scenarios. Replace with pre_integrate_hook after updating to CocoaPods 1.11
245  spec.prepare_command = prepare_command
246
247  env_files = ["$PODS_ROOT/../.xcode.env.local", "$PODS_ROOT/../.xcode.env"]
248
249  spec.script_phase = {
250    :name => 'Generate Specs',
251    :input_files => input_files + env_files, # This also needs to be relative to Xcode
252    :output_files => ["${DERIVED_FILE_DIR}/codegen-#{library_name}.log"].concat(generated_files.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}"} ),
253    # The final generated files will be created when this script is invoked at Xcode build time.
254    :script => get_script_phases_no_codegen_discovery_ABI47_0_0(
255      react_native_path: react_native_path,
256      codegen_output_dir: $ABI47_0_0CODEGEN_OUTPUT_DIR,
257      codegen_module_dir: $CODEGEN_MODULE_DIR,
258      codegen_component_dir: $CODEGEN_COMPONENT_DIR,
259      library_name: library_name,
260      library_type: library_type,
261      js_srcs_pattern: js_srcs_pattern,
262      js_srcs_dir: js_srcs_dir,
263      file_list: file_list
264    ),
265    :execution_position => :before_compile,
266    :show_env_vars_in_log => true
267  }
268end
269
270# This provides a post_install workaround for build issues related Xcode 12.5 and Apple Silicon (M1) machines.
271# Call this in the app's main Podfile's post_install hook.
272# See https://github.com/facebook/react-native/issues/31480#issuecomment-902912841 for more context.
273# Actual fix was authored by https://github.com/mikehardy.
274# New app template will call this for now until the underlying issue is resolved.
275def __apply_Xcode_12_5_M1_post_install_workaround(installer)
276  # Flipper podspecs are still targeting an older iOS deployment target, and may cause an error like:
277  #   "error: thread-local storage is not supported for the current target"
278  # The most reliable known workaround is to bump iOS deployment target to match react-native (iOS 11 now).
279  installer.pods_project.targets.each do |target|
280    target.build_configurations.each do |config|
281      # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 11.0
282      deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
283      should_upgrade = deployment_target < 11.0 && deployment_target != 0.0
284      if should_upgrade
285        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
286      end
287    end
288  end
289
290  # But... doing so caused another issue in Flipper:
291  #   "Time.h:52:17: error: typedef redefinition with different types"
292  # We need to make a patch to RCT-Folly - remove the `__IPHONE_OS_VERSION_MIN_REQUIRED` check.
293  # See https://github.com/facebook/flipper/issues/834 for more details.
294  time_header = "#{Pod::Config.instance.installation_root.to_s}/Pods/RCT-Folly/folly/portability/Time.h"
295  `sed -i -e  $'s/ && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)//' #{time_header}`
296end
297