# 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 'open3' require 'pathname' require_relative './react_native_pods_utils/script_phases.rb' require_relative './cocoapods/jsengine.rb' require_relative './cocoapods/flipper.rb' require_relative './cocoapods/fabric.rb' require_relative './cocoapods/codegen.rb' require_relative './cocoapods/codegen_utils.rb' require_relative './cocoapods/utils.rb' require_relative './cocoapods/new_architecture.rb' require_relative './cocoapods/local_podspec_patch.rb' $CODEGEN_OUTPUT_DIR = 'build/generated/ios' $CODEGEN_COMPONENT_DIR = 'react/renderer/components' $CODEGEN_MODULE_DIR = '.' $FOLLY_VERSION = '2021.07.22.00' $START_TIME = Time.now.to_i # This function returns the min iOS version supported by React Native # By using this function, you won't have to manualy change your Podfile # when we change the minimum version supported by the framework. def min_ios_version_supported return '12.4' end # This function prepares the project for React Native, before processing # all the target exposed by the framework. def prepare_react_native_project! # Temporary solution to suppress duplicated GUID error. # Can be removed once we move to generate files outside pod install. install! 'cocoapods', :deterministic_uuids => false ReactNativePodsUtils.create_xcode_env_if_missing end # Function that setup all the react native dependencies #  # Parameters # - path: path to react_native installation. # - fabric_enabled: whether fabric should be enabled or not. # - new_arch_enabled: whether the new architecture should be enabled or not. # - production: whether the dependencies must be installed to target a Debug or a Release build. # - hermes_enabled: whether Hermes should be enabled or not. # - flipper_configuration: The configuration to use for flipper. # - app_path: path to the React Native app. Required by the New Architecture. # - config_file_dir: directory of the `package.json` file, required by the New Architecture. def use_react_native! ( path: "../node_modules/react-native", fabric_enabled: false, new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == '1', production: ENV['PRODUCTION'] == '1', hermes_enabled: ENV['USE_HERMES'] && ENV['USE_HERMES'] == '0' ? false : true, flipper_configuration: FlipperConfiguration.disabled, app_path: '..', config_file_dir: '') CodegenUtils.clean_up_build_folder(app_path, $CODEGEN_OUTPUT_DIR) # We are relying on this flag also in third parties libraries to proper install dependencies. # Better to rely and enable this environment flag if the new architecture is turned on using flags. ENV['RCT_NEW_ARCH_ENABLED'] = new_arch_enabled ? "1" : "0" fabric_enabled = fabric_enabled || new_arch_enabled ENV['USE_HERMES'] = hermes_enabled ? "1" : "0" prefix = path ReactNativePodsUtils.warn_if_not_on_arm64() # The Pods which should be included in all projects pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector" pod 'FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec" pod 'RCTRequired', :path => "#{prefix}/Libraries/RCTRequired" pod 'RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :modular_headers => true pod 'React', :path => "#{prefix}/" pod 'React-Core', :path => "#{prefix}/" pod 'React-CoreModules', :path => "#{prefix}/React/CoreModules" pod 'React-RCTAppDelegate', :path => "#{prefix}/Libraries/AppDelegate" pod 'React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS" pod 'React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation" pod 'React-RCTBlob', :path => "#{prefix}/Libraries/Blob" pod 'React-RCTImage', :path => "#{prefix}/Libraries/Image" pod 'React-RCTLinking', :path => "#{prefix}/Libraries/LinkingIOS" pod 'React-RCTNetwork', :path => "#{prefix}/Libraries/Network" pod 'React-RCTSettings', :path => "#{prefix}/Libraries/Settings" pod 'React-RCTText', :path => "#{prefix}/Libraries/Text" pod 'React-RCTVibration', :path => "#{prefix}/Libraries/Vibration" pod 'React-Core/RCTWebSocket', :path => "#{prefix}/" pod 'React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact" if hermes_enabled setup_hermes!(:react_native_path => prefix, :fabric_enabled => fabric_enabled) else setup_jsc!(:react_native_path => prefix, :fabric_enabled => fabric_enabled) end pod 'React-jsiexecutor', :path => "#{prefix}/ReactCommon/jsiexecutor" pod 'React-jsinspector', :path => "#{prefix}/ReactCommon/jsinspector" pod 'React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker" pod 'React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor" pod 'React-perflogger', :path => "#{prefix}/ReactCommon/reactperflogger" pod 'React-logger', :path => "#{prefix}/ReactCommon/logger" pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :modular_headers => true pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true pod 'DoubleConversion', :podspec => "#{prefix}/third-party-podspecs/DoubleConversion.podspec" pod 'glog', :podspec => "#{prefix}/third-party-podspecs/glog.podspec" pod 'boost', :podspec => "#{prefix}/third-party-podspecs/boost.podspec" pod 'RCT-Folly', :podspec => "#{prefix}/third-party-podspecs/RCT-Folly.podspec", :modular_headers => true run_codegen!( app_path, config_file_dir, :new_arch_enabled => new_arch_enabled, :disable_codegen => ENV['DISABLE_CODEGEN'] == '1', :react_native_path => prefix, :fabric_enabled => fabric_enabled, :hermes_enabled => hermes_enabled, :codegen_output_dir => $CODEGEN_OUTPUT_DIR, :package_json_file => File.join(__dir__, "..", "package.json"), :folly_version => $FOLLY_VERSION ) pod 'React-Codegen', :path => $CODEGEN_OUTPUT_DIR, :modular_headers => true if fabric_enabled checkAndGenerateEmptyThirdPartyProvider!(prefix, new_arch_enabled, $CODEGEN_OUTPUT_DIR) setup_fabric!(:react_native_path => prefix) end # CocoaPods `configurations` option ensures that the target is copied only for the specified configurations, # but those dependencies are still built. # Flipper doesn't currently compile for release https://github.com/facebook/react-native/issues/33764 # 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. if flipper_configuration.flipper_enabled && !production install_flipper_dependencies(prefix) use_flipper_pods(flipper_configuration.versions, :configurations => flipper_configuration.configurations) end pods_to_update = LocalPodspecPatch.pods_to_update(:react_native_path => prefix) if !pods_to_update.empty? if Pod::Lockfile.public_instance_methods.include?(:detect_changes_with_podfile) Pod::Lockfile.prepend(LocalPodspecPatch) else 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." end end end # Getter to retrieve the folly flags in case contributors need to apply them manually. # # Returns: the folly compiler flags def folly_flags() return NewArchitectureHelper.folly_compiler_flags end # This function can be used by library developer to prepare their modules for the New Architecture. # It passes the Folly Flags to the module, it configures the search path and installs some New Architecture specific dependencies. # # Parameters: # - spec: The spec that has to be configured with the New Architecture code # - new_arch_enabled: Whether the module should install dependencies for the new architecture def install_modules_dependencies(spec, new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == "1") NewArchitectureHelper.install_modules_dependencies(spec, new_arch_enabled, $FOLLY_VERSION) end # It returns the default flags. def get_default_flags() return ReactNativePodsUtils.get_default_flags() end # It installs the flipper dependencies into the project. # # Parameters # - versions: a dictionary of Flipper Library -> Versions that can be used to customize which version of Flipper to install. # - configurations: an array of configuration where to install the dependencies. def use_flipper!(versions = {}, configurations: ['Debug']) Pod::UI.warn "use_flipper is deprecated, use the flipper_configuration option in the use_react_native function" use_flipper_pods(versions, :configurations => configurations) end # Function that executes after React Native has been installed to configure some flags and build settings. # # Parameters # - installer: the Cocoapod object that allows to customize the project. # - react_native_path: path to React Native. # - mac_catalyst_enabled: whether we are running the Pod on a Mac Catalyst project or not. def react_native_post_install(installer, react_native_path = "../node_modules/react-native", mac_catalyst_enabled: false) ReactNativePodsUtils.turn_off_resource_bundle_react_core(installer) ReactNativePodsUtils.apply_mac_catalyst_patches(installer) if mac_catalyst_enabled if ReactNativePodsUtils.has_pod(installer, 'Flipper') flipper_post_install(installer) end package = JSON.parse(File.read(File.join(react_native_path, "package.json"))) version = package['version'] ReactNativePodsUtils.exclude_i386_architecture_while_using_hermes(installer) ReactNativePodsUtils.fix_library_search_paths(installer) ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path) NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer) is_new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == "1" NewArchitectureHelper.modify_flags_for_new_architecture(installer, is_new_arch_enabled) Pod::UI.puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run".green end # === LEGACY METHOD === # We need to keep this while we continue to support the old architecture. # ===================== def use_react_native_codegen!(spec, options={}) return if ENV['RCT_NEW_ARCH_ENABLED'] == "1" # TODO: Once the new codegen approach is ready for use, we should output a warning here to let folks know to migrate. # The prefix to react-native react_native_path = options[:react_native_path] ||= "../.." # Library name (e.g. FBReactNativeSpec) library_name = options[:library_name] ||= "#{spec.name.gsub('_','-').split('-').collect(&:capitalize).join}Spec" Pod::UI.puts "[Codegen] Found #{library_name}" relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd) output_dir = options[:output_dir] ||= $CODEGEN_OUTPUT_DIR output_dir_module = "#{output_dir}/#{$CODEGEN_MODULE_DIR}" output_dir_component = "#{output_dir}/#{$CODEGEN_COMPONENT_DIR}" codegen_config = { "modules" => { :js_srcs_pattern => "Native*.js", :generated_dir => "#{relative_installation_root}/#{output_dir_module}/#{library_name}", :generated_files => [ "#{library_name}.h", "#{library_name}-generated.mm" ] }, "components" => { :js_srcs_pattern => "*NativeComponent.js", :generated_dir => "#{relative_installation_root}/#{output_dir_component}/#{library_name}", :generated_files => [ "ComponentDescriptors.h", "EventEmitters.cpp", "EventEmitters.h", "Props.cpp", "Props.h", "States.cpp", "States.h", "RCTComponentViewHelpers.h", "ShadowNodes.cpp", "ShadowNodes.h" ] } } # The path to JavaScript files js_srcs_dir = options[:js_srcs_dir] ||= "./" library_type = options[:library_type] if library_type if !codegen_config[library_type] 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" end js_srcs_pattern = codegen_config[library_type][:js_srcs_pattern] end if library_type generated_dirs = [ codegen_config[library_type][:generated_dir] ] generated_files = codegen_config[library_type][:generated_files].map { |filename| "#{codegen_config[library_type][:generated_dir]}/#{filename}" } else generated_dirs = [ codegen_config["modules"][:generated_dir], codegen_config["components"][:generated_dir] ] generated_files = codegen_config["modules"][:generated_files].map { |filename| "#{codegen_config["modules"][:generated_dir]}/#{filename}" } generated_files = generated_files.concat(codegen_config["components"][:generated_files].map { |filename| "#{codegen_config["components"][:generated_dir]}/#{filename}" }) end if js_srcs_pattern file_list = `find #{js_srcs_dir} -type f -name #{js_srcs_pattern}`.split("\n").sort input_files = file_list.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}" } else input_files = [ js_srcs_dir ] end # Prepare filesystem by creating empty files that will be picked up as references by CocoaPods. prepare_command = "mkdir -p #{generated_dirs.join(" ")} && touch -a #{generated_files.join(" ")}" 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 spec.prepare_command = prepare_command env_files = ["$PODS_ROOT/../.xcode.env.local", "$PODS_ROOT/../.xcode.env"] spec.script_phase = { :name => 'Generate Specs', :input_files => input_files + env_files, # This also needs to be relative to Xcode :output_files => ["${DERIVED_FILE_DIR}/codegen-#{library_name}.log"].concat(generated_files.map { |filename| "${PODS_TARGET_SRCROOT}/#{filename}"} ), # The final generated files will be created when this script is invoked at Xcode build time. :script => get_script_phases_no_codegen_discovery( react_native_path: react_native_path, codegen_output_dir: $CODEGEN_OUTPUT_DIR, codegen_module_dir: $CODEGEN_MODULE_DIR, codegen_component_dir: $CODEGEN_COMPONENT_DIR, library_name: library_name, library_type: library_type, js_srcs_pattern: js_srcs_pattern, js_srcs_dir: js_srcs_dir, file_list: file_list ), :execution_position => :before_compile, :show_env_vars_in_log => true } end # This provides a post_install workaround for build issues related Xcode 12.5 and Apple Silicon (M1) machines. # Call this in the app's main Podfile's post_install hook. # See https://github.com/facebook/react-native/issues/31480#issuecomment-902912841 for more context. # Actual fix was authored by https://github.com/mikehardy. # New app template will call this for now until the underlying issue is resolved. def __apply_Xcode_12_5_M1_post_install_workaround(installer) # Flipper podspecs are still targeting an older iOS deployment target, and may cause an error like: # "error: thread-local storage is not supported for the current target" # The most reliable known workaround is to bump iOS deployment target to match react-native (iOS 11 now). installer.pods_project.targets.each do |target| target.build_configurations.each do |config| # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 11.0 deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f should_upgrade = deployment_target < 11.0 && deployment_target != 0.0 if should_upgrade config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' end end end # But... doing so caused another issue in Flipper: # "Time.h:52:17: error: typedef redefinition with different types" # We need to make a patch to RCT-Folly - remove the `__IPHONE_OS_VERSION_MIN_REQUIRED` check. # See https://github.com/facebook/flipper/issues/834 for more details. time_header = "#{Pod::Config.instance.installation_root.to_s}/Pods/RCT-Folly/folly/portability/Time.h" `sed -i -e $'s/ && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)//' '#{time_header}'` end