diff --git a/packages/react-native/scripts/cocoapods/__tests__/test_utils/FileMock.rb b/packages/react-native/scripts/cocoapods/__tests__/test_utils/FileMock.rb index 8b9796cd951f..cc85741e2a00 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/test_utils/FileMock.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/test_utils/FileMock.rb @@ -16,6 +16,7 @@ class FileMock < File @@open_files = [] @@files_to_read = {} + @@written_files = {} attr_reader :collected_write attr_reader :fsync_invocation_count @@ -98,6 +99,14 @@ def self.read(filepath) return @@files_to_read[filepath] end + def self.write(filepath, content) + @@written_files[filepath] = content + end + + def self.written_files + return @@written_files + end + # Resets all the settings for the File mock def self.reset() @@delete_invocation_count = 0 @@ -109,5 +118,6 @@ def self.reset() @@file_invocation_params = [] @@exist_invocation_params = [] @@files_to_read = {} + @@written_files = {} end end diff --git a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb index 2f338430009c..5b2e28090002 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb @@ -747,13 +747,43 @@ def test_setBuildSettingDebug_addTheUserSetting # =================================== # def test_createXcodeEnvIfMissing_whenTheyArePresent_doNothing # Arrange - FileMock.mocked_existing_files("/.xcode.env") - FileMock.mocked_existing_files("/.xcode.env.local") + FileMock.mocked_existing_files(["/.xcode.env", "/.xcode.env.local", "/usr/local/bin/node"]) + FileMock.files_to_read({ + "/.xcode.env.local" => "export NODE_BINARY=/usr/local/bin/node\n", + }) + # Act + ReactNativePodsUtils.create_xcode_env_if_missing(file_manager: FileMock) + # Assert + assert_equal(["/.xcode.env", "/.xcode.env.local", "/usr/local/bin/node"], FileMock.exist_invocation_params) + assert_equal([], $collected_commands) + end + + def test_createXcodeEnvIfMissing_whenLocalNodeIsMissing_updatesNodeBinary + # Arrange + FileMock.mocked_existing_files(["/.xcode.env", "/.xcode.env.local"]) + FileMock.files_to_read({ + "/.xcode.env.local" => "export FOO=bar\nexport NODE_BINARY=/old/bin/node\n", + }) + # Act + ReactNativePodsUtils.create_xcode_env_if_missing(file_manager: FileMock) + # Assert + assert_equal(["/.xcode.env", "/.xcode.env.local", "/old/bin/node"], FileMock.exist_invocation_params) + assert_equal([], $collected_commands) + assert_match(/\Aexport FOO=bar\nexport NODE_BINARY=(?!\/old\/bin\/node).+\n\z/, FileMock.written_files["/.xcode.env.local"]) + end + + def test_createXcodeEnvIfMissing_whenLocalNodeIsEmpty_updatesNodeBinary + # Arrange + FileMock.mocked_existing_files(["/.xcode.env", "/.xcode.env.local"]) + FileMock.files_to_read({ + "/.xcode.env.local" => "export FOO=bar\nexport NODE_BINARY=\n", + }) # Act ReactNativePodsUtils.create_xcode_env_if_missing(file_manager: FileMock) # Assert assert_equal(["/.xcode.env", "/.xcode.env.local"], FileMock.exist_invocation_params) assert_equal([], $collected_commands) + assert_match(/\Aexport FOO=bar\nexport NODE_BINARY=.+\n\z/, FileMock.written_files["/.xcode.env.local"]) end def test_createXcodeEnvIfMissing_whenTheyAreNotPresent_createsThem diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 6473278a2e7e..b8e6263d9fc5 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -306,16 +306,31 @@ def self.create_xcode_env_if_missing(file_manager: File) system("echo 'export NODE_BINARY=$(command -v node)' > #{file_path}") end - if !file_manager.exist?("#{file_path}.local") + local_file_exists = file_manager.exist?("#{file_path}.local") + if !local_file_exists || !self.local_node_binary_exists?("#{file_path}.local", file_manager: file_manager) # When installing pods with a yarn alias, yarn creates a fake yarn and node executables # in a temporary folder. # Using `node --print "process.argv[0]";` we are able to retrieve the actual path from which node is running. # see https://github.com/facebook/react-native/issues/43285 for more info. We've tweaked this slightly. node_binary = Shellwords.escape(`node --print "process.argv[0]"`.strip) - system("echo 'export NODE_BINARY=#{node_binary}' > #{file_path}.local") + if local_file_exists + local_env = file_manager.read("#{file_path}.local") + file_manager.write("#{file_path}.local", local_env.gsub(/^export NODE_BINARY=.*$/) { "export NODE_BINARY=#{node_binary}" }) + else + system("echo 'export NODE_BINARY=#{node_binary}' > #{file_path}.local") + end end end + def self.local_node_binary_exists?(local_file_path, file_manager: File) + node_binary = file_manager.read(local_file_path).scan(/^export NODE_BINARY=(.*)$/).flatten.last + return true if node_binary.nil? + node_binary = Shellwords.split(node_binary).first + return false if node_binary.nil? || node_binary.empty? + return true if !node_binary.start_with?("/") + return file_manager.exist?(node_binary) + end + # It examines the target_definition property and sets the appropriate value for # ENV['USE_FRAMEWORKS'] variable. #