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_relative "./helpers.rb" 7 8# Utilities class for React Native Cocoapods 9class ReactNativePodsUtils 10 def self.warn_if_not_on_arm64 11 if SysctlChecker.new().call_sysctl_arm64() == 1 && !Environment.new().ruby_platform().include?('arm64') 12 Pod::UI.warn 'Do not use "pod install" from inside Rosetta2 (x86_64 emulation on arm64).' 13 Pod::UI.warn ' - Emulated x86_64 is slower than native arm64' 14 Pod::UI.warn ' - May result in mixed architectures in rubygems (eg: ffi_c.bundle files may be x86_64 with an arm64 interpreter)' 15 Pod::UI.warn 'Run "env /usr/bin/arch -arm64 /bin/bash --login" then try again.' 16 end 17 end 18 19 def self.get_default_flags 20 flags = { 21 :fabric_enabled => false, 22 :hermes_enabled => true, 23 :flipper_configuration => FlipperConfiguration.disabled 24 } 25 26 if ENV['RCT_NEW_ARCH_ENABLED'] == '1' 27 flags[:fabric_enabled] = true 28 flags[:hermes_enabled] = true 29 end 30 31 if ENV['USE_HERMES'] == '0' 32 flags[:hermes_enabled] = false 33 end 34 35 return flags 36 end 37 38 def self.has_pod(installer, name) 39 installer.pods_project.pod_group(name) != nil 40 end 41 42 def self.turn_off_resource_bundle_react_core(installer) 43 # this is needed for Xcode 14, see more details here https://github.com/facebook/react-native/issues/34673 44 # we should be able to remove this once CocoaPods catches up to it, see more details here https://github.com/CocoaPods/CocoaPods/issues/11402 45 installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result| 46 if pod_name.to_s == 'React-Core' 47 target_installation_result.resource_bundle_targets.each do |resource_bundle_target| 48 resource_bundle_target.build_configurations.each do |config| 49 config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' 50 end 51 end 52 end 53 end 54 end 55 56 def self.exclude_i386_architecture_while_using_hermes(installer) 57 projects = self.extract_projects(installer) 58 59 # Hermes does not support `i386` architecture 60 excluded_archs_default = self.has_pod(installer, 'hermes-engine') ? "i386" : "" 61 62 projects.each do |project| 63 project.build_configurations.each do |config| 64 config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = excluded_archs_default 65 end 66 67 project.save() 68 end 69 end 70 71 def self.set_node_modules_user_settings(installer, react_native_path) 72 Pod::UI.puts("Setting REACT_NATIVE build settings") 73 projects = self.extract_projects(installer) 74 75 projects.each do |project| 76 project.build_configurations.each do |config| 77 config.build_settings["REACT_NATIVE_PATH"] = File.join("${PODS_ROOT}", "..", react_native_path) 78 end 79 80 project.save() 81 end 82 end 83 84 def self.fix_library_search_paths(installer) 85 projects = self.extract_projects(installer) 86 87 projects.each do |project| 88 project.build_configurations.each do |config| 89 self.fix_library_search_path(config) 90 end 91 project.native_targets.each do |target| 92 target.build_configurations.each do |config| 93 self.fix_library_search_path(config) 94 end 95 end 96 project.save() 97 end 98 end 99 100 def self.apply_mac_catalyst_patches(installer) 101 # Fix bundle signing issues 102 installer.pods_project.targets.each do |target| 103 if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle" 104 target.build_configurations.each do |config| 105 config.build_settings['CODE_SIGN_IDENTITY[sdk=macosx*]'] = '-' 106 end 107 end 108 end 109 110 installer.aggregate_targets.each do |aggregate_target| 111 aggregate_target.user_project.native_targets.each do |target| 112 target.build_configurations.each do |config| 113 # Explicitly set dead code stripping flags 114 config.build_settings['DEAD_CODE_STRIPPING'] = 'YES' 115 config.build_settings['PRESERVE_DEAD_CODE_INITS_AND_TERMS'] = 'YES' 116 # Modify library search paths 117 config.build_settings['LIBRARY_SEARCH_PATHS'] = ['$(SDKROOT)/usr/lib/swift', '$(SDKROOT)/System/iOSSupport/usr/lib/swift', '$(inherited)'] 118 end 119 end 120 aggregate_target.user_project.save() 121 end 122 end 123 124 def self.apply_xcode_15_patch(installer) 125 installer.target_installation_results.pod_target_installation_results 126 .each do |pod_name, target_installation_result| 127 target_installation_result.native_target.build_configurations.each do |config| 128 # unary_function and binary_function are no longer provided in C++17 and newer standard modes as part of Xcode 15. They can be re-enabled with setting _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION 129 # Ref: https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Deprecations 130 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= '$(inherited) ' 131 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << '"_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION" ' 132 end 133 end 134 end 135 136 def self.apply_flags_for_fabric(installer, fabric_enabled: false) 137 fabric_flag = "-DRN_FABRIC_ENABLED" 138 if fabric_enabled 139 self.add_compiler_flag_to_project(installer, fabric_flag) 140 else 141 self.remove_compiler_flag_from_project(installer, fabric_flag) 142 end 143 end 144 145 private 146 147 def self.fix_library_search_path(config) 148 lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"] 149 150 if lib_search_paths == nil 151 # No search paths defined, return immediately 152 return 153 end 154 155 if lib_search_paths.include?("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") || lib_search_paths.include?("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"") 156 # $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple M1) 157 # since the libraries there are only built for x86_64 and i386. 158 lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") 159 lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"") 160 end 161 162 if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\"")) 163 # however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11 164 lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift") 165 end 166 end 167 168 def self.create_xcode_env_if_missing(file_manager: File) 169 relative_path = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd) 170 file_path = file_manager.join(relative_path, '.xcode.env') 171 if file_manager.exist?(file_path) 172 return 173 end 174 175 system("echo 'export NODE_BINARY=$(command -v node)' > #{file_path}") 176 end 177 178 # It examines the target_definition property and sets the appropriate value for 179 # ENV['USE_FRAMEWORKS'] variable. 180 # 181 # - parameter target_definition: The current target definition 182 def self.detect_use_frameworks(target_definition) 183 if ENV['USE_FRAMEWORKS'] != nil 184 return 185 end 186 187 framework_build_type = target_definition.build_type.to_s 188 189 Pod::UI.puts("Framework build type is #{framework_build_type}") 190 191 if framework_build_type === "static framework" 192 ENV['USE_FRAMEWORKS'] = 'static' 193 elsif framework_build_type === "dynamic framework" 194 ENV['USE_FRAMEWORKS'] = 'dynamic' 195 else 196 ENV['USE_FRAMEWORKS'] = nil 197 end 198 end 199 200 def self.update_search_paths(installer) 201 return if ENV['USE_FRAMEWORKS'] == nil 202 203 projects = self.extract_projects(installer) 204 205 projects.each do |project| 206 project.build_configurations.each do |config| 207 208 header_search_paths = config.build_settings["HEADER_SEARCH_PATHS"] ||= "$(inherited)" 209 210 header_search_paths = self.add_search_path_if_not_included(header_search_paths, "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers") 211 header_search_paths = self.add_search_path_if_not_included(header_search_paths, "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core") 212 header_search_paths = self.add_search_path_if_not_included(header_search_paths, "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios") 213 header_search_paths = self.add_search_path_if_not_included(header_search_paths, "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers") 214 header_search_paths = self.add_search_path_if_not_included(header_search_paths, "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios") 215 216 config.build_settings["HEADER_SEARCH_PATHS"] = header_search_paths 217 end 218 219 project.save() 220 end 221 222 installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result| 223 if self.react_native_pods.include?(pod_name) || pod_name.include?("Pod") || pod_name.include?("Tests") 224 next 225 end 226 227 self.set_rctfolly_search_paths(target_installation_result) 228 self.set_codegen_search_paths(target_installation_result) 229 self.set_reactcommon_searchpaths(target_installation_result) 230 self.set_rctfabric_search_paths(target_installation_result) 231 self.set_imagemanager_search_path(target_installation_result) 232 end 233 end 234 235 # ========= # 236 # Utilities # 237 # ========= # 238 239 def self.extract_projects(installer) 240 return installer.aggregate_targets 241 .map{ |t| t.user_project } 242 .uniq{ |p| p.path } 243 .push(installer.pods_project) 244 end 245 246 def self.add_compiler_flag_to_project(installer, flag, configuration: nil) 247 projects = self.extract_projects(installer) 248 249 projects.each do |project| 250 project.build_configurations.each do |config| 251 self.set_flag_in_config(config, flag, configuration: configuration) 252 end 253 project.save() 254 end 255 end 256 257 def self.remove_compiler_flag_from_project(installer, flag, configuration: nil) 258 projects = self.extract_projects(installer) 259 260 projects.each do |project| 261 project.build_configurations.each do |config| 262 self.remove_flag_in_config(config, flag, configuration: configuration) 263 end 264 project.save() 265 end 266 end 267 268 def self.add_compiler_flag_to_pods(installer, flag, configuration: nil) 269 installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result| 270 target_installation_result.native_target.build_configurations.each do |config| 271 self.set_flag_in_config(config, flag, configuration: configuration) 272 end 273 end 274 end 275 276 def self.set_flag_in_config(config, flag, configuration: nil) 277 if configuration == nil || config.name == configuration 278 self.add_flag_for_key(config, flag, "OTHER_CFLAGS") 279 self.add_flag_for_key(config, flag, "OTHER_CPLUSPLUSFLAGS") 280 end 281 end 282 283 def self.remove_flag_in_config(config, flag, configuration: nil) 284 if configuration == nil || config.name == configuration 285 self.remove_flag_for_key(config, flag, "OTHER_CFLAGS") 286 self.remove_flag_for_key(config, flag, "OTHER_CPLUSPLUSFLAGS") 287 end 288 end 289 290 291 def self.add_flag_for_key(config, flag, key) 292 current_setting = config.build_settings[key] ? config.build_settings[key] : "$(inherited)" 293 294 if current_setting.kind_of?(Array) 295 current_setting = current_setting 296 .map { |s| s.gsub('"', '') } 297 .map { |s| s.gsub('\"', '') } 298 .join(" ") 299 end 300 301 if !current_setting.include?(flag) 302 current_setting = "#{current_setting} #{flag}" 303 end 304 305 config.build_settings[key] = current_setting 306 end 307 308 def self.remove_flag_for_key(config, flag, key) 309 current_setting = config.build_settings[key] ? config.build_settings[key] : "$(inherited)" 310 311 if current_setting.kind_of?(Array) 312 current_setting = current_setting 313 .map { |s| s.gsub('"', '') } 314 .map { |s| s.gsub('\"', '') } 315 .join(" ") 316 end 317 318 if current_setting.include?(flag) 319 current_setting.slice! flag 320 end 321 322 config.build_settings[key] = current_setting 323 end 324 325 def self.add_search_path_if_not_included(current_search_paths, new_search_path) 326 if !current_search_paths.include?(new_search_path) 327 current_search_paths << " #{new_search_path}" 328 end 329 return current_search_paths 330 end 331 332 def self.update_header_paths_if_depends_on(target_installation_result, dependency_name, header_paths) 333 depends_on_framework = target_installation_result.native_target.dependencies.any? { |d| d.name == dependency_name } 334 if depends_on_framework 335 target_installation_result.native_target.build_configurations.each do |config| 336 header_search_path = config.build_settings["HEADER_SEARCH_PATHS"] != nil ? config.build_settings["HEADER_SEARCH_PATHS"] : "$(inherited)" 337 header_paths.each { |header| header_search_path = ReactNativePodsUtils.add_search_path_if_not_included(header_search_path, header) } 338 config.build_settings["HEADER_SEARCH_PATHS"] = header_search_path 339 end 340 end 341 end 342 343 def self.set_rctfolly_search_paths(target_installation_result) 344 ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "RCT-Folly", [ 345 "\"$(PODS_ROOT)/RCT-Folly\"", 346 "\"$(PODS_ROOT)/DoubleConversion\"", 347 "\"$(PODS_ROOT)/boost\"" 348 ]) 349 end 350 351 def self.set_codegen_search_paths(target_installation_result) 352 ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "ABI49_0_0React-Codegen", [ 353 "\"${PODS_CONFIGURATION_BUILD_DIR}/ABI49_0_0React-Codegen/React_Codegen.framework/Headers\"", 354 ]) 355 end 356 357 def self.set_reactcommon_searchpaths(target_installation_result) 358 ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "ReactCommon", [ 359 "\"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers\"", 360 "\"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core\"", 361 ]) 362 363 end 364 365 def self.set_rctfabric_search_paths(target_installation_result) 366 ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "React-RCTFabric", [ 367 "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers\"", 368 "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\"", 369 "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Graphics/React_graphics.framework/Headers\"", 370 "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios\"", 371 ]) 372 end 373 374 def self.set_imagemanager_search_path(target_installation_result) 375 ReactNativePodsUtils.update_header_paths_if_depends_on(target_installation_result, "React-ImageManager", [ 376 "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/imagemanager/platform/ios\"" 377 ]) 378 end 379 380 def self.react_native_pods 381 return [ 382 "DoubleConversion", 383 "FBLazyVector", 384 "RCT-Folly", 385 "RCTRequired", 386 "RCTTypeSafety", 387 "React", 388 "ABI49_0_0React-Codegen", 389 "React-Core", 390 "React-CoreModules", 391 "React-Fabric", 392 "React-ImageManager", 393 "React-RCTActionSheet", 394 "React-RCTAnimation", 395 "React-RCTAppDelegate", 396 "React-RCTBlob", 397 "React-RCTFabric", 398 "React-RCTImage", 399 "React-RCTLinking", 400 "React-RCTNetwork", 401 "React-RCTPushNotification", 402 "React-RCTSettings", 403 "React-RCTText", 404 "React-RCTTest", 405 "React-RCTVibration", 406 "React-callinvoker", 407 "React-cxxreact", 408 "React-graphics", 409 "React-jsc", 410 "React-jsi", 411 "React-jsiexecutor", 412 "React-jsinspector", 413 "React-logger", 414 "React-perflogger", 415 "React-rncore", 416 "React-runtimeexecutor", 417 "ReactCommon", 418 "Yoga", 419 "boost", 420 "fmt", 421 "glog", 422 "hermes-engine", 423 "libevent", 424 "React-hermes", 425 ] 426 end 427end 428