Skip to content

Commit

Permalink
Update XCFramework setup to work with React Native 0.71.11 (#5924)
Browse files Browse the repository at this point in the history
* Update Gutenberg reference

* Update script for generating React-Codegen

The method we used to generate React-Codegen (get_react_codegen_spec) was moved to a different file in newer versions of React Native. We need to update the script for generating it accordingly.

* Update third-party podspecs to target 0.71.11

* Update FBReactNativeSpec.podspec.json with hash

* Apply bundle changes

* Manually add React-bridging.podspec.json

The file was automatically deleted via the generate-podspecs.sh script. It has been manually added as a temporary measure, to enable further testing.

* Revert "Manually add React-bridging.podspec.json"

This reverts commit 9707305.

* Update list of dependencies in Podfile for iOS

* Add tag to Hermes podspec to workaround error

This is intended as a temporary workaround, we should implement a more robust fix before merging.

* Update dependency versions in Podfile.lock

* [Temporary] Add React Native as a dependency

* Revert "[Temporary] Add React Native as a dependency"

This reverts commit 21a4fbe.

* Update Gutenberg ref

* Update Gutenberg ref

* Point RNReanimated to a custom branch to work around 2.17.0 issue

The issue is due to RNReanimated looking for react-native in the
node_modules relative to its location, which is how most projects would
be setup. Unfortunately, our projects is not standard.

When running `pod install` or `pod update`, we'd get:

```
Installing RNReanimated 2.17.0 (was 2.9.1-wp-4)
internal/modules/cjs/loader.js:934
  throw err;
  ^

Error: Cannot find module 'react-native/package.json'
Require stack:
- /Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/[eval]
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:931:15)
    at Function.resolve (internal/modules/cjs/helpers.js:113:19)
    at [eval]:1:9
    at Script.runInThisContext (vm.js:134:12)
    at Object.runInThisContext (vm.js:310:38)
    at internal/process/execution.js:81:19
    at [eval]-wrapper:6:22
    at evalScript (internal/process/execution.js:80:60)
    at internal/main/eval_string.js:27:3 {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/[eval]'
  ]
}

[!] Invalid `RNReanimated.podspec` file: no implicit conversion of nil into String.

 #  from /var/folders/dq/cdqxvx3s5ps75564rpmb_dc00000gn/T/d20230627-89428-abwplk/RNReanimated.podspec:5
 #  -------------------------------------------
 #  reanimated_package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))
 >  config = find_config()
 #  assert_no_multiple_instances(config)
 #  -------------------------------------------
```

* Print message during `pod install` about `REACT_NATIVE_NODE_MODULES_DIR`

* Setup NVM and node modules for RNReanimated in XCFramework CI step

* Apply bundle changes

* Switch to using `use_react_native!` in XCFramework `Podfile`

Using only our custom specs, the build failed with

```
Multiple commands produce '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/DerivedData/XCFrameworkScaffold/Build/Products/Debug-iphonesimulator/ReactCommon/ReactCommon.framework/Headers/CallbackWrapper.h'

- Target 'ReactCommon' (project 'Pods') has copy command from '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/Pods/ReactCommon/react/bridging/CallbackWrapper.h' to '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/DerivedData/XCFrameworkScaffold/Build/Products/Debug-iphonesimulator/ReactCommon/ReactCommon.framework/Headers/CallbackWrapper.h'
- Target 'ReactCommon' (project 'Pods') has copy command from '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/Pods/ReactCommon/react/nativemodule/core/ReactCommon/CallbackWrapper.h' to '/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/DerivedData/XCFrameworkScaffold/Build/Products/Debug-iphonesimulator/ReactCommon/ReactCommon.framework/Headers/CallbackWrapper.h'
```

However, with this setup the build fails with:

```
/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/Pods/../../gutenberg/node_modules/react-native/React/FBReactNativeSpec/../../scripts/xcode/with-environment.sh: line 35: .xcode.env: command not found
/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/Pods/../../gutenberg/node_modules/react-native/React/FBReactNativeSpec/../../scripts/xcode/with-environment.sh: line 35: node: command not found
[Warning] You need to configure your node path in the  environment.  You can set it up quickly by running:  echo 'export NODE_BINARY=' > .xcode.env  in the ios folder. This is needed by React Native to work correctly.  We fallback to the DEPRECATED behavior of finding . This will be REMOVED in a future version.  You can read more about this here: https://reactnative.dev/docs/environment-setup#optional-configuring-your-environment
/Users/gio/Developer/a8c/gutenberg-mobile/ios-xcframework/Pods/../../gutenberg/node_modules/react-native/React/FBReactNativeSpec/../../scripts/xcode/with-environment.sh: line 41: /scripts/find-node-for-xcode.sh: No such file or directory
[Error] Could not find node. It looks like that the .xcode.env or .xcode.env.local
```

* Add React Native `post_install` hook

* Use `.xcode.env` instead of `.xcode.env.local`

This way, users can override what CocoaPods does without modifying the
codebase via the `.local` file.

* Bump XCFramework deployment target to iOS 15.0

* Use Xcode 14.3.1 in CI, like WordPress iOS does

Otherwise, we're getting a compilation failure.
See
wordpress-mobile/WordPress-iOS@eadad98#commitcomment-119894859

* Disable Hermes when building XCFramework

* Refine messaging when setting `REACT_NATIVE_NODE_MODULES_DIR` env var

* Set `SKIP_INSTALL = NO` at the `xcconfig` level, too

* Switch an `unless !=` to `if ==`

* Add workaround to setup Hermes correctly in XCFramework

They will take place if the process runs with `HERMES_ENABLED` true.

* Enable building XCFramework with Hermes

* Embed Hermes XCFramework in Gutenberg XCFramework

This is so that, at runtime, apps can find it. Otherwise, the build will
run but the app will fail at runtime with:

```
dyld[99988]: Library not loaded: @rpath/hermes.framework/hermes
  Referenced from: <931F58EC-3AC7-39DE-BEB7-1D44D8A5CF15> /Users/gio/Library/Developer/CoreSimulator/Devices/90F54127-B854-4ED8-93FA-D58167331941/data/Containers/Bundle/Application/3DA26EC7-B308-4029-9974-426839535542/WordPress.app/Frameworks/Gutenberg.framework/Gutenberg
```

* Prevent XCFramework build script from deleting RN-code-generated files

* Update Gutenberg ref

* Set `REACT_NATIVE_NODE_MODULES_DIR` env var before fetching Reanimated pod

* Update `Podfile.lock` file

* Add `.xcode.env` file with dynamic node binary path

---------

Co-authored-by: Siobhan <[email protected]>
Co-authored-by: Carlos Garcia <[email protected]>
  • Loading branch information
3 people authored Jul 6, 2023
1 parent 628f596 commit 30a4548
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 223 deletions.
2 changes: 1 addition & 1 deletion .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ steps:
agents:
queue: mac
env:
IMAGE_ID: xcode-14.3
IMAGE_ID: xcode-14.3.1
15 changes: 15 additions & 0 deletions .buildkite/publish-react-native-ios-artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
echo "--- :arrow_down: Download iOS JS bundle"
buildkite-agent artifact download bundle/ios/App.js .

echo '--- :node: Setup node_modules for RNReanimated'
echo '--- :node: 1. Install nvm'
brew install nvm

echo '--- :node: 2. Load nvm in the current shell'
export NVM_DIR="$HOME/.nvm"
mkdir -p "$NVM_DIR"
[ -s "$HOMEBREW_PREFIX/opt/nvm/nvm.sh" ] && \. "$HOMEBREW_PREFIX/opt/nvm/nvm.sh" --install

echo '--- :node: 3. Install node version from .nvmrc'
nvm install "$(cat .nvmrc)" && nvm use

echo '--- :node: 4. nmp ci'
npm ci

echo "--- :rubygems: Setting up Gems"
cd ./ios-xcframework

Expand Down
4 changes: 4 additions & 0 deletions ios-xcframework/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ fastlane/report.xml

# XCFramework build process intermediate and output artifacts
build

# This file takes precedence over .xcode.env.
# Developers can use it to set a custom value without having to update .xcode.env after every `pod install`
.xcode.env.local
1 change: 1 addition & 0 deletions ios-xcframework/.xcode.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export NODE_BINARY=$(command -v node)
11 changes: 8 additions & 3 deletions ios-xcframework/Config/Gutenberg-Shared.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ INSTALL_PATH = $(LOCAL_LIBRARY_DIR)/Frameworks
// Code will load on this and later versions of iOS. Framework APIs that are unavailable
// in earlier versions will be weak-linked; your code should check for null function
// pointers or specific system versions before calling newer APIs.
IPHONEOS_DEPLOYMENT_TARGET = 14.0
//
// At the moment, this needs to be the same value as what's used by the consumer (Jetpack and WordPress iOS)
// See https://github.com/wordpress-mobile/WordPress-iOS/commit/eadad98d81c8970144707d8967f8c9f06a6ada38#commitcomment-119894859
IPHONEOS_DEPLOYMENT_TARGET = 15.0
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
// Marketing Version
//
Expand All @@ -78,7 +81,6 @@ MTL_FAST_MATH = YES
PRODUCT_BUNDLE_IDENTIFIER = org.wordpress.Gutenberg
PRODUCT_NAME = $(TARGET_NAME:c99extidentifier)
SDKROOT = iphoneos
SKIP_INSTALL = YES
SWIFT_EMIT_LOC_STRINGS = YES
// Swift Language Version
//
Expand All @@ -87,12 +89,15 @@ SWIFT_VERSION = 5.0
TARGETED_DEVICE_FAMILY = 1,2
VERSION_INFO_PREFIX =
VERSIONING_SYSTEM = apple-generic
// This should be unnecessary because the script that builds the project should set it.
// These should be unnecessary because the script that builds the project should set it.
//
// But I'm getting the following error after switching from Xcode 14.3 to 14.3.1:
//
// > Failed to build module 'Gutenberg'; this SDK is not supported by the compiler
// > (the SDK is built with 'Apple Swift version 5.8 (swiftlang-5.8.0.124.2 clang-1403.0.22.11.100)',
// > while this compiler is 'Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)').
// > Please select a toolchain which matches the SDK.
//
// See also https://github.com/facebook/facebook-ios-sdk/issues/2180#issuecomment-1485846511
BUILD_LIBRARY_FOR_DISTRIBUTION = YES
SKIP_INSTALL = NO
99 changes: 62 additions & 37 deletions ios-xcframework/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,25 @@

require 'xcodeproj'

REACT_NATIVE_PATH = '../gutenberg/node_modules/react-native'
require_relative File.join(REACT_NATIVE_PATH, 'scripts', 'react_native_pods')

# We are still trying to decide whether to adopt Hermes or not.
#
# This switch allows us to switch between approaches on the go.
HERMES_ENABLED = ENV.fetch('HERMES_ENABLED', true)

puts "[Gutenberg] Installing pods with Hermes #{HERMES_ENABLED ? 'enabled' : 'disabled'}"

# Note that the pods in this array might seem unused if you look for
# `import` statements in this codebase. However, make sure to also check
# whether they are used in the gutenberg-mobile and Gutenberg projects.
#
# Also notice that these are not all the dependencies the project uses.
# Later in the config, we call use_react_native! which fetches more.
#
# See https://github.com/wordpress-mobile/gutenberg-mobile/issues/5025
DEPENDENCIES = %w[
FBLazyVector
React
ReactCommon
RCTRequired
RCTTypeSafety
React-Core
React-CoreModules
React-RCTActionSheet
React-RCTAnimation
React-RCTBlob
React-RCTImage
React-RCTLinking
React-RCTNetwork
React-RCTSettings
React-RCTText
React-RCTVibration
React-callinvoker
React-cxxreact
React-jsinspector
React-jsi
React-jsiexecutor
React-logger
React-perflogger
React-runtimeexecutor
boost
Yoga
RCT-Folly
glog
react-native-safe-area
react-native-safe-area-context
react-native-video
Expand All @@ -51,21 +36,31 @@ DEPENDENCIES = %w[
RNCMaskedView
RNCClipboard
RNFastImage
React-Codegen
React-jsc
React-hermes
].freeze

def gutenberg_dependencies
podspec_prefix = '..'

# FBReactNativeSpec needs special treatment because of react-native-codegen code generation
pod 'FBReactNativeSpec',
podspec: "#{podspec_prefix}/third-party-podspecs/FBReactNativeSpec/FBReactNativeSpec.podspec.json"
computed_dependencies = DEPENDENCIES.dup

# Use a custom RNReanimated version while we coordinate a fix upstream
computed_dependencies.delete('RNReanimated')

DEPENDENCIES.each do |pod_name|
computed_dependencies.delete('React-jsc') unless HERMES_ENABLED

computed_dependencies.each do |pod_name|
pod pod_name, podspec: "#{podspec_prefix}/third-party-podspecs/#{pod_name}.podspec.json"
end

# This is required to workaround an issue with RNReanimated after upgrading to version 2.17.0
rn_node_modules = File.join(Dir.pwd, '..', 'gutenberg', 'node_modules')
raise "Could not find node modules at given path #{rn_node_modules}" unless File.exist? rn_node_modules
ENV['REACT_NATIVE_NODE_MODULES_DIR'] = rn_node_modules
puts "[Gutenberg] Set REACT_NATIVE_NODE_MODULES_DIR env var for RNReanimated to #{rn_node_modules}"

# Use a custom RNReanimated version while we coordinate a fix upstream
pod 'RNReanimated', git: 'https://github.com/wordpress-mobile/react-native-reanimated', branch: 'wp-fork-2.17.0'
end

VERSION_XCCONFIG_PATH = File.join(File.expand_path(__dir__), 'Config', 'Gutenberg-Shared.xcconfig')
Expand All @@ -74,21 +69,51 @@ APP_IOS_DEPLOYMENT_TARGET = Gem::Version.new(Xcodeproj::Config.new(VERSION_XCCON

platform :ios, APP_IOS_DEPLOYMENT_TARGET.version

target 'Gutenberg' do
use_frameworks! linkage: :static
# It's important to call use_frameworks! before use_react_native!
#
# See https://github.com/facebook/react-native/issues/36120#issuecomment-1425892304
use_frameworks! linkage: :static
use_react_native! path: REACT_NATIVE_PATH, hermes_enabled: HERMES_ENABLED

target 'Gutenberg' do
pod 'RNTAztecView', path: '..'
gutenberg_dependencies
end

post_install do |installer|
react_native_post_install(installer, REACT_NATIVE_PATH)

installer.pods_project.targets.each do |target|
# Work around issue with embedding the Hermes XCFramework
#
# See https://github.com/facebook/react-native/issues/35863
if HERMES_ENABLED && target.name == 'hermes-engine'
installer.pods_project.files.each do |fileref|
next unless fileref.path.end_with? 'hermes.xcframework'

plist_buddy = '/usr/libexec/PlistBuddy'

raise "[Gutenberg] Could not find PlistBuddy at #{plist_buddy}." unless File.exist?(plist_buddy)

hermes_plist_file = "#{fileref.real_path}/Info.plist"

# Patch Hermes to remove the debug symbols entry from the Info.plist (as it's not shipped with it)
# This might be removed once Hermes starts to ship with Debug symbols or we remove our
# direct dependency from the Main iOS target on "hermes.xcframework"
Open3.capture3(plist_buddy, '-c', 'Delete :AvailableLibraries:0:DebugSymbolsPath', hermes_plist_file)
Open3.capture3(plist_buddy, '-c', 'Delete :AvailableLibraries:1:DebugSymbolsPath', hermes_plist_file)
Open3.capture3(plist_buddy, '-c', 'Delete :AvailableLibraries:2:DebugSymbolsPath', hermes_plist_file)

puts '[Gutenberg] Removed Hermes dSYMs references from its XCFramework'
end
end

# Let Pods targets inherit deployment target from the app
# See https://github.com/CocoaPods/CocoaPods/issues/4859
#
# Exclude RCT-Folly as it requires explicit deployment target
# See https://git.io/JPb73
next unless target.name != 'RCT-Folly'
next if target.name == 'RCT-Folly'

target.build_configurations.each do |configuration|
pod_ios_deployment_target = Gem::Version.new(configuration.build_settings[IOS_VERSION_KEY])
Expand Down
Loading

0 comments on commit 30a4548

Please sign in to comment.