From fcea806d41c9a1b6d53946c69f4be736d2b99717 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 7 Nov 2023 03:38:37 -0800 Subject: [PATCH] Add helper function to add dependencies Summary: In order to make the infra scalable and avoid a maintenance nightmare for macOS and future platform, we are introducing this function that automate adding a dependency to a podspec and it generates the required search paths. ## Context Last week I helped macOS to work with static framework. When multiple platforms are specified, frameworks are build in two variants, the iOS and macOS one. This break all the HEADER_SEARCH_PATHS as now we have to properly specify the base folder from which the search path is generated. See also [this PR](https://github.com/microsoft/react-native-macos/pull/1967) where I manually make MacOS work with `use_framewroks!` ## Changelog: [Internal] - Add helper function to create header_search_path Differential Revision: D51027343 --- .../scripts/cocoapods/__tests__/utils-test.rb | 103 ++++++++++++++++++ .../react-native/scripts/cocoapods/utils.rb | 24 ++++ 2 files changed, 127 insertions(+) diff --git a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb index e4d2254e77699a..654f9eb53f672d 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb @@ -15,6 +15,7 @@ require_relative "./test_utils/TargetDefinitionMock.rb" require_relative "./test_utils/XcodeprojMock.rb" require_relative "./test_utils/XcodebuildMock.rb" +require_relative "./test_utils/SpecMock.rb" class UtilsTests < Test::Unit::TestCase def setup @@ -967,6 +968,108 @@ def test_creatHeaderSearchPathForFrameworks_whenMultiplePlatformsAndExtraPath_cr "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric-macOS/React_Fabric.framework/Headers/react/renderer/components/view/platform/ios", ]) end + + # ===================== # + # TEST - Add Dependency # + # ===================== # + def test_addDependency_whenNoHeaderSearchPathAndNoVersion_addsThem + spec = SpecMock.new + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric") + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric"}]) + assert_equal(spec.to_hash["pod_target_xcconfig"], {"HEADER_SEARCH_PATHS" => "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\""}) + end + + def test_addDependency_whenNoHeaderSearchPathAndVersion_addsThem + spec = SpecMock.new + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric", :additional_paths => [], :version => '1000.0.0') + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric", "version" => '1000.0.0'}]) + assert_equal(spec.to_hash["pod_target_xcconfig"], {"HEADER_SEARCH_PATHS" => "\"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\""}) + end + + def test_addDependency_whenHeaderSearchPathAndVersion_addsThemMaintainingTheSearchPaths + spec = SpecMock.new + spec.pod_target_xcconfig["HEADER_SEARCH_PATHS"] = "\"$(PODS_ROOT)/RCT-Folly\"" + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric", :additional_paths => [], :version => '1000.0.0') + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric", "version" => '1000.0.0'}]) + assert_equal(spec.to_hash["pod_target_xcconfig"], {"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/RCT-Folly\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\""}) + end + + def test_addDependencies_whenHeaderSearchPathAndVersion_addsThemMaintainingTheSearchPaths + spec = SpecMock.new + spec.pod_target_xcconfig["HEADER_SEARCH_PATHS"] = "\"$(PODS_ROOT)/RCT-Folly\"" + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric", :additional_paths => [], :version => '1000.0.0') + ReactNativePodsUtils.add_dependency(spec, "React-RCTFabric", "PODS_CONFIGURATION_BUILD_DIR", "RCTFabric", :additional_paths => []) + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric", "version" => '1000.0.0'}, {:dependency_name => "React-RCTFabric" }]) + assert_equal(spec.to_hash["pod_target_xcconfig"], { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/RCT-Folly\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers\""}) + end + + def test_addDependencies_whenHeaderSearchPathAndVersionWithAdditionalPaths_addsThemMaintainingTheSearchPaths + spec = SpecMock.new + spec.pod_target_xcconfig["HEADER_SEARCH_PATHS"] = "\"$(PODS_ROOT)/RCT-Folly\"" + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric", :additional_paths => [], :version => '1000.0.0') + ReactNativePodsUtils.add_dependency(spec, "React-RCTFabric", "PODS_CONFIGURATION_BUILD_DIR", "RCTFabric", :additional_paths => ["react/renderer/components/view/platform/ios"]) + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric", "version" => '1000.0.0'}, {:dependency_name => "React-RCTFabric" }]) + assert_equal(spec.to_hash["pod_target_xcconfig"], { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/RCT-Folly\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers/react/renderer/components/view/platform/ios\""}) + end + + def test_addDependencies_whenHeaderSearchPathAndVersionWithAdditionalPathsAndPlatforms_addsThemMaintainingTheSearchPaths + spec = SpecMock.new + spec.pod_target_xcconfig["HEADER_SEARCH_PATHS"] = "\"$(PODS_ROOT)/RCT-Folly\"" + $RN_PLATFORMS = ['iOS', 'macOS'] + + ReactNativePodsUtils.add_dependency(spec, "React-Fabric", "PODS_CONFIGURATION_BUILD_DIR", "React_Fabric", :additional_paths => [], :version => '1000.0.0') + ReactNativePodsUtils.add_dependency(spec, "React-RCTFabric", "PODS_CONFIGURATION_BUILD_DIR", "RCTFabric", :additional_paths => ["react/renderer/components/view/platform/ios"]) + + expected_search_paths = [ + "$(PODS_ROOT)/RCT-Folly", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric-iOS/React_Fabric.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric-iOS/RCTFabric.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric-iOS/RCTFabric.framework/Headers/react/renderer/components/view/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric-macOS/React_Fabric.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric-macOS/RCTFabric.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric-macOS/RCTFabric.framework/Headers/react/renderer/components/view/platform/ios" + ] + .map { |sp| return "\"#{sp}\"" } + .join(" ") + + assert_equal(spec.dependencies, [{:dependency_name => "React-Fabric", "version" => '1000.0.0'}, {:dependency_name => "React-RCTFabric" }]) + assert_equal(spec.to_hash["pod_target_xcconfig"], { + "HEADER_SEARCH_PATHS" => expected_search_paths}) + end + + def test_addDependencies_whenSubspecsAndHeaderSearchPathAndVersionWithAdditionalPathsAndPlatforms_addsThemMaintainingTheSearchPaths + spec = SpecMock.new + spec.pod_target_xcconfig["HEADER_SEARCH_PATHS"] = "\"$(PODS_ROOT)/RCT-Folly\"" + $RN_PLATFORMS = ['iOS', 'macOS'] + + ReactNativePodsUtils.add_dependency(spec, "ReactCommon", "PODS_CONFIGURATION_BUILD_DIR", "ReactCommon", :additional_paths => ["react/nativemodule/core"], :subspec_dependency => 'turbomodule/core') + + expected_search_paths = [ + "$(PODS_ROOT)/RCT-Folly", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-iOS/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-iOS/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-macOS/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-macOS/ReactCommon.framework/Headers/react/nativemodule/core", + ] + .map { |sp| return "\"#{sp}\"" } + .join(" ") + + assert_equal(spec.dependencies, [{:dependency_name => "ReactCommon/turbomodule/core"}]) + assert_equal(spec.to_hash["pod_target_xcconfig"], { + "HEADER_SEARCH_PATHS" => expected_search_paths}) + end end # ===== # diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index d661f4b62609e9..96f2329064fdea 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -244,6 +244,30 @@ def self.create_header_search_path_for_frameworks(base_folder, pod_name, framewo return search_paths end + # Add a new dependency to an existing spec, configuring also the headers search paths + def self.add_dependency(spec, dependency_name, base_folder_for_frameworks, framework_name, additional_paths: [], version: nil, subspec_dependency: nil) + # Update Search PAth + optional_current_earch_path = spec.to_hash["pod_target_xcconfig"]["HEADER_SEARCH_PATHS"] + current_search_paths = (optional_current_earch_path != nil ? optional_current_earch_path : "") + .split(" ") + create_header_search_path_for_frameworks(base_folder_for_frameworks, dependency_name, framework_name, additional_paths) + .each { |path| + wrapped_path = "\"#{path}\"" + current_search_paths << wrapped_path + } + current_pod_target_xcconfig = spec.to_hash["pod_target_xcconfig"] + current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = current_search_paths.join(" ") + spec.pod_target_xcconfig = current_pod_target_xcconfig + + actual_dependency = subspec_dependency != nil ? "#{dependency_name}/#{subspec_dependency}" : dependency_name + # Set Dependency + if !version + spec.dependency actual_dependency + else + spec.dependency actual_dependency, version + end + end + def self.update_search_paths(installer) return if ENV['USE_FRAMEWORKS'] == nil