From 8341154e3f9b624988852fd789b11aa364562d14 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 11 Jan 2024 05:16:18 -0800 Subject: [PATCH] Fix symbol not found _jump_fcontext with use_frameworks! (#42230) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42230 While developing Xcode 15, Apple reimplemented the linker. In Xcode 15.0, the linker was making old iOS (< 15) crash when they were built using Xcode 15. To fix that, we make Apple create new compiler flags (`-ld_classic`) to have a backward compatible linker. In Xcode 15.1, Apple fixed that behavior, so the flags should not be required anymore. But now, if we pass `-ld_classic` to the linker and we have an app that is using `use_framworks!`, that app crashes at startup. This change remove the flags if the Xcode that is used is 15.1 or greater. *Note:* The previous change added the flags to Hermes as well. I tested this fix in a configuration where Hermes has the flags and React Native does not, and it works. So we are removing the flags only from React Native. This Fixes https://github.com/facebook/react-native/issues/39945 ## Changelog: [Internal] - Do not add the `-ld_classic` flag if the app is built with Xcode 15.1 or greater. Reviewed By: cortinico Differential Revision: D52658197 fbshipit-source-id: 37d6bc895921c0fc3661f301870477191e7e42b3 --- .../scripts/cocoapods/__tests__/utils-test.rb | 57 +++++++++++++++++-- .../react-native/scripts/cocoapods/utils.rb | 7 ++- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb index cc6963c73a4fc0..206496899513c9 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb @@ -484,6 +484,52 @@ def test_applyXcode15Patch_whenXcodebuild14_correctlyAppliesNecessaryPatch assert_equal(2, XcodebuildMock.version_invocation_count) end + def test_applyXcode15Patch_whenXcodebuild15_1_does_not_apply_patch + # Arrange + XcodebuildMock.set_version = "Xcode 15.1" + first_target = prepare_target("FirstTarget") + second_target = prepare_target("SecondTarget") + third_target = TargetMock.new("ThirdTarget", [ + BuildConfigurationMock.new("Debug", { + "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) "SomeFlag=1" ' + }), + BuildConfigurationMock.new("Release", { + "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) "SomeFlag=1" ' + }), + ], nil) + + user_project_mock = UserProjectMock.new("/a/path", [ + prepare_config("Debug"), + prepare_config("Release"), + ], + :native_targets => [ + first_target, + second_target + ] + ) + pods_projects_mock = PodsProjectMock.new([], {"hermes-engine" => {}}, :native_targets => [ + third_target + ]) + installer = InstallerMock.new(pods_projects_mock, [ + AggregatedProjectMock.new(user_project_mock) + ]) + + # Act + user_project_mock.build_configurations.each do |config| + assert_nil(config.build_settings["OTHER_LDFLAGS"]) + end + + ReactNativePodsUtils.apply_xcode_15_patch(installer, :xcodebuild_manager => XcodebuildMock) + + # Assert + user_project_mock.build_configurations.each do |config| + assert_equal("$(inherited) ", config.build_settings["OTHER_LDFLAGS"]) + end + + # User project and Pods project + assert_equal(2, XcodebuildMock.version_invocation_count) + end + def test_applyXcode15Patch_whenXcodebuild15_correctlyAppliesNecessaryPatch # Arrange XcodebuildMock.set_version = "Xcode 15.0" @@ -1018,13 +1064,13 @@ def test_add_flag_to_map_with_inheritance_whenUsedWithBuildConfigBuildSettings }) twiceProcessed_config = BuildConfigurationMock.new("TwiceProcessedConfig"); test_flag = " -DTEST_FLAG=1" - + # Act ReactNativePodsUtils.add_flag_to_map_with_inheritance(empty_config.build_settings, "OTHER_CPLUSPLUSFLAGS", test_flag) ReactNativePodsUtils.add_flag_to_map_with_inheritance(initialized_config.build_settings, "OTHER_CPLUSPLUSFLAGS", test_flag) ReactNativePodsUtils.add_flag_to_map_with_inheritance(twiceProcessed_config.build_settings, "OTHER_CPLUSPLUSFLAGS", test_flag) ReactNativePodsUtils.add_flag_to_map_with_inheritance(twiceProcessed_config.build_settings, "OTHER_CPLUSPLUSFLAGS", test_flag) - + # Assert assert_equal("$(inherited)" + test_flag, empty_config.build_settings["OTHER_CPLUSPLUSFLAGS"]) assert_equal("$(inherited) INIT_FLAG" + test_flag, initialized_config.build_settings["OTHER_CPLUSPLUSFLAGS"]) @@ -1051,7 +1097,7 @@ def test_add_flag_to_map_with_inheritance_whenUsedWithXCConfigAttributes assert_equal("$(inherited) INIT_FLAG" + test_flag, initialized_xcconfig.attributes["OTHER_CPLUSPLUSFLAGS"]) assert_equal("$(inherited)" + test_flag, twiceProcessed_xcconfig.attributes["OTHER_CPLUSPLUSFLAGS"]) end - + def test_add_ndebug_flag_to_pods_in_release # Arrange xcconfig = XCConfigMock.new("Config") @@ -1061,7 +1107,7 @@ def test_add_ndebug_flag_to_pods_in_release custom_debug_config2 = BuildConfigurationMock.new("Custom") custom_release_config1 = BuildConfigurationMock.new("CustomRelease") custom_release_config2 = BuildConfigurationMock.new("Production") - + installer = prepare_installer_for_cpp_flags( [ xcconfig ], { @@ -1072,7 +1118,7 @@ def test_add_ndebug_flag_to_pods_in_release ) # Act ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer) - + # Assert assert_equal(nil, default_debug_config.build_settings["OTHER_CPLUSPLUSFLAGS"]) assert_equal("$(inherited) -DNDEBUG", default_release_config.build_settings["OTHER_CPLUSPLUSFLAGS"]) @@ -1150,4 +1196,3 @@ def prepare_installer_for_cpp_flags(xcconfigs, build_configs) :pod_target_installation_results => pod_target_installation_results_map ) end - diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index d93183dbbe478d..2692733cb984d9 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -172,7 +172,7 @@ def self.apply_xcode_15_patch(installer, xcodebuild_manager: Xcodebuild) project.build_configurations.each do |config| # fix for weak linking self.safe_init(config, other_ld_flags_key) - if self.is_using_xcode15_or_greater(:xcodebuild_manager => xcodebuild_manager) + if self.is_using_xcode15_0(:xcodebuild_manager => xcodebuild_manager) self.add_value_to_setting_if_missing(config, other_ld_flags_key, xcode15_compatibility_flags) else self.remove_value_from_setting_if_present(config, other_ld_flags_key, xcode15_compatibility_flags) @@ -404,7 +404,7 @@ def self.remove_value_from_setting_if_present(config, setting_name, value) end end - def self.is_using_xcode15_or_greater(xcodebuild_manager: Xcodebuild) + def self.is_using_xcode15_0(xcodebuild_manager: Xcodebuild) xcodebuild_version = xcodebuild_manager.version # The output of xcodebuild -version is something like @@ -415,7 +415,8 @@ def self.is_using_xcode15_or_greater(xcodebuild_manager: Xcodebuild) regex = /(\d+)\.(\d+)(?:\.(\d+))?/ if match_data = xcodebuild_version.match(regex) major = match_data[1].to_i - return major >= 15 + minor = match_data[2].to_i + return major == 15 && minor == 0 end return false