diff --git a/scripts/cocoapods/__tests__/test_utils/InstallerMock.rb b/scripts/cocoapods/__tests__/test_utils/InstallerMock.rb index 32a1594e22f80f..f7ad218714b362 100644 --- a/scripts/cocoapods/__tests__/test_utils/InstallerMock.rb +++ b/scripts/cocoapods/__tests__/test_utils/InstallerMock.rb @@ -54,15 +54,19 @@ def target_with_name(name) class PodsProjectMock attr_reader :targets + attr_reader :native_targets attr_reader :path attr_reader :build_configurations @pod_group + attr_reader :save_invocation_count - def initialize(targets = [], pod_group = {}, path = "test/path-pod.xcodeproj", build_configurations = []) + def initialize(targets = [], pod_group = {}, path = "test/path-pod.xcodeproj", build_configurations = [], native_targets: []) @targets = targets @pod_group = pod_group @path = path @build_configurations = build_configurations + @save_invocation_count = 0 + @native_targets = native_targets end def pod_group(name) @@ -70,6 +74,7 @@ def pod_group(name) end def save() + @save_invocation_count += 1 end end @@ -84,13 +89,20 @@ def initialize(user_project = UserProjectMock.new) class UserProjectMock attr_reader :path attr_reader :build_configurations + attr_reader :native_targets - def initialize(path = "/test/path.xcproj", build_configurations = []) + attr_reader :save_invocation_count + + + def initialize(path = "/test/path.xcproj", build_configurations = [], native_targets: []) @path = path @build_configurations = build_configurations + @native_targets = native_targets + @save_invocation_count = 0 end def save() + @save_invocation_count += 1 end end diff --git a/scripts/cocoapods/__tests__/utils-test.rb b/scripts/cocoapods/__tests__/utils-test.rb index 19262613bf0bd8..dedbd52eb14440 100644 --- a/scripts/cocoapods/__tests__/utils-test.rb +++ b/scripts/cocoapods/__tests__/utils-test.rb @@ -131,10 +131,8 @@ def test_hasPod_whenInstallerHasPod_returnTrue # ============================ # def test_excludeArchitectures_whenHermesEngineIsNotIncluded_excludeNothing # Arrange - user_project_mock = UserProjectMock.new("a/path", [ - BuildConfigurationMock.new("Debug"), - BuildConfigurationMock.new("Release"), - ]) + user_project_mock = prepare_empty_user_project_mock() + pods_projects_mock = PodsProjectMock.new() installer = InstallerMock.new(PodsProjectMock.new(), [ AggregatedProjectMock.new(user_project_mock) ]) @@ -146,16 +144,15 @@ def test_excludeArchitectures_whenHermesEngineIsNotIncluded_excludeNothing user_project_mock.build_configurations.each do |config| assert_equal(config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"], "") end - + assert_equal(user_project_mock.save_invocation_count, 1) + assert_equal(pods_projects_mock.save_invocation_count, 0) end def test_excludeArchitectures_whenHermesEngineIsIncluded_excludeI386 # Arrange - user_project_mock = UserProjectMock.new("a/path", [ - BuildConfigurationMock.new("Debug"), - BuildConfigurationMock.new("Release"), - ]) - installer = InstallerMock.new(PodsProjectMock.new([], {"hermes-engine" => {}}), [ + user_project_mock = prepare_empty_user_project_mock() + pods_projects_mock = PodsProjectMock.new([], {"hermes-engine" => {}}) + installer = InstallerMock.new(pods_projects_mock, [ AggregatedProjectMock.new(user_project_mock) ]) @@ -166,6 +163,169 @@ def test_excludeArchitectures_whenHermesEngineIsIncluded_excludeI386 user_project_mock.build_configurations.each do |config| assert_equal(config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"], "i386") end + + assert_equal(user_project_mock.save_invocation_count, 1) + assert_equal(pods_projects_mock.save_invocation_count, 1) + end + + # ================= # + # Test - Fix Config # + # ================= # + + def test_fixConfig_whenThereIsNoSearchPaths_doNothing + # Arrange + buildConfig = BuildConfigurationMock.new("Debug") + + # Act + ReactNativePodsUtils.fix_config(buildConfig) + + # Assert + assert_nil(buildConfig.build_settings["LIBRARY_SEARCH_PATHS"]) + end + + def test_fixConfig_whenThereAreSearchPathsAndSwiftUnescaped_removesSwift5_5 + # Arrange + buildConfig = BuildConfigurationMock.new("Debug", {"LIBRARY_SEARCH_PATHS" => [ + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "$(SDKROOT)/usr/lib/swift" + ]}) + + # Act + ReactNativePodsUtils.fix_config(buildConfig) + + # Assert + assert_equal(buildConfig.build_settings["LIBRARY_SEARCH_PATHS"], ["$(SDKROOT)/usr/lib/swift"]) + end + + def test_fixConfig_whenThereAreSearchPathsAndSwiftEscaped_removesSwift5_5 + # Arrange + buildConfig = BuildConfigurationMock.new("Debug", {"LIBRARY_SEARCH_PATHS" => [ + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "Another/path", + "\"$(SDKROOT)/usr/lib/swift\"" + ]}) + + # Act + ReactNativePodsUtils.fix_config(buildConfig) + + # Assert + assert_equal(buildConfig.build_settings["LIBRARY_SEARCH_PATHS"], ["Another/path", "\"$(SDKROOT)/usr/lib/swift\""]) end + def test_fixConfig_whenThereAreSearchPathsAndNoSwift_removesSwift5_5AndAddsSwiftAsFirst + # Arrange + buildConfig = BuildConfigurationMock.new("Debug", {"LIBRARY_SEARCH_PATHS" => [ + "Another/path" + ]}) + + # Act + ReactNativePodsUtils.fix_config(buildConfig) + + # Assert + assert_equal(buildConfig.build_settings["LIBRARY_SEARCH_PATHS"], ["$(SDKROOT)/usr/lib/swift", "Another/path"]) + end + + # ============================== # + # Test - Fix Library Search Path # + # ============================== # + + def test_fixLibrarySearchPaths_correctlySetsTheSearchPathsForAllProjects + firstTarget = prepare_target("FirstTarget") + secondTarget = prepare_target("SecondTarget") + thirdTarget = prepare_target("ThirdTarget") + user_project_mock = UserProjectMock.new("a/path", [ + prepare_config("Debug"), + prepare_config("Release"), + ], + :native_targets => [ + firstTarget, + secondTarget + ] + ) + pods_projects_mock = PodsProjectMock.new([], {"hermes-engine" => {}}, :native_targets => [ + thirdTarget + ]) + installer = InstallerMock.new(pods_projects_mock, [ + AggregatedProjectMock.new(user_project_mock) + ]) + + # Act + ReactNativePodsUtils.fix_library_search_paths(installer) + + # Assert + user_project_mock.build_configurations.each do |config| + assert_equal(config.build_settings["LIBRARY_SEARCH_PATHS"], [ + "$(SDKROOT)/usr/lib/swift", "Another/path" + ]) + end + + user_project_mock.native_targets.each do |target| + target.build_configurations.each do |config| + assert_equal(config.build_settings["LIBRARY_SEARCH_PATHS"], [ + "$(SDKROOT)/usr/lib/swift", "Another/path" + ]) + end + end + + pods_projects_mock.native_targets.each do |target| + target.build_configurations.each do |config| + assert_equal(config.build_settings["LIBRARY_SEARCH_PATHS"], [ + "$(SDKROOT)/usr/lib/swift", "Another/path" + ]) + end + end + + assert_equal(user_project_mock.save_invocation_count, 1) + assert_equal(pods_projects_mock.save_invocation_count, 1) + end + + # ==================================== # + # Test - Set Node_Modules User Setting # + # ==================================== # + + def test_setNodeModulesUserSettings_addTheUserSetting + # Arrange + react_native_path = "react_native/node_modules" + user_project_mock = prepare_empty_user_project_mock() + pods_projects_mock = PodsProjectMock.new([], {"hermes-engine" => {}}) + installer = InstallerMock.new(pods_projects_mock, [ + AggregatedProjectMock.new(user_project_mock) + ]) + + # Act + ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path) + + # Assert + user_project_mock.build_configurations.each do |config| + assert_equal(config.build_settings["REACT_NATIVE_PATH"], "${PODS_ROOT}/../#{react_native_path}") + end + + assert_equal(user_project_mock.save_invocation_count, 1) + assert_equal(pods_projects_mock.save_invocation_count, 1) + assert_equal(Pod::UI.collected_messages, ["Setting REACT_NATIVE build settings"]) + end +end + +def prepare_empty_user_project_mock + return UserProjectMock.new("a/path", [ + BuildConfigurationMock.new("Debug"), + BuildConfigurationMock.new("Release"), + ]) +end + +def prepare_config(config_name) + return BuildConfigurationMock.new(config_name, {"LIBRARY_SEARCH_PATHS" => [ + "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "Another/path", + ]}) +end + +def prepare_target(name) + return TargetMock.new(name, [ + prepare_config("Debug"), + prepare_config("Release") + ]) end diff --git a/scripts/cocoapods/utils.rb b/scripts/cocoapods/utils.rb index 5d7cc34a0867e2..80ab6ea0d1cbb9 100644 --- a/scripts/cocoapods/utils.rb +++ b/scripts/cocoapods/utils.rb @@ -53,4 +53,62 @@ def self.exclude_i386_architecture_while_using_hermes(installer) project.save() end end + + def self.fix_library_search_paths(installer) + projects = installer.aggregate_targets + .map{ |t| t.user_project } + .uniq{ |p| p.path } + .push(installer.pods_project) + + projects.each do |project| + project.build_configurations.each do |config| + ReactNativePodsUtils.fix_config(config) + end + project.native_targets.each do |target| + target.build_configurations.each do |config| + ReactNativePodsUtils.fix_config(config) + end + end + project.save() + end + end + + def self.set_node_modules_user_settings(installer, react_native_path) + Pod::UI.puts("Setting REACT_NATIVE build settings") + projects = installer.aggregate_targets + .map{ |t| t.user_project } + .uniq{ |p| p.path } + .push(installer.pods_project) + + projects.each do |project| + project.build_configurations.each do |config| + config.build_settings["REACT_NATIVE_PATH"] = File.join("${PODS_ROOT}", "..", react_native_path) + end + + project.save() + end + end + + private + + def self.fix_config(config) + lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"] + + if lib_search_paths == nil + # No search paths defined, return immediately + return + end + + # $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple M1) + # since the libraries there are only built for x86_64 and i386. + lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") + lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"") + + if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\"")) + # however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11 + lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift") + end + end + + end diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index f5f1c56ef2e80e..88eb651a174519 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -142,64 +142,13 @@ def use_flipper!(versions = {}, configurations: ['Debug']) use_flipper_pods(versions, :configurations => configurations) end -def fix_library_search_paths(installer) - def fix_config(config) - lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"] - if lib_search_paths - 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)\"") - # $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple M1) - # since the libraries there are only built for x86_64 and i386. - lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)") - lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"") - if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\"")) - # however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11 - lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift") - end - end - end - end - - projects = installer.aggregate_targets - .map{ |t| t.user_project } - .uniq{ |p| p.path } - .push(installer.pods_project) - - projects.each do |project| - project.build_configurations.each do |config| - fix_config(config) - end - project.native_targets.each do |target| - target.build_configurations.each do |config| - fix_config(config) - end - end - project.save() - end -end - -def set_node_modules_user_settings(installer, react_native_path) - puts "Setting REACT_NATIVE build settings" - projects = installer.aggregate_targets - .map{ |t| t.user_project } - .uniq{ |p| p.path } - .push(installer.pods_project) - - projects.each do |project| - project.build_configurations.each do |config| - config.build_settings["REACT_NATIVE_PATH"] = File.join("${PODS_ROOT}", "..", react_native_path) - end - - project.save() - end -end - def react_native_post_install(installer, react_native_path = "../node_modules/react-native") if ReactNativePodsUtils.has_pod(installer, 'Flipper') flipper_post_install(installer) end ReactNativePodsUtils.exclude_i386_architecture_while_using_hermes(installer) - fix_library_search_paths(installer) + ReactNativePodsUtils.fix_library_search_paths(installer) cpp_flags = DEFAULT_OTHER_CPLUSPLUSFLAGS if ENV['RCT_NEW_ARCH_ENABLED'] == '1' @@ -207,7 +156,7 @@ def react_native_post_install(installer, react_native_path = "../node_modules/re end modify_flags_for_new_architecture(installer, cpp_flags) - set_node_modules_user_settings(installer, react_native_path) + ReactNativePodsUtils.set_node_modules_user_settings(installer, react_native_path) puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run" end