Create script to automatically set CLANG_CXX_LANGUAGE_STANDARD on the client project (#33863)

Summary:
Currently this [section](https://reactnative.dev/docs/next/new-architecture-app-intro#ios-enable-c17-language-feature-support) of the Playbook tells us to set CLANG_CXX_LANGUAGE_STANDARD = "c++17" in the main app target for the new architecture to work.
Would be nice to be able to automate that instead

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Added] - Cocoapods function to add the `CLANG_CXX_LANGUAGE_STANDARD` to all the targets if needed

Pull Request resolved: https://github.com/facebook/react-native/pull/33863

Test Plan:
I've created some unit tests for the newly added function.
I've executed pod install and the ruby tests locally.

Reviewed By: cipolleschi

Differential Revision: D36484366

Pulled By: f-meloni

fbshipit-source-id: 553b092e747bef11d82195619ae1058985fdc325
This commit is contained in:
Franco Meloni 2022-05-19 14:57:04 -07:00 коммит произвёл Facebook GitHub Bot
Родитель baada4e299
Коммит ca8174e15f
7 изменённых файлов: 206 добавлений и 4 удалений

Просмотреть файл

@ -47,7 +47,7 @@ Pod::Spec.new do |s|
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.header_dir = "React"
s.framework = "JavaScriptCore"
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"${PODS_ROOT}/Headers/Public/React-hermes\" \"${PODS_ROOT}/Headers/Public/hermes-engine\" \"${PODS_ROOT}/Headers/Public/FlipperKit\" \"$(PODS_ROOT)/Headers/Public/ReactCommon\" \"$(PODS_ROOT)/Headers/Public/React-RCTFabric\"", "DEFINES_MODULE" => "YES", "GCC_PREPROCESSOR_DEFINITIONS" => "RCT_METRO_PORT=${RCT_METRO_PORT}" }
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/ReactCommon\" \"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/RCT-Folly\" \"${PODS_ROOT}/Headers/Public/React-hermes\" \"${PODS_ROOT}/Headers/Public/hermes-engine\" \"${PODS_ROOT}/Headers/Public/FlipperKit\" \"$(PODS_ROOT)/Headers/Public/ReactCommon\" \"$(PODS_ROOT)/Headers/Public/React-RCTFabric\"", "DEFINES_MODULE" => "YES", "GCC_PREPROCESSOR_DEFINITIONS" => "RCT_METRO_PORT=${RCT_METRO_PORT}", "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }
s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\""}
s.default_subspec = "Default"

Просмотреть файл

@ -56,6 +56,7 @@
"scripts/cocoapods/codegen.rb",
"scripts/cocoapods/fabric.rb",
"scripts/cocoapods/flipper.rb",
"scripts/cocoapods/new_architecture.rb",
"scripts/react-native-xcode.sh",
"sdks/hermes-engine",
"sdks/hermesc",

Просмотреть файл

@ -1,4 +1,5 @@
require_relative '../../scripts/react_native_pods'
require_relative '../../scripts/cocoapods/new_architecture'
source 'https://cdn.cocoapods.org/'
platform :ios, '12.4'
@ -65,4 +66,5 @@ end
post_install do |installer|
react_native_post_install(installer, @prefix_path)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
set_clang_cxx_language_standard_if_needed(installer)
end

Просмотреть файл

@ -843,7 +843,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -913,6 +913,7 @@
"-ObjC",
"-lc++",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../..";
SDKROOT = iphoneos;
WARNING_CFLAGS = (
"-Wextra",
@ -927,7 +928,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -989,6 +990,7 @@
"-ObjC",
"-lc++",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../..";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = (

Просмотреть файл

@ -0,0 +1,133 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "test/unit"
require_relative "../new_architecture.rb"
require_relative "./test_utils/InstallerMock.rb"
require_relative "./test_utils/PodMock.rb"
class NewArchitectureTests < Test::Unit::TestCase
def setup
File.enable_testing_mode!
end
def teardown
Pod::UI.reset()
end
def test_setClangCxxLanguageStandardIfNeeded_whenReactCoreIsPresent
installer = prepare_mocked_installer_with_react_core
set_clang_cxx_language_standard_if_needed(installer)
assert_equal(installer.aggregate_targets[0].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], "c++17")
assert_equal(installer.aggregate_targets[1].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], "c++17")
assert_equal(installer.pods_project.targets[1].received_resolved_build_setting_parameters, [ReceivedCommonResolvedBuildSettings.new("CLANG_CXX_LANGUAGE_STANDARD", true)])
assert_equal(Pod::UI.collected_messages, ["Setting CLANG_CXX_LANGUAGE_STANDARD to c++17 on /test/path.xcproj", "Setting CLANG_CXX_LANGUAGE_STANDARD to c++17 on /test/path2.xcproj"])
end
def test_setClangCxxLanguageStandardIfNeeded_whenReactCoreIsNotPresent
installer = prepare_mocked_installer_without_react_core
set_clang_cxx_language_standard_if_needed(installer)
assert_equal(installer.aggregate_targets[0].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], nil)
assert_equal(installer.aggregate_targets[1].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], nil)
assert_equal(installer.pods_project.targets[0].received_resolved_build_setting_parameters, [])
assert_equal(Pod::UI.collected_messages, [])
end
def test_setClangCxxLanguageStandardIfNeeded_whenThereAreDifferentValuesForLanguageStandard_takesTheFirstValue
installer = prepare_mocked_installer_with_react_core_and_different_language_standards
set_clang_cxx_language_standard_if_needed(installer)
assert_equal(installer.aggregate_targets[0].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], "c++17")
assert_equal(installer.aggregate_targets[1].user_project.build_configurations[0].build_settings["CLANG_CXX_LANGUAGE_STANDARD"], "c++17")
assert_equal(installer.pods_project.targets[1].received_resolved_build_setting_parameters, [ReceivedCommonResolvedBuildSettings.new("CLANG_CXX_LANGUAGE_STANDARD", true)])
assert_equal(Pod::UI.collected_messages, ["Setting CLANG_CXX_LANGUAGE_STANDARD to c++17 on /test/path.xcproj", "Setting CLANG_CXX_LANGUAGE_STANDARD to c++17 on /test/path2.xcproj"])
end
end
def prepare_mocked_installer_with_react_core
return InstallerMock.new(
PodsProjectMock.new([
TargetMock.new(
"YogaKit",
[
BuildConfigurationMock.new("Debug"),
BuildConfigurationMock.new("Release"),
]
),
TargetMock.new(
"React-Core",
[
BuildConfigurationMock.new("Debug", { "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }),
BuildConfigurationMock.new("Release", { "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }),
]
)
]
),
[
AggregatedProjectMock.new(
UserProjectMock.new("/test/path.xcproj", [BuildConfigurationMock.new("Debug")])
),
AggregatedProjectMock.new(
UserProjectMock.new("/test/path2.xcproj", [BuildConfigurationMock.new("Debug")])
),
]
)
end
def prepare_mocked_installer_with_react_core_and_different_language_standards
return InstallerMock.new(
PodsProjectMock.new([
TargetMock.new(
"YogaKit",
[
BuildConfigurationMock.new("Debug"),
BuildConfigurationMock.new("Release"),
]
),
TargetMock.new(
"React-Core",
[
BuildConfigurationMock.new("Debug", { "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" }),
BuildConfigurationMock.new("Release", { "CLANG_CXX_LANGUAGE_STANDARD" => "new" }),
]
)
]
),
[
AggregatedProjectMock.new(
UserProjectMock.new("/test/path.xcproj", [BuildConfigurationMock.new("Debug")])
),
AggregatedProjectMock.new(
UserProjectMock.new("/test/path2.xcproj", [BuildConfigurationMock.new("Debug")])
),
]
)
end
def prepare_mocked_installer_without_react_core
return InstallerMock.new(
PodsProjectMock.new([
TargetMock.new(
"YogaKit",
[
BuildConfigurationMock.new("Debug"),
BuildConfigurationMock.new("Release"),
]
)
]
),
[
AggregatedProjectMock.new(
UserProjectMock.new("/test/path.xcproj", [BuildConfigurationMock.new("Debug")])
),
AggregatedProjectMock.new(
UserProjectMock.new("/test/path2.xcproj", [BuildConfigurationMock.new("Debug")])
),
]
)
end

Просмотреть файл

@ -38,9 +38,11 @@
class InstallerMock
attr_reader :pods_project
attr_reader :aggregate_targets
def initialize(pods_project = PodsProjectMock.new)
def initialize(pods_project = PodsProjectMock.new, aggregate_targets = [AggregatedProjectMock.new])
@pods_project = pods_project
@aggregate_targets = aggregate_targets
end
def target_with_name(name)
@ -58,13 +60,45 @@ class PodsProjectMock
end
end
class AggregatedProjectMock
attr_reader :user_project
def initialize(user_project = UserProjectMock.new)
@user_project = user_project
end
end
class UserProjectMock
attr_reader :path
attr_reader :build_configurations
def initialize(path = "/test/path.xcproj", build_configurations = [])
@path = path
@build_configurations = build_configurations
end
def save()
end
end
ReceivedCommonResolvedBuildSettings = Struct.new(:key, :resolve_against_xcconfig)
class TargetMock
attr_reader :name
attr_reader :build_configurations
attr_reader :received_resolved_build_setting_parameters
def initialize(name, build_configurations = [])
@name = name
@build_configurations = build_configurations
@received_resolved_build_setting_parameters = []
end
def resolved_build_setting(key, resolve_against_xcconfig: false)
received_resolved_build_setting_parameters.append(ReceivedCommonResolvedBuildSettings.new(key, resolve_against_xcconfig))
return {name: build_configurations[0].build_settings[key]}
end
end

Просмотреть файл

@ -0,0 +1,30 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
def set_clang_cxx_language_standard_if_needed(installer)
language_standard = nil
installer.pods_project.targets.each do |target|
if target.name == 'React-Core'
language_standard = target.resolved_build_setting("CLANG_CXX_LANGUAGE_STANDARD", resolve_against_xcconfig: true).values[0]
end
end
unless language_standard.nil?
projects = installer.aggregate_targets
.map{ |t| t.user_project }
.uniq{ |p| p.path }
projects.each do |project|
Pod::UI.puts("Setting CLANG_CXX_LANGUAGE_STANDARD to #{ language_standard } on #{ project.path }")
project.build_configurations.each do |config|
config.build_settings["CLANG_CXX_LANGUAGE_STANDARD"] = language_standard
end
project.save()
end
end
end