diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 6d0af84fc364..bef13959b49d 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -117,8 +117,6 @@ "scripts/setup-apple-spm.js", "scripts/spm", "scripts/xcode/asset-catalog.sh", - "scripts/xcode/ccache-clang.sh", - "scripts/xcode/ccache-clang++.sh", "scripts/xcode/ccache.conf", "scripts/xcode/with-environment.sh", "sdks/.hermesv1version", diff --git a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb index 44e0e3bcaa9c..c18eabe6eadf 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/utils-test.rb @@ -1155,6 +1155,56 @@ def test_add_ndebug_flag_to_pods_in_release assert_equal("$(inherited) -DNDEBUG", custom_release_config2.build_settings["OTHER_CPLUSPLUSFLAGS"]) assert_equal("$(inherited) -DNDEBUG", custom_release_config3.build_settings["OTHER_CPLUSPLUSFLAGS"]) end + + # ================================ # + # Test - ccache launcher env # + # ================================ # + + def test_ccacheLauncherEnv_exportsResolvedPaths + # Act + env = ReactNativePodsUtils.ccache_launcher_env("/opt/homebrew/bin/ccache", "/app/rn/scripts/xcode/ccache.conf") + + # Assert + assert(env.include?("REACT_NATIVE_CCACHE_BINARY=/opt/homebrew/bin/ccache\n")) + assert(env.include?("REACT_NATIVE_CCACHE_CONFIGPATH=/app/rn/scripts/xcode/ccache.conf\n")) + end + + def test_ccacheLauncherEnv_escapesPathsWithSpaces + # Act + env = ReactNativePodsUtils.ccache_launcher_env("/opt/homebrew/bin/ccache", "/Users/me/My App/rn/ccache.conf") + + # Assert + assert(env.include?("REACT_NATIVE_CCACHE_CONFIGPATH=/Users/me/My\\ App/rn/ccache.conf\n")) + end + + # ================================ # + # Test - remove ccache launcher # + # ================================ # + + def test_removeCcacheLauncher_removesCurrentLauncher + # Act + value = ReactNativePodsUtils.remove_ccache_launcher("$(PODS_ROOT)/ccache-clang.sh", "ccache-clang.sh") + + # Assert + assert_equal("", value) + end + + def test_removeCcacheLauncher_removesLauncherFromOlderReactNative + # Act + value = ReactNativePodsUtils.remove_ccache_launcher("$(REACT_NATIVE_PATH)/scripts/xcode/ccache-clang++.sh", "ccache-clang++.sh") + + # Assert + assert_equal("", value) + end + + def test_removeCcacheLauncher_keepsOtherCompilers + # Act + value = ReactNativePodsUtils.remove_ccache_launcher("/usr/bin/clang", "ccache-clang.sh") + + # Assert + assert_equal("/usr/bin/clang", value) + end + end # ===== # diff --git a/packages/react-native/scripts/xcode/ccache-clang++.sh b/packages/react-native/scripts/cocoapods/ccache-clang++.sh similarity index 55% rename from packages/react-native/scripts/xcode/ccache-clang++.sh rename to packages/react-native/scripts/cocoapods/ccache-clang++.sh index 54ff8ba5816d..9bcae5c5e58e 100755 --- a/packages/react-native/scripts/xcode/ccache-clang++.sh +++ b/packages/react-native/scripts/cocoapods/ccache-clang++.sh @@ -4,11 +4,12 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -# Get the absolute path of this script -SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +# Xcode gives a compiler launcher none of the build settings, so `pod install` +# copies this script next to a generated environment file and points CC/LD at +# the copy. +. "$(dirname "$0")/ccache-launcher.env" -REACT_NATIVE_CCACHE_CONFIGPATH=$SCRIPT_DIR/ccache.conf # Provide our config file if none is already provided export CCACHE_CONFIGPATH="${CCACHE_CONFIGPATH:-$REACT_NATIVE_CCACHE_CONFIGPATH}" -exec $CCACHE_BINARY clang++ "$@" +exec "$REACT_NATIVE_CCACHE_BINARY" clang++ "$@" diff --git a/packages/react-native/scripts/xcode/ccache-clang.sh b/packages/react-native/scripts/cocoapods/ccache-clang.sh similarity index 56% rename from packages/react-native/scripts/xcode/ccache-clang.sh rename to packages/react-native/scripts/cocoapods/ccache-clang.sh index 9b1a355c2cce..6a732d91fd39 100755 --- a/packages/react-native/scripts/xcode/ccache-clang.sh +++ b/packages/react-native/scripts/cocoapods/ccache-clang.sh @@ -4,11 +4,12 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -# Get the absolute path of this script -SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +# Xcode gives a compiler launcher none of the build settings, so `pod install` +# copies this script next to a generated environment file and points CC/LD at +# the copy. +. "$(dirname "$0")/ccache-launcher.env" -REACT_NATIVE_CCACHE_CONFIGPATH=$SCRIPT_DIR/ccache.conf # Provide our config file if none is already provided export CCACHE_CONFIGPATH="${CCACHE_CONFIGPATH:-$REACT_NATIVE_CCACHE_CONFIGPATH}" -exec $CCACHE_BINARY clang "$@" +exec "$REACT_NATIVE_CCACHE_BINARY" clang "$@" diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index f65d0798fcb5..b6e796be9b78 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -4,6 +4,7 @@ # LICENSE file in the root directory of this source tree. require 'shellwords' +require 'fileutils' require 'digest' require 'uri' require 'net/http' @@ -156,6 +157,43 @@ def self.set_build_setting(installer, build_setting:, value:, config_name: nil) end end + # Values that only `pod install` can resolve, written next to the launcher + # scripts so that they can read them at build time. + def self.ccache_launcher_env(ccache_path, config_path) + <<~ENVFILE + # Generated by pod install. Edit scripts/cocoapods/utils.rb, not this file. + REACT_NATIVE_CCACHE_BINARY=#{Shellwords.escape(ccache_path)} + REACT_NATIVE_CCACHE_CONFIGPATH=#{Shellwords.escape(config_path)} + ENVFILE + end + + # Older React Native versions pointed the build settings at the copy of the + # launcher shipped in node_modules, so both paths have to be recognized. + def self.remove_ccache_launcher(value, launcher_name) + [ + File.join("$(PODS_ROOT)", launcher_name), + File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', launcher_name) + ].reduce(value) { |result, path| result.gsub(path, '') } + end + + def self.generate_ccache_launchers(installer, react_native_path, ccache_path) + pods_root = installer.sandbox.root.to_s + config_path = File.expand_path( + File.join(react_native_path, 'scripts', 'xcode', 'ccache.conf'), + Pod::Config.instance.installation_root.to_s + ) + File.write( + File.join(pods_root, 'ccache-launcher.env'), + self.ccache_launcher_env(ccache_path, config_path) + ) + + ['ccache-clang.sh', 'ccache-clang++.sh'].each do |name| + destination = File.join(pods_root, name) + FileUtils.cp(File.join(__dir__, name), destination) + File.chmod(0755, destination) + end + end + def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_path, ccache_enabled) projects = self.extract_projects(installer) @@ -169,8 +207,8 @@ def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_p end # Using scripts wrapping the ccache executable, to allow injection of configurations - ccache_clang_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang.sh') - ccache_clangpp_sh = File.join("$(REACT_NATIVE_PATH)", 'scripts', 'xcode', 'ccache-clang++.sh') + ccache_clang_sh = File.join("$(PODS_ROOT)", 'ccache-clang.sh') + ccache_clangpp_sh = File.join("$(PODS_ROOT)", 'ccache-clang++.sh') if ccache_available and ccache_enabled Pod::UI.puts("#{message_prefix}: Setting CC, LD, CXX & LDPLUSPLUS build settings") @@ -182,25 +220,30 @@ def self.set_ccache_compiler_and_linker_build_settings(installer, react_native_p config.build_settings["LD"] = ccache_clang_sh config.build_settings["CXX"] = ccache_clangpp_sh config.build_settings["LDPLUSPLUS"] = ccache_clangpp_sh - config.build_settings["CCACHE_BINARY"] = ccache_path end project.save() end - elsif ccache_available and !ccache_enabled - Pod::UI.puts("#{message_prefix}: Pass ':ccache_enabled => true' to 'react_native_post_install' in your Podfile or set environment variable 'USE_CCACHE=1' to increase the speed of subsequent builds") - elsif !ccache_available and ccache_enabled - Pod::UI.warn("#{message_prefix}: Install ccache or ensure your neither passing ':ccache_enabled => true' nor setting environment variable 'USE_CCACHE=1'") + + self.generate_ccache_launchers(installer, react_native_path, ccache_path) else - Pod::UI.puts("#{message_prefix}: Removing Ccache from CC, LD, CXX & LDPLUSPLUS build settings") + if ccache_available and !ccache_enabled + Pod::UI.puts("#{message_prefix}: Pass ':ccache_enabled => true' to 'react_native_post_install' in your Podfile or set environment variable 'USE_CCACHE=1' to increase the speed of subsequent builds") + elsif !ccache_available and ccache_enabled + Pod::UI.warn("#{message_prefix}: Install ccache or ensure your neither passing ':ccache_enabled => true' nor setting environment variable 'USE_CCACHE=1'") + else + Pod::UI.puts("#{message_prefix}: Removing Ccache from CC, LD, CXX & LDPLUSPLUS build settings") + end + # The launchers only exist when this install created them, so the + # settings cannot be left behind in any of these cases. projects.each do |project| project.build_configurations.each do |config| # Using the un-qualified names means you can swap in different implementations, for example ccache - config.build_settings["CC"] = config.build_settings["CC"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, '') if config.build_settings["CC"] - config.build_settings["LD"] = config.build_settings["LD"].gsub(/#{Regexp.escape(ccache_clang_sh)}/, "") if config.build_settings["LD"] - config.build_settings["CXX"] = config.build_settings["CXX"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") if config.build_settings["CXX"] - config.build_settings["LDPLUSPLUS"] = config.build_settings["LDPLUSPLUS"].gsub(/#{Regexp.escape(ccache_clangpp_sh)}/, "") if config.build_settings["LDPLUSPLUS"] + config.build_settings["CC"] = self.remove_ccache_launcher(config.build_settings["CC"], 'ccache-clang.sh') if config.build_settings["CC"] + config.build_settings["LD"] = self.remove_ccache_launcher(config.build_settings["LD"], 'ccache-clang.sh') if config.build_settings["LD"] + config.build_settings["CXX"] = self.remove_ccache_launcher(config.build_settings["CXX"], 'ccache-clang++.sh') if config.build_settings["CXX"] + config.build_settings["LDPLUSPLUS"] = self.remove_ccache_launcher(config.build_settings["LDPLUSPLUS"], 'ccache-clang++.sh') if config.build_settings["LDPLUSPLUS"] end project.save()