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/jsengine.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$ABI49_0_0CODEGEN_OUTPUT_DIR = 'versioned-react-native/ABI49_0_0/ReactNative/codegen/ios'
20$CODEGEN_COMPONENT_DIR = 'react/renderer/components'
21$CODEGEN_MODULE_DIR = '.'
22$FOLLY_VERSION = '2021.07.22.00'
23
24$START_TIME = Time.now.to_i
25
26# `@react-native-community/cli-platform-ios/native_modules` defines
27# use_native_modules. We use node to resolve its path to allow for
28# different packager and workspace setups. This is reliant on
29# `@react-native-community/cli-platform-ios` being a direct dependency
30# of `react-native`.
31require Pod::Executable.execute_command('node', ['-p',
32  'require.resolve(
33    "@react-native-community/cli-platform-ios/native_modules.rb",
34    {paths: [process.argv[1]]},
35  )', __dir__]).strip
36
37# This function returns the min iOS version supported by React Native
38# By using this function, you won't have to manually change your Podfile
39# when we change the minimum version supported by the framework.
40def min_ios_version_supported
41  return '12.4'
42end
43
44# This function prepares the project for React Native, before processing
45# all the target exposed by the framework.
46def prepare_react_native_project!
47  # Temporary solution to suppress duplicated GUID error.
48  # Can be removed once we move to generate files outside pod install.
49  install! 'cocoapods', :deterministic_uuids => false
50
51  ReactNativePodsUtils.create_xcode_env_if_missing
52end
53
54# Function that setup all the react native dependencies
55
56# Parameters
57# - path: path to react_native installation.
58# - fabric_enabled: whether fabric should be enabled or not.
59# - new_arch_enabled: whether the new architecture should be enabled or not.
60# - production: whether the dependencies must be installed to target a Debug or a Release build.
61# - hermes_enabled: whether Hermes should be enabled or not.
62# - flipper_configuration: The configuration to use for flipper.
63# - app_path: path to the React Native app. Required by the New Architecture.
64# - config_file_dir: directory of the `package.json` file, required by the New Architecture.
65# - ios_folder: the folder where the iOS code base lives. For a template app, it is `ios`, the default. For RNTester, it is `.`.
66def use_react_native_ABI49_0_0! (
67  path: "../node_modules/react-native",
68  fabric_enabled: false,
69  new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == '1',
70  production: ENV['PRODUCTION'] == '1',
71  hermes_enabled: ENV['USE_HERMES'] && ENV['USE_HERMES'] == '0' ? false : true,
72  flipper_configuration: FlipperConfiguration.disabled,
73  app_path: '..',
74  config_file_dir: '',
75  ios_folder: 'ios'
76)
77
78  # Set the app_path as env variable so the podspecs can access it.
79  ENV['APP_PATH'] = app_path
80  ENV['REACT_NATIVE_PATH'] = path
81
82  # Current target definition is provided by Cocoapods and it refers to the target
83  # that has invoked the `use_react_native!` function.
84  ReactNativePodsUtils.detect_use_frameworks(current_target_definition)
85
86
87  # We are relying on this flag also in third parties libraries to proper install dependencies.
88  # Better to rely and enable this environment flag if the new architecture is turned on using flags.
89  ENV['RCT_NEW_ARCH_ENABLED'] = new_arch_enabled ? "1" : "0"
90  fabric_enabled = fabric_enabled || new_arch_enabled
91  ENV['RCT_FABRIC_ENABLED'] = fabric_enabled ? "1" : "0"
92  ENV['USE_HERMES'] = hermes_enabled ? "1" : "0"
93
94  prefix = path
95
96  ReactNativePodsUtils.warn_if_not_on_arm64()
97
98  # The Pods which should be included in all projects
99  pod 'ABI49_0_0FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector", :project_name => 'ABI49_0_0'
100  pod 'ABI49_0_0FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec", :project_name => 'ABI49_0_0' if !new_arch_enabled
101  pod 'ABI49_0_0RCTRequired', :path => "#{prefix}/Libraries/RCTRequired", :project_name => 'ABI49_0_0'
102  pod 'ABI49_0_0RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :project_name => 'ABI49_0_0', :modular_headers => true
103  pod 'ABI49_0_0React', :path => "#{prefix}/", :project_name => 'ABI49_0_0'
104  pod 'ABI49_0_0React-Core', :path => "#{prefix}/", :project_name => 'ABI49_0_0'
105  pod 'ABI49_0_0React-CoreModules', :path => "#{prefix}/React/CoreModules", :project_name => 'ABI49_0_0'
106  pod 'ABI49_0_0React-RCTAppDelegate', :path => "#{prefix}/Libraries/AppDelegate", :project_name => 'ABI49_0_0'
107  pod 'ABI49_0_0React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS", :project_name => 'ABI49_0_0'
108  pod 'ABI49_0_0React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation", :project_name => 'ABI49_0_0'
109  pod 'ABI49_0_0React-RCTBlob', :path => "#{prefix}/Libraries/Blob", :project_name => 'ABI49_0_0'
110  pod 'ABI49_0_0React-RCTImage', :path => "#{prefix}/Libraries/Image", :project_name => 'ABI49_0_0'
111  pod 'ABI49_0_0React-RCTLinking', :path => "#{prefix}/Libraries/LinkingIOS", :project_name => 'ABI49_0_0'
112  pod 'ABI49_0_0React-RCTNetwork', :path => "#{prefix}/Libraries/Network", :project_name => 'ABI49_0_0'
113  pod 'ABI49_0_0React-RCTSettings', :path => "#{prefix}/Libraries/Settings", :project_name => 'ABI49_0_0'
114  pod 'ABI49_0_0React-RCTText', :path => "#{prefix}/Libraries/Text", :project_name => 'ABI49_0_0'
115  pod 'ABI49_0_0React-RCTVibration', :path => "#{prefix}/Libraries/Vibration", :project_name => 'ABI49_0_0'
116  pod 'ABI49_0_0React-Core/RCTWebSocket', :path => "#{prefix}/", :project_name => 'ABI49_0_0'
117  pod 'ABI49_0_0React-rncore', :path => "#{prefix}/ReactCommon", :project_name => 'ABI49_0_0'
118  pod 'ABI49_0_0React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact", :project_name => 'ABI49_0_0'
119  pod 'ABI49_0_0React-debug', :path => "#{prefix}/ReactCommon/react/debug", :project_name => 'ABI49_0_0'
120  pod 'ABI49_0_0React-utils', :path => "#{prefix}/ReactCommon/react/utils", :project_name => 'ABI49_0_0'
121
122  if hermes_enabled
123    setup_hermes_ABI49_0_0!(:react_native_path => prefix, :fabric_enabled => fabric_enabled)
124  else
125    setup_jsc_ABI49_0_0!(:react_native_path => prefix, :fabric_enabled => fabric_enabled)
126  end
127
128  pod 'ABI49_0_0React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor", :project_name => 'ABI49_0_0'
129  pod 'ABI49_0_0React-jsinspector', :path => "#{prefix}/ReactCommon/jsinspector", :project_name => 'ABI49_0_0'
130
131  pod 'ABI49_0_0React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker", :project_name => 'ABI49_0_0'
132  pod 'ABI49_0_0React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor", :project_name => 'ABI49_0_0'
133  pod 'ABI49_0_0React-runtimescheduler', :path => "#{prefix}/ReactCommon/react/renderer/runtimescheduler", :project_name => 'ABI49_0_0'
134  pod 'ABI49_0_0React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger", :project_name => 'ABI49_0_0'
135  pod 'ABI49_0_0React-logger', :path => "#{prefix}/ReactCommon/logger", :project_name => 'ABI49_0_0'
136  pod 'ABI49_0_0ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :project_name => 'ABI49_0_0', :modular_headers => true
137  pod 'ABI49_0_0React-NativeModulesApple', :path => "#{prefix}/ReactCommon/react/nativemodule/core/platform/ios", :project_name => 'ABI49_0_0', :modular_headers => true
138  pod 'ABI49_0_0Yoga', :path => "#{prefix}/ReactCommon/yoga", :project_name => 'ABI49_0_0', :modular_headers => true
139
140  # pod 'ABI49_0_0DoubleConversion', :podspec => "#{prefix}/third-party-podspecs/DoubleConversion.podspec"
141  # pod 'ABI49_0_0glog', :podspec => "#{prefix}/third-party-podspecs/glog.podspec"
142  # pod 'ABI49_0_0boost', :podspec => "#{prefix}/third-party-podspecs/boost.podspec"
143  # pod 'ABI49_0_0RCT-Folly', :podspec => "#{prefix}/third-party-podspecs/RCT-Folly.podspec", :modular_headers => true
144
145  run_codegen_ABI49_0_0!(
146    app_path,
147    config_file_dir,
148    :new_arch_enabled => new_arch_enabled,
149    :disable_codegen => ENV['DISABLE_CODEGEN'] == '1',
150    :react_native_path => prefix,
151    :fabric_enabled => fabric_enabled,
152    :hermes_enabled => hermes_enabled,
153    :codegen_output_dir => $ABI49_0_0CODEGEN_OUTPUT_DIR,
154    :package_json_file => File.join(__dir__, "..", "package.json"),
155    :folly_version => $FOLLY_VERSION
156  )
157
158  pod 'ABI49_0_0React-Codegen', :path => $ABI49_0_0CODEGEN_OUTPUT_DIR, :modular_headers => true
159
160  if fabric_enabled
161    checkAndGenerateEmptyThirdPartyProvider!(prefix, new_arch_enabled)
162    setup_fabric!(:react_native_path => prefix, new_arch_enabled: new_arch_enabled)
163  else
164    relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
165
166  end
167
168  # CocoaPods `configurations` option ensures that the target is copied only for the specified configurations,
169  # but those dependencies are still built.
170  # Flipper doesn't currently compile for release https://github.com/facebook/react-native/issues/33764
171  # 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.
172  if flipper_configuration.flipper_enabled && !production
173    install_flipper_dependencies(prefix)
174    use_flipper_pods(flipper_configuration.versions, :configurations => flipper_configuration.configurations)
175  end
176
177  pods_to_update = LocalPodspecPatch.pods_to_update(:react_native_path => prefix)
178  if !pods_to_update.empty?
179    if Pod::Lockfile.public_instance_methods.include?(:detect_changes_with_podfile)
180      Pod::Lockfile.prepend(LocalPodspecPatch)
181    else
182      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."
183    end
184  end
185end
186
187# Getter to retrieve the folly flags in case contributors need to apply them manually.
188#
189# Returns: the folly compiler flags
190def folly_flags()
191  return NewArchitectureHelper.folly_compiler_flags
192end
193
194# This function can be used by library developer to prepare their modules for the New Architecture.
195# It passes the Folly Flags to the module, it configures the search path and installs some New Architecture specific dependencies.
196#
197# Parameters:
198# - spec: The spec that has to be configured with the New Architecture code
199# - new_arch_enabled: Whether the module should install dependencies for the new architecture
200def install_modules_dependencies(spec, new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == "1")
201  NewArchitectureHelper.install_modules_dependencies(spec, new_arch_enabled, $FOLLY_VERSION)
202end
203
204# It returns the default flags.
205def get_default_flags()
206  return ReactNativePodsUtils.get_default_flags()
207end
208
209# It installs the flipper dependencies into the project.
210#
211# Parameters
212# - versions: a dictionary of Flipper Library -> Versions that can be used to customize which version of Flipper to install.
213# - configurations: an array of configuration where to install the dependencies.
214def use_flipper!(versions = {}, configurations: ['Debug'])
215  Pod::UI.warn "use_flipper is deprecated, use the flipper_configuration option in the use_react_native function"
216  use_flipper_pods(versions, :configurations => configurations)
217end
218
219# Function that executes after React Native has been installed to configure some flags and build settings.
220#
221# Parameters
222# - installer: the Cocoapod object that allows to customize the project.
223# - react_native_path: path to React Native.
224# - mac_catalyst_enabled: whether we are running the Pod on a Mac Catalyst project or not.
225# - enable_hermes_profiler: whether the hermes profiler should be turned on in Release mode
226def react_native_post_install(
227  installer,
228  react_native_path = "../node_modules/react-native",
229  mac_catalyst_enabled: false
230)
231  ReactNativePodsUtils.turn_off_resource_bundle_react_core(installer)
232
233  ReactNativePodsUtils.apply_mac_catalyst_patches(installer) if mac_catalyst_enabled
234
235  if ReactNativePodsUtils.has_pod(installer, 'Flipper')
236    flipper_post_install(installer)
237  end
238
239  fabric_enabled = ReactNativePodsUtils.has_pod(installer, 'React-Fabric')
240
241  ReactNativePodsUtils.exclude_i386_architecture_while_using_hermes(installer)
242  ReactNativePodsUtils.fix_library_search_paths(installer)
243  ReactNativePodsUtils.update_search_paths(installer)
244  ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path)
245  ReactNativePodsUtils.apply_flags_for_fabric(installer, fabric_enabled: fabric_enabled)
246  ReactNativePodsUtils.apply_xcode_15_patch(installer)
247
248  NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer)
249  is_new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == "1"
250  NewArchitectureHelper.modify_flags_for_new_architecture(installer, is_new_arch_enabled)
251
252  Pod::UI.puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run".green
253end
254
255# === LEGACY METHOD ===
256# We need to keep this while we continue to support the old architecture.
257# =====================
258def use_react_native_codegen_ABI49_0_0!(spec, options={})
259  return if ENV['RCT_NEW_ARCH_ENABLED'] == "1"
260  # TODO: Once the new codegen approach is ready for use, we should output a warning here to let folks know to migrate.
261
262  # The prefix to react-native
263  react_native_path = options[:react_native_path] ||= ".."
264
265  # Library name (e.g. FBReactNativeSpec)
266  library_name = options[:library_name] ||= "#{spec.name.gsub('_','-').split('-').collect(&:capitalize).join}Spec"
267  Pod::UI.puts "[Codegen] Found #{library_name}"
268
269  relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
270  output_dir = options[:output_dir] ||= $ABI49_0_0CODEGEN_OUTPUT_DIR
271  output_dir_module = "#{output_dir}/#{$CODEGEN_MODULE_DIR}"
272  output_dir_component = "#{output_dir}/#{$CODEGEN_COMPONENT_DIR}"
273
274  codegen_config = {
275    "modules" => {
276      :js_srcs_pattern => "Native*.js",
277      :generated_dir => "#{relative_installation_root}/#{output_dir_module}/#{library_name}",
278      :generated_files => [
279        "#{library_name}.h",
280        "#{library_name}-generated.mm"
281      ]
282    },
283    "components" => {
284      :js_srcs_pattern => "*NativeComponent.js",
285      :generated_dir => "#{relative_installation_root}/#{output_dir_component}/#{library_name}",
286      :generated_files => [
287        "ComponentDescriptors.h",
288        "EventEmitters.cpp",
289        "EventEmitters.h",
290        "Props.cpp",
291        "Props.h",
292        "States.cpp",
293        "States.h",
294        "RCTComponentViewHelpers.h",
295        "ShadowNodes.cpp",
296        "ShadowNodes.h"
297      ]
298    }
299  }
300
301  # The path to JavaScript files
302  js_srcs_dir = options[:js_srcs_dir] ||= "./"
303  library_type = options[:library_type]
304
305  if library_type
306    if !codegen_config[library_type]
307      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"
308    end
309    js_srcs_pattern = codegen_config[library_type][:js_srcs_pattern]
310  end
311
312  if library_type
313    generated_dirs = [ codegen_config[library_type][:generated_dir] ]
314    generated_files = codegen_config[library_type][:generated_files].map { |filename| "#{codegen_config[library_type][:generated_dir]}/#{filename}" }
315  else
316    generated_dirs = [ codegen_config["modules"][:generated_dir], codegen_config["components"][:generated_dir] ]
317    generated_files = codegen_config["modules"][:generated_files].map { |filename| "#{codegen_config["modules"][:generated_dir]}/#{filename}" }
318    generated_files = generated_files.concat(codegen_config["components"][:generated_files].map { |filename| "#{codegen_config["components"][:generated_dir]}/#{filename}" })
319  end
320
321  if js_srcs_pattern
322    file_list = `find #{js_srcs_dir} -type f -name #{js_srcs_pattern}`.split("\n").sort
323    input_files = file_list.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}" }
324  else
325    input_files = [ js_srcs_dir ]
326  end
327
328  # Prepare filesystem by creating empty files that will be picked up as references by CocoaPods.
329  prepare_command = "mkdir -p #{generated_dirs.join(" ")} && touch -a #{generated_files.join(" ")}"
330  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
331  spec.prepare_command = prepare_command
332
333  env_files = ["$PODS_ROOT/../.xcode.env.local", "$PODS_ROOT/../.xcode.env"]
334
335  spec.script_phase = {
336    :name => 'Generate Specs',
337    :input_files => input_files + env_files, # This also needs to be relative to Xcode
338    :output_files => ["${DERIVED_FILE_DIR}/codegen-#{library_name}.log"].concat(generated_files.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}"} ),
339    # The final generated files will be created when this script is invoked at Xcode build time.
340    :script => get_script_phases_no_codegen_discovery_ABI49_0_0(
341      react_native_path: react_native_path,
342      codegen_output_dir: output_dir,
343      codegen_module_dir: output_dir_module,
344      codegen_component_dir: output_dir_component,
345      library_name: library_name,
346      library_type: library_type,
347      js_srcs_pattern: js_srcs_pattern,
348      js_srcs_dir: js_srcs_dir,
349      file_list: file_list
350    ),
351    :execution_position => :before_compile,
352    :show_env_vars_in_log => true
353  }
354end
355
356# This provides a post_install workaround for build issues related Xcode 12.5 and Apple Silicon (M1) machines.
357# Call this in the app's main Podfile's post_install hook.
358# See https://github.com/facebook/react-native/issues/31480#issuecomment-902912841 for more context.
359# Actual fix was authored by https://github.com/mikehardy.
360# New app template will call this for now until the underlying issue is resolved.
361def __apply_Xcode_12_5_M1_post_install_workaround(installer)
362  # Flipper podspecs are still targeting an older iOS deployment target, and may cause an error like:
363  #   "error: thread-local storage is not supported for the current target"
364  # The most reliable known workaround is to bump iOS deployment target to match react-native (iOS 11 now).
365  installer.pods_project.targets.each do |target|
366    target.build_configurations.each do |config|
367      # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 11.0
368      deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
369      should_upgrade = deployment_target < 11.0 && deployment_target != 0.0
370      if should_upgrade
371        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
372      end
373    end
374  end
375
376  # But... doing so caused another issue in Flipper:
377  #   "Time.h:52:17: error: typedef redefinition with different types"
378  # We need to make a patch to RCT-Folly - remove the `__IPHONE_OS_VERSION_MIN_REQUIRED` check.
379  # See https://github.com/facebook/flipper/issues/834 for more details.
380  time_header = "#{Pod::Config.instance.installation_root.to_s}/Pods/RCT-Folly/folly/portability/Time.h"
381  `sed -i -e  $'s/ && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)//' '#{time_header}'`
382end
383