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 = installer.aggregate_targets
58            .map{ |t| t.user_project }
59            .uniq{ |p| p.path }
60            .push(installer.pods_project)
61
62
63        # Hermes does not support `i386` architecture
64        excluded_archs_default = ReactNativePodsUtils.has_pod(installer, 'hermes-engine') ? "i386" : ""
65
66        projects.each do |project|
67            project.build_configurations.each do |config|
68                config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = excluded_archs_default
69            end
70
71            project.save()
72        end
73    end
74
75    def self.set_node_modules_user_settings(installer, react_native_path)
76        Pod::UI.puts("Setting REACT_NATIVE build settings")
77        projects = installer.aggregate_targets
78            .map{ |t| t.user_project }
79            .uniq{ |p| p.path }
80            .push(installer.pods_project)
81
82        projects.each do |project|
83            project.build_configurations.each do |config|
84                config.build_settings["REACT_NATIVE_PATH"] = File.join("${PODS_ROOT}", "..", react_native_path)
85            end
86
87            project.save()
88        end
89    end
90
91    def self.fix_library_search_paths(installer)
92        projects = installer.aggregate_targets
93            .map{ |t| t.user_project }
94            .uniq{ |p| p.path }
95            .push(installer.pods_project)
96
97        projects.each do |project|
98            project.build_configurations.each do |config|
99                ReactNativePodsUtils.fix_library_search_path(config)
100            end
101            project.native_targets.each do |target|
102                target.build_configurations.each do |config|
103                    ReactNativePodsUtils.fix_library_search_path(config)
104                end
105            end
106            project.save()
107        end
108    end
109
110    def self.apply_mac_catalyst_patches(installer)
111        # Fix bundle signing issues
112        installer.pods_project.targets.each do |target|
113            if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
114                target.build_configurations.each do |config|
115                    config.build_settings['CODE_SIGN_IDENTITY[sdk=macosx*]'] = '-'
116                end
117            end
118        end
119
120        installer.aggregate_targets.each do |aggregate_target|
121            aggregate_target.user_project.native_targets.each do |target|
122                target.build_configurations.each do |config|
123                    # Explicitly set dead code stripping flags
124                    config.build_settings['DEAD_CODE_STRIPPING'] = 'YES'
125                    config.build_settings['PRESERVE_DEAD_CODE_INITS_AND_TERMS'] = 'YES'
126                    # Modify library search paths
127                    config.build_settings['LIBRARY_SEARCH_PATHS'] = ['$(SDKROOT)/usr/lib/swift', '$(SDKROOT)/System/iOSSupport/usr/lib/swift', '$(inherited)']
128                end
129            end
130            aggregate_target.user_project.save()
131        end
132    end
133
134    private
135
136    def self.fix_library_search_path(config)
137        lib_search_paths = config.build_settings["LIBRARY_SEARCH_PATHS"]
138
139        if lib_search_paths == nil
140            # No search paths defined, return immediately
141            return
142        end
143
144        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)\"")
145            # $(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME) causes problem with Xcode 12.5 + arm64 (Apple M1)
146            # since the libraries there are only built for x86_64 and i386.
147            lib_search_paths.delete("$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)")
148            lib_search_paths.delete("\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"")
149        end
150
151        if !(lib_search_paths.include?("$(SDKROOT)/usr/lib/swift") || lib_search_paths.include?("\"$(SDKROOT)/usr/lib/swift\""))
152            # however, $(SDKROOT)/usr/lib/swift is required, at least if user is not running CocoaPods 1.11
153            lib_search_paths.insert(0, "$(SDKROOT)/usr/lib/swift")
154        end
155    end
156
157    def self.create_xcode_env_if_missing
158        relative_path = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd)
159        file_path = File.join(relative_path, '.xcode.env')
160        if File.exist?(file_path)
161            return
162        end
163
164        system("echo 'export NODE_BINARY=$(command -v node)' > #{file_path}")
165    end
166
167    # It examines the target_definition property and sets the appropriate value for
168    # ENV['USE_FRAMEWORKS'] variable.
169    #
170    # - parameter target_definition: The current target definition
171    def self.detect_use_frameworks(target_definition)
172        if ENV['USE_FRAMEWORKS'] != nil
173            return
174        end
175
176        framework_build_type = target_definition.build_type.to_s
177
178        Pod::UI.puts("Framework build type is #{framework_build_type}")
179
180        if framework_build_type === "static framework"
181            ENV['USE_FRAMEWORKS'] = 'static'
182        elsif framework_build_type === "dynamic framework"
183            ENV['USE_FRAMEWORKS'] = 'dynamic'
184        else
185            ENV['USE_FRAMEWORKS'] = nil
186        end
187    end
188end
189