diff --git a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb index f2570238226a01..1b7d2e53ca8489 100644 --- a/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb +++ b/packages/react-native/scripts/cocoapods/__tests__/new_architecture-test.rb @@ -8,10 +8,13 @@ require_relative "./test_utils/InstallerMock.rb" require_relative "./test_utils/PodMock.rb" require_relative "./test_utils/SpecMock.rb" +require_relative "./test_utils/FileMock.rb" class NewArchitectureTests < Test::Unit::TestCase def teardown Pod::UI.reset() + FileMock.reset() + ENV["RCT_NEW_ARCH_ENABLED"] = nil end # ============================= # @@ -181,6 +184,141 @@ def test_installModulesDependencies_whenNewArchDisabledAndSearchPathsAndCompiler ] ) end + + # =============================== # + # Test - Compute New Arch Enabled # + # =============================== # + + def test_computeNewArchEnabled_whenOnMainAndFlagTrue_returnTrue + version = '1000.0.0' + new_arch_enabled = true + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOnMainAndFlagFalse_returnFalse + version = '1000.0.0' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("0", isEnabled) + end + + def test_computeNewArchEnabled_whenOnStableAndFlagTrue_returnTrue + version = '0.73.0' + new_arch_enabled = true + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOnStableAndFlagFalse_returnFalse + version = '0.73.0' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("0", isEnabled) + end + + def test_computeNewArchEnabled_whenOn100AndFlagTrue_returnTrue + version = '1.0.0-prealpha.0' + new_arch_enabled = true + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOn100PrealphaWithDotsAndFlagFalse_returnTrue + version = '1.0.0-prealpha.0' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOn100PrealphaWithDashAndFlagFalse_returnTrue + version = '1.0.0-prealpha-0' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOn100PrealphaOnlyWordsAndFlagFalse_returnTrue + version = '1.0.0-prealpha0' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + def test_computeNewArchEnabled_whenOnGreaterThan100AndFlagFalse_returnTrue + version = '3.2.1' + new_arch_enabled = false + + isEnabled = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, version) + + assert_equal("1", isEnabled) + end + + # =================================== # + # Test - Extract React Native Version # + # =================================== # + def test_extractReactNativeVersion_whenFileDoesNotExists_raiseError() + react_native_path = './node_modules/react-native/' + + exception = assert_raise(RuntimeError) do + NewArchitectureHelper.extract_react_native_version(react_native_path, :file_manager => FileMock) + end + + assert_equal("Couldn't find the React Native package.json file at ./node_modules/react-native/package.json", exception.message) + end + + def test_extractReactNativeVersion_whenFileExists_returnTheRightVersion() + react_native_path = "./node_modules/react-native/" + full_path = File.join(react_native_path, "package.json") + json = "{\"version\": \"1.0.0-prealpha.0\"}" + FileMock.mocked_existing_files([full_path]) + FileMock.files_to_read({ + full_path => json + }) + + version = NewArchitectureHelper.extract_react_native_version(react_native_path, :file_manager => FileMock) + + assert_equal("1.0.0-prealpha.0", version) + end + + # =============================== # + # Test - New Architecture Enabled # + # =============================== # + def test_newArchEnabled_whenRCTNewArchEnabledIsSetTo1_returnTrue + ENV["RCT_NEW_ARCH_ENABLED"] = "1" + is_enabled = NewArchitectureHelper.new_arch_enabled + assert_true(is_enabled) + end + + def test_newArchEnabled_whenRCTNewArchEnabledIsSetTo0_returnFalse + ENV["RCT_NEW_ARCH_ENABLED"] = "0" + is_enabled = NewArchitectureHelper.new_arch_enabled + assert_false(is_enabled) + end + + def test_newArchEnabled_whenRCTNewArchEnabledIsNotSet_returnFalse + ENV["RCT_NEW_ARCH_ENABLED"] = nil + is_enabled = NewArchitectureHelper.new_arch_enabled + assert_false(is_enabled) + end + + end # ================ # diff --git a/packages/react-native/scripts/cocoapods/new_architecture.rb b/packages/react-native/scripts/cocoapods/new_architecture.rb index 956582032070fa..42556e332037ed 100644 --- a/packages/react-native/scripts/cocoapods/new_architecture.rb +++ b/packages/react-native/scripts/cocoapods/new_architecture.rb @@ -3,6 +3,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +require 'json' + class NewArchitectureHelper @@shared_flags = "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1" @@ -72,6 +74,7 @@ def self.modify_flags_for_new_architecture(installer, is_new_arch_enabled) # Set "RCT_DYNAMIC_FRAMEWORKS=1" if pod are installed with USE_FRAMEWORKS=dynamic # This helps with backward compatibility. if pod_name == 'React-RCTFabric' && ENV['USE_FRAMEWORKS'] == 'dynamic' + Pod::UI.puts "Setting -DRCT_DYNAMIC_FRAMEWORKS=1 to React-RCTFabric".green rct_dynamic_framework_flag = " -DRCT_DYNAMIC_FRAMEWORKS=1" target_installation_result.native_target.build_configurations.each do |config| prev_build_settings = config.build_settings['OTHER_CPLUSPLUSFLAGS'] != nil ? config.build_settings['OTHER_CPLUSPLUSFLAGS'] : "$(inherithed)" @@ -158,4 +161,40 @@ def self.install_modules_dependencies(spec, new_arch_enabled, folly_version) def self.folly_compiler_flags return @@folly_compiler_flags end + + def self.extract_react_native_version(react_native_path, file_manager: File, json_parser: JSON) + package_json_file = File.join(react_native_path, "package.json") + if !file_manager.exist?(package_json_file) + raise "Couldn't find the React Native package.json file at #{package_json_file}" + end + package = json_parser.parse(file_manager.read(package_json_file)) + return package["version"] + end + + def self.compute_new_arch_enabled(new_arch_enabled, react_native_version) + # Regex that identify a version with the syntax `..[-[.-]k] + # where + # - major is a number + # - minor is a number + # - patch is a number + # - prerelease is a string (can include numbers) + # - k is a number + version_regex = /^(\d+)\.(\d+)\.(\d+)(?:-(\w+(?:[-.]\d+)?))?$/ + + if match_data = react_native_version.match(version_regex) + + major = match_data[1].to_i + + # We want to enforce the new architecture for 1.0.0 and greater, + # but not for 1000 as version 1000 is currently main. + if major > 0 && major < 1000 + return "1" + end + end + return new_arch_enabled ? "1" : "0" + end + + def self.new_arch_enabled + return ENV["RCT_NEW_ARCH_ENABLED"] == "1" + end end diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 4582d541506be5..87f8d1966306b5 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -16,6 +16,9 @@ def self.warn_if_not_on_arm64 end end + # deprecated. These checks are duplicated in the react_native_pods function + # and we don't really need them. Removing this function will make it easy to + # move forward. def self.get_default_flags flags = { :fabric_enabled => false, diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 0d7e0bfcd2d092..4c1904443487be 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -72,7 +72,7 @@ def prepare_react_native_project! def use_react_native! ( path: "../node_modules/react-native", fabric_enabled: false, - new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == '1', + new_arch_enabled: NewArchitectureHelper.new_arch_enabled, production: false, # deprecated hermes_enabled: ENV['USE_HERMES'] && ENV['USE_HERMES'] == '0' ? false : true, flipper_configuration: FlipperConfiguration.disabled, @@ -93,8 +93,11 @@ def use_react_native! ( # We are relying on this flag also in third parties libraries to proper install dependencies. # Better to rely and enable this environment flag if the new architecture is turned on using flags. - ENV['RCT_NEW_ARCH_ENABLED'] = new_arch_enabled ? "1" : "0" - fabric_enabled = fabric_enabled || new_arch_enabled + relative_path_from_current = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd) + react_native_version = NewArchitectureHelper.extract_react_native_version(File.join(relative_path_from_current, path)) + ENV['RCT_NEW_ARCH_ENABLED'] = NewArchitectureHelper.compute_new_arch_enabled(new_arch_enabled, react_native_version) + + fabric_enabled = fabric_enabled || NewArchitectureHelper.new_arch_enabled ENV['RCT_FABRIC_ENABLED'] = fabric_enabled ? "1" : "0" ENV['USE_HERMES'] = hermes_enabled ? "1" : "0" @@ -104,7 +107,7 @@ def use_react_native! ( # The Pods which should be included in all projects pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector" - pod 'FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec" if !new_arch_enabled + pod 'FBReactNativeSpec', :path => "#{prefix}/React/FBReactNativeSpec" if !NewArchitectureHelper.new_arch_enabled pod 'RCTRequired', :path => "#{prefix}/Libraries/RCTRequired" pod 'RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :modular_headers => true pod 'React', :path => "#{prefix}/" @@ -156,7 +159,7 @@ def use_react_native! ( run_codegen!( app_path, config_file_dir, - :new_arch_enabled => new_arch_enabled, + :new_arch_enabled => NewArchitectureHelper.new_arch_enabled, :disable_codegen => ENV['DISABLE_CODEGEN'] == '1', :react_native_path => prefix, :fabric_enabled => fabric_enabled, @@ -172,14 +175,14 @@ def use_react_native! ( # If the New Arch is turned off, we will use the Old Renderer, though. # RNTester always installed Fabric, this change is required to make the template work. setup_fabric!(:react_native_path => prefix) - checkAndGenerateEmptyThirdPartyProvider!(prefix, new_arch_enabled) + checkAndGenerateEmptyThirdPartyProvider!(prefix, NewArchitectureHelper.new_arch_enabled) if !fabric_enabled relative_installation_root = Pod::Config.instance.installation_root.relative_path_from(Pathname.pwd) build_codegen!(prefix, relative_installation_root) end - if new_arch_enabled + if NewArchitectureHelper.new_arch_enabled setup_bridgeless!(:react_native_path => prefix, :use_hermes => hermes_enabled) end @@ -212,12 +215,15 @@ def folly_flags() # Parameters: # - spec: The spec that has to be configured with the New Architecture code # - new_arch_enabled: Whether the module should install dependencies for the new architecture -def install_modules_dependencies(spec, new_arch_enabled: ENV['RCT_NEW_ARCH_ENABLED'] == "1") +def install_modules_dependencies(spec, new_arch_enabled: NewArchitectureHelper.new_arch_enabled) NewArchitectureHelper.install_modules_dependencies(spec, new_arch_enabled, $FOLLY_VERSION) end # It returns the default flags. +# deprecated. def get_default_flags() + warn 'get_default_flags is deprecated. Please remove the keys from the `use_react_native!` function' + warn 'if you are using the default already and pass the value you need in case you don\'t want the default' return ReactNativePodsUtils.get_default_flags() end @@ -268,8 +274,7 @@ def react_native_post_install( ReactNativePodsUtils.apply_ats_config(installer) NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer) - is_new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == "1" - NewArchitectureHelper.modify_flags_for_new_architecture(installer, is_new_arch_enabled) + NewArchitectureHelper.modify_flags_for_new_architecture(installer, NewArchitectureHelper.new_arch_enabled) Pod::UI.puts "Pod install took #{Time.now.to_i - $START_TIME} [s] to run".green end @@ -278,7 +283,7 @@ def react_native_post_install( # We need to keep this while we continue to support the old architecture. # ===================== def use_react_native_codegen!(spec, options={}) - return if ENV['RCT_NEW_ARCH_ENABLED'] == "1" + return if NewArchitectureHelper.new_arch_enabled # TODO: Once the new codegen approach is ready for use, we should output a warning here to let folks know to migrate. # The prefix to react-native diff --git a/packages/react-native/template/ios/Podfile b/packages/react-native/template/ios/Podfile index 9e19c2319bd1bc..8a7176582a8408 100644 --- a/packages/react-native/template/ios/Podfile +++ b/packages/react-native/template/ios/Podfile @@ -28,14 +28,8 @@ end target 'HelloWorld' do config = use_native_modules! - # Flags change depending on the env values. - flags = get_default_flags() - use_react_native!( :path => config[:reactNativePath], - # Hermes is now enabled by default. Disable by setting this flag to false. - :hermes_enabled => flags[:hermes_enabled], - :fabric_enabled => flags[:fabric_enabled], # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and diff --git a/packages/rn-tester/Podfile b/packages/rn-tester/Podfile index 6c278245086cd9..443048d7b2dead 100644 --- a/packages/rn-tester/Podfile +++ b/packages/rn-tester/Podfile @@ -44,11 +44,6 @@ def pods(target_name, options = {}, use_flipper: $shouldUseFlipper) hermes_enabled = !ENV.has_key?('USE_HERMES') || ENV['USE_HERMES'] == '1' puts "Configuring #{target_name} with Fabric #{fabric_enabled ? "enabled" : "disabled"}.#{hermes_enabled ? " Using Hermes engine." : ""}" - if ENV['RCT_NEW_ARCH_ENABLED'] == '1' - # Custom fabric component is only supported when using codegen discovery. - pod 'MyNativeView', :path => "NativeComponentExample" - end - use_react_native!( path: @prefix_path, fabric_enabled: fabric_enabled, @@ -68,7 +63,9 @@ def pods(target_name, options = {}, use_flipper: $shouldUseFlipper) # RNTester native modules and components pod 'ScreenshotManager', :path => "NativeModuleExample" + if ENV['RCT_NEW_ARCH_ENABLED'] == '1' + pod 'MyNativeView', :path => "NativeComponentExample" pod 'NativeCxxModuleExample', :path => "NativeCxxModuleExample" end end