Skip to content

Commit

Permalink
Fix static linking on iOS (#6614)
Browse files Browse the repository at this point in the history
Fixes
#6607.

This is more of a workaround to fix issue with missing headers when
building Reanimated with static linkage for iOS. I spent several hours
on trying to make it right but it would work correctly only partially. I
will post my findings here or internally later on and I hope to find a
better solution sometime in the future.

This workaround adds paths to `Common/cpp` and `apple/` directories to
`HEADER_SEARCH_PATHS` – the preprocessor looks for headers in these
locations. I can't use an absolute path (calculated in Ruby) because it
would make the output setup-dependent and thus affect checksums in
`Podfile.lock` which is unwanted as some apps check against checksum
changes on CI.

Tested on fabric-example app with `USE_FRAMEWORKS=static` and without.

---

When installing pods in `fabric-example` with `cd ios && bundle install
&& bundle exec pod install`, the headers are visible in `ios/Pods`
directory:

```
$ find . -name "REAUIKit.h"
./Pods/Headers/Public/RNReanimated/reanimated/apple/REAUIKit.h
./Pods/Headers/Private/RNReanimated/reanimated/apple/REAUIKit.h
```

However, after running `USE_FRAMEWORKS=static bundle exec pod install`,
the headers are no longer present:

```
$ find . -name "REAUIKit.h"
<no output>
```

Because of this, `#import <reanimated/apple/REAUIKit.h>` doesn't work,
but when changed back to `#import <RNReanimated/REAUIKit.h>` it works
fine (jump to file also works in Xcode).

This made me wonder what's the location of `REAUIKit.h`. Obviously, the
file is located in
`react-native-reanimated/packages/react-native-reanimated/apple/reanimated/apple/REAUIKit.h`
(using symlinks as a Development Pod), but the path is not included in
header search paths. Hence, I decided to add
`react-native-reanimated/packages/react-native-reanimated/apple/` to
header search paths. However, this can't be done using absolute paths
because they are likely to be machine-specific (e.g. containing the home
directory name) which also affects checksum in Podfile.lock which is
inconvenient since some setups assume the checksum to be stable (unless
the version of the library is changed) due to security concerns.

A better idea would be to take inspiration from react-native itself.
I've noticed that `ReactCommon` has a similar feature – the headers are
in nested subdirectories (e.g. `react/renderer/core/ShadowNode.h`) and
the imports don't assume flat structure (e.g. it's `#import
<react/renderer/core/ShadowNode.h>` instead of `#import
<ReactCommon/ShadowNode.h>`).

I remembered that frameworks create `FrameworkName.framework`
directories. I finally found `RNReanimated.framework` in Xcode build
folder that you can open from menu bar:

<img width="312" alt="Screenshot 2024-10-18 at 18 04 45"
src="https://github.com/user-attachments/assets/1db95b44-d21c-4e9a-a1d5-19674093094b">

Taking a quick look inside and comparing `ReactCommon` and
`RNReanimated`, I've noticed that `RNReanimated.framework` doesn't
contain any header files (except for umbrella header) while
`ReactCommon` does:

<img width="1178" alt="Screenshot 2024-10-18 at 18 05 33"
src="https://github.com/user-attachments/assets/2633ab64-a85e-4a04-b2b3-97a11e576d6c">

When I commented out all 3 occurrences of `header_mappings_dir` in
RNReanimated.podspec and run the Xcode build (first build lasts until
the first error, if you hit the play button once again then, I assume it
runs the remaining tasks), the headers were finally there but the
directory structure was flattened:

```diff
-ss.header_mappings_dir = "Common/cpp/reanimated"
+#ss.header_mappings_dir = "Common/cpp/reanimated"
```

<img width="1178" alt="Screenshot 2024-10-18 at 18 14 52"
src="https://github.com/user-attachments/assets/c25c1dfe-ce45-45ff-856f-be005907ffe2">

Then I tried passing an absolute path to appropriate directories to see
if this would fix the structure:

```diff
-ss.header_mappings_dir = "Common/cpp/reanimated"
+ss.header_mappings_dir = "/Users/tomekzaw/RNOS/react-native-reanimated/packages/react-native-reanimated/Common/cpp"

-sss.header_mappings_dir = "apple/reanimated"
+sss.header_mappings_dir = "/Users/tomekzaw/RNOS/react-native-reanimated/packages/react-native-reanimated/

-ss.header_mappings_dir = "Common/cpp/worklets"
+ss.header_mappings_dir = "/Users/tomekzaw/RNOS/react-native-reanimated/packages/react-native-reanimated/Common/cpp"
```

Then I investigated `ReactCommon.podspec` inside react-native repository
and found several interesting lines of code:

```rb
s.header_dir = "ReactCommon" # Use global header_dir for all subspecs for use_frameworks! compatibility
```

It looks like `header_dir` must contain `header_mappings_dir` for the
latter to work properly when `use_frameworks!` is enabled.

```rb
if ENV['USE_FRAMEWORKS']
  s.header_mappings_dir     = './'
end
```
For some reason, `s.header_mappings_dir` is set to the current directory
only if `USE_FRAMEWORKS` is set.

...

```
HEADER_SEARCH_PATHS = (
	"$(inherited)",
	"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
	"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
	"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
	"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
	"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
	"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
	"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
	"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
);
```
  • Loading branch information
tomekzaw committed Oct 21, 2024
1 parent 969bad6 commit 3bf4cd8
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 5 deletions.
2 changes: 1 addition & 1 deletion apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2229,7 +2229,7 @@ SPEC CHECKSUMS:
RNCPicker: d051e0647af8b2ad01a3d39a6b5dd9b7c0ccc166
RNFlashList: 6f169ad83e52579b7754cbbcec1b004c27d82c93
RNGestureHandler: c374c750a0a9bacd95f5c740d146ab9428549d6b
RNReanimated: 3eda8373a520a38427b68054026b5a8d40d42cfe
RNReanimated: 648a88c56c6881c93255e0eab431859b346e0b42
RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2
RNSVG: 08750404f92a36162a92522cc77dee437be1d257
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Expand Down
2 changes: 1 addition & 1 deletion apps/macos-example/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,7 @@ SPEC CHECKSUMS:
RNCAsyncStorage: ec53e44dc3e75b44aa2a9f37618a49c3bc080a7a
RNCPicker: 0173dedc74776227ec6dcc61bb85cd9f07bbb2ac
RNGestureHandler: bb81850add626ddd265294323310fec6e861c96b
RNReanimated: 857daa16ccc117c0491ad6950c9579cb436295df
RNReanimated: 8e5c163219a6444172d851df401a1e86721635f0
RNSVG: 01eb8d8a0e2289ec3ecc9626ce920e00d2174992
SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9
Yoga: 329461de6a23b9e0c108d197fd0f6e87c8c8ecf2
Expand Down
2 changes: 1 addition & 1 deletion apps/paper-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ SPEC CHECKSUMS:
RNCPicker: 0173dedc74776227ec6dcc61bb85cd9f07bbb2ac
RNFlashList: 115dd44377580761bff386a0caebf165424cf16f
RNGestureHandler: 6dfe7692a191ee224748964127114edf057a1475
RNReanimated: 3ffdf9d59cc891c4cbd29b5182ed845aea6ec187
RNReanimated: ac6bc4e337cfc694308e094f1d73487c21832bc8
RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06
RNSVG: 01eb8d8a0e2289ec3ecc9626ce920e00d2174992
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Expand Down
2 changes: 1 addition & 1 deletion apps/tvos-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,7 @@ SPEC CHECKSUMS:
React-runtimescheduler: 20b2202e3396589a71069d12ae9f328949c7c7b8
React-utils: 0307d396f233e47a167b5aaf045b0e4e1dc19d74
ReactCommon: 17891ca337bfa5a7263649b09f27a8c664537bf2
RNReanimated: fe28ce0f9e7a41852e6b0e0b5ac557236ae39db9
RNReanimated: 0552e8e7058dfe736c41ce163c0cd66eaf0ca906
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: e7f2a2256464d4ef7b3825d216bd22aac3b449c1

Expand Down
12 changes: 11 additions & 1 deletion packages/react-native-reanimated/RNReanimated.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,17 @@ Pod::Spec.new do |s|
}
s.compiler_flags = "#{folly_compiler_flags} #{boost_compiler_flags}"
s.xcconfig = {
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/glog\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/Headers/Public/React-hermes\" \"$(PODS_ROOT)/Headers/Public/hermes-engine\" \"$(PODS_ROOT)/#{$config[:react_native_common_dir]}\"",
"HEADER_SEARCH_PATHS" => [
'"$(PODS_ROOT)/boost"',
'"$(PODS_ROOT)/boost-for-react-native"',
'"$(PODS_ROOT)/glog"',
'"$(PODS_ROOT)/RCT-Folly"',
'"$(PODS_ROOT)/Headers/Public/React-hermes"',
'"$(PODS_ROOT)/Headers/Public/hermes-engine"',
"\"$(PODS_ROOT)/#{$config[:react_native_common_dir]}\"",
"\"$(PODS_ROOT)/#{$config[:react_native_reanimated_dir_from_pods_root]}/apple\"",
"\"$(PODS_ROOT)/#{$config[:react_native_reanimated_dir_from_pods_root]}/Common/cpp\"",
].join(' '),
"OTHER_CFLAGS" => "$(inherited) #{folly_flags} #{fabric_flags} #{example_flag} #{version_flag} #{debug_flag} #{compilation_metadata_generation_flag}"
}
s.requires_arc = true
Expand Down
5 changes: 5 additions & 0 deletions packages/react-native-reanimated/scripts/reanimated_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def find_config()
:is_tvos_target => nil,
:react_native_node_modules_dir => nil,
:react_native_common_dir => nil,
:react_native_reanimated_dir_from_pods_root => nil,
}

react_native_node_modules_dir = File.join(File.dirname(`cd "#{Pod::Config.instance.installation_root.to_s}" && node --print "require.resolve('react-native/package.json')"`), '..')
Expand Down Expand Up @@ -43,6 +44,10 @@ def find_config()
react_native_common_dir_relative = Pathname.new(react_native_common_dir_absolute).relative_path_from(pods_root).to_s
result[:react_native_common_dir] = react_native_common_dir_relative

react_native_reanimated_dir_absolute = File.join(__dir__, '..')
react_native_reanimated_dir_relative = Pathname.new(react_native_reanimated_dir_absolute).relative_path_from(pods_root).to_s
result[:react_native_reanimated_dir_from_pods_root] = react_native_reanimated_dir_relative

return result
end

Expand Down

0 comments on commit 3bf4cd8

Please sign in to comment.