From 65abc361bffe12df6af5e9b6a527775e7260f92c Mon Sep 17 00:00:00 2001 From: Kudo Chien Date: Mon, 11 Apr 2022 06:04:20 -0700 Subject: [PATCH] Fix ios build error when podfile generate_multiple_pod_projects=true and fabric is on (#33381) Summary: there are build errors happened when in [`generate_multiple_pod_projects`](https://blog.cocoapods.org/CocoaPods-1.7.0-beta/#multiple-xcodeproj-generation) mode and fabric is on. since we have multiple pod projects, there are something breaks. ### `-DRCT_NEW_ARCH_ENABLED=1` does not pass to `RCTAppSetupUtils.mm` because `installer.pods_project` is targeting `Pods.xcodeproj` but not `React-Core.xcodeproj`. we should use ` installer.target_installation_results.pod_target_installation_results` to deal with `generate_multiple_pod_projects` mode. ### fatal error: 'CompactValue.h' file not found ``` In file included from /path/to/react-native/packages/rn-tester/build/generated/ios/react/renderer/components/rncore/Props.cpp:11: In file included from /path/to/react-native/packages/rn-tester/Pods/Headers/Private/React-Codegen/react/renderer/components/rncore/Props.h:13: In file included from /path/to/react-native/packages/rn-tester/Pods/Headers/Private/React-Fabric/react/renderer/components/view/ViewProps.h:11: In file included from /path/to/react-native/packages/rn-tester/Pods/Headers/Private/React-Fabric/react/renderer/components/view/YogaStylableProps.h:10: /path/to/react-native/packages/rn-tester/Pods/Headers/Public/Yoga/yoga/YGStyle.h:16:10: fatal error: 'CompactValue.h' file not found #include "CompactValue.h" ^~~~~~~~~~~~~~~~ 1 error generated. ``` `Props.cpp` -> `YogaStylableProps.h` -> `YGStyle.h` -> `CompactValue.h` [`CompactValue.h` is internal private header for Yoga pod](https://github.com/facebook/react-native/blob/4eef075a583224ec9da6bbc4b42bc52508eb31be/ReactCommon/yoga/Yoga.podspec#L54-L56) where `React-Codegen` project cannot access to. ~there are some solutions toward this problem. one way is to make other yoga headers as public headers. i am not sure whether this solution would introduce any side effects. so i only make necessary projects to search yoga private headers.~ Update: a solution is to expose all yoga headers publicly. however, CocoaPods will put all public headers to module umbrella header. this will break YogaKit (swift) integration because swift module doesn't support c++. my pr is trying to expose all yoga headers to `$PODS_ROOT/Headers/Public/Yoga/yoga`, but use custom `module_map` to control which headers should be exposed to the swift module. CocoaPods's custom module_map has some limitation where cannot well support for both `use_frameworks!` mode and non use_frameworks! mode. there's a workaround to use `script_phase` copying the umbrella header to right place. ## Changelog [iOS] [Fixed] - Fix iOS build error when Podfile `generate_multiple_pod_projects=true` and Fabric is on Pull Request resolved: https://github.com/facebook/react-native/pull/33381 Test Plan: verify with rn-tester 1. add `generate_multiple_pod_projects` ```diff --- a/packages/rn-tester/Podfile +++ b/packages/rn-tester/Podfile @@ -5,7 +5,7 @@ platform :ios, '11.0' # Temporary solution to suppress duplicated GUID error. # Can be removed once we move to generate files outside pod install. -install! 'cocoapods', :deterministic_uuids => false +install! 'cocoapods', :generate_multiple_pod_projects => true, :deterministic_uuids => false USE_FRAMEWORKS = ENV['USE_FRAMEWORKS'] == '1' ``` 2. pod install and build rn-tester from xcode Reviewed By: cortinico Differential Revision: D34844041 Pulled By: dmitryrykun fbshipit-source-id: 93311b56d8e44491307a911ad58442f267c979eb --- ReactCommon/yoga/Yoga-umbrella.h | 21 +++++++++++++++++++++ ReactCommon/yoga/Yoga.modulemap | 6 ++++++ ReactCommon/yoga/Yoga.podspec | 24 +++++++++++++++++++----- scripts/react_native_pods.rb | 12 ++++++------ 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 ReactCommon/yoga/Yoga-umbrella.h create mode 100644 ReactCommon/yoga/Yoga.modulemap diff --git a/ReactCommon/yoga/Yoga-umbrella.h b/ReactCommon/yoga/Yoga-umbrella.h new file mode 100644 index 00000000000..2e62c472a22 --- /dev/null +++ b/ReactCommon/yoga/Yoga-umbrella.h @@ -0,0 +1,21 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "yoga/YGEnums.h" +#import "yoga/YGMacros.h" +#import "yoga/YGNode.h" +#import "yoga/YGStyle.h" +#import "yoga/YGValue.h" +#import "yoga/Yoga.h" + +FOUNDATION_EXPORT double yogaVersionNumber; +FOUNDATION_EXPORT const unsigned char yogaVersionString[]; diff --git a/ReactCommon/yoga/Yoga.modulemap b/ReactCommon/yoga/Yoga.modulemap new file mode 100644 index 00000000000..54ea79247e1 --- /dev/null +++ b/ReactCommon/yoga/Yoga.modulemap @@ -0,0 +1,6 @@ +framework module yoga { + umbrella header "Yoga-umbrella.h" + + export * + module * { export * } +} diff --git a/ReactCommon/yoga/Yoga.podspec b/ReactCommon/yoga/Yoga.podspec index fe698a99134..8b2ef89961b 100644 --- a/ReactCommon/yoga/Yoga.podspec +++ b/ReactCommon/yoga/Yoga.podspec @@ -47,11 +47,25 @@ Pod::Spec.new do |spec| # Set this environment variable when *not* using the `:path` option to install the pod. # E.g. when publishing this spec to a spec repo. - source_files = 'yoga/**/*.{cpp,h}' - source_files = File.join('ReactCommon/yoga', source_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION'] + source_files = 'yoga/**/*.{cpp,h}', 'Yoga-umbrella.h' + source_files = source_files.map { |file| File.join('ReactCommon/yoga', file) } if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION'] spec.source_files = source_files - header_files = 'yoga/{Yoga,YGEnums,YGMacros,YGNode,YGStyle,YGValue}.h' - header_files = File.join('ReactCommon/yoga', header_files) if ENV['INSTALL_YOGA_WITHOUT_PATH_OPTION'] - spec.public_header_files = header_files + spec.module_map = 'Yoga.modulemap' + + # CocoaPods custom `module_map` + `header_dir` doesn't put the umbrella header in right place. + # Ideally, modulemap should be placed with the umbrella header, that would work for both use_frameworks! mode and non use_frameworks! mode. + # This script copy the umbrella header back to right place. + spec.script_phase = { + :name => 'Copy umbrella header', + :input_files => ["$PODS_ROOT/Headers/Public/Yoga/yoga/Yoga-umbrella.h"], + :output_files => ["$PODS_ROOT/Headers/Private/Yoga/Yoga-umbrella.h"], + :execution_position => :before_compile, + :shell_path => '/bin/bash', + + # In use_frameworks! mode, the umbrella header will by copied to right place. + # This copy command will fail because "$PODS_ROOT/Headers/Public/Yoga/yoga/Yoga-umbrella.h" doesn't exist. + # The command is just a no-op in use_frameworks! mode. + :script => 'cp -f "$PODS_ROOT/Headers/Public/Yoga/yoga/Yoga-umbrella.h" "$PODS_ROOT/Headers/Private/Yoga/Yoga-umbrella.h" || true', + } end diff --git a/scripts/react_native_pods.rb b/scripts/react_native_pods.rb index 25af80ca960..37f63b6789c 100644 --- a/scripts/react_native_pods.rb +++ b/scripts/react_native_pods.rb @@ -284,12 +284,12 @@ def modify_flags_for_new_architecture(installer, cpp_flags) config_file.save_as(xcconfig_path) end end - # Add RCT_NEW_ARCH_ENABLED to Pods project xcconfig - installer.pods_project.targets.each do |target| - # if target.name == 'React-Core' - if target.name == 'React-Core' - puts "#{target.name}" - target.build_configurations.each do |config| + + # Add RCT_NEW_ARCH_ENABLED to generated pod target projects + installer.target_installation_results.pod_target_installation_results + .each do |pod_name, target_installation_result| + if pod_name == 'React-Core' + target_installation_result.native_target.build_configurations.each do |config| config.build_settings['OTHER_CPLUSPLUSFLAGS'] = cpp_flags end end