diff --git a/packages/core/RNSentry.podspec b/packages/core/RNSentry.podspec index ddff4d305a..9e6f5ae0ef 100644 --- a/packages/core/RNSentry.podspec +++ b/packages/core/RNSentry.podspec @@ -164,10 +164,14 @@ Pod::Spec.new do |s| # # The flag must ride whichever target's link does the stripping, which # depends on linkage: static libs (the default) absorb Sentry into the app - # binary → app link → `user_target_xcconfig`; `:linkage => :dynamic` absorbs + # binary → app link → `user_target_xcconfig`; a dynamic framework absorbs # it into the RNSentry dylib → that link → `pod_target_xcconfig`. Never - # both, or a second copy of Sentry lands in the app. Detected via - # `ENV['USE_FRAMEWORKS']`. + # both, or a second copy of Sentry lands in the app. + # + # Linkage is resolved by `sentry_uses_dynamic_linkage` (see sentry_utils): + # `ENV['USE_FRAMEWORKS']` when set (this repo's CI + the RN/Expo samples), + # otherwise read off the consumer's Podfile so a bare `use_frameworks!` + # without that env var still lands on the right target. force_load_flags = SENTRY_XCFRAMEWORK_SLICES_BY_SDK.each_with_object({}) do |(sdk, slice_ids), acc| loads = slice_ids.map do |slice| %(-force_load "#{File.join(sentry_xcframework_ref, slice, 'Sentry.framework', 'Sentry')}") @@ -177,7 +181,7 @@ Pod::Spec.new do |s| pod_target_xcconfig.merge!(xcframework_search_paths) user_target_xcconfig = xcframework_search_paths.dup - if ENV['USE_FRAMEWORKS'] == 'dynamic' + if sentry_uses_dynamic_linkage pod_target_xcconfig.merge!(force_load_flags) else user_target_xcconfig.merge!(force_load_flags) diff --git a/packages/core/scripts/sentry_utils.rb b/packages/core/scripts/sentry_utils.rb index 8d6e899a71..b5cd61e02c 100644 --- a/packages/core/scripts/sentry_utils.rb +++ b/packages/core/scripts/sentry_utils.rb @@ -233,3 +233,40 @@ def stage_sentry_xcframework_in_pods(xcframework_dir, version, product = 'Sentry end nil end + +# Whether RNSentry (and thus the Sentry static archive it absorbs) is linked +# into a dynamic framework rather than statically into the app. This decides +# which target's link the `-force_load` flag must ride — see the call site in +# `RNSentry.podspec`. +# +# `ENV['USE_FRAMEWORKS']` is an explicit override (the convention the RN and +# Expo sample Podfiles and this repo's CI already use): `dynamic` forces the +# dynamic path, any other value forces static. When it is unset we read the +# actual linkage off the Podfile CocoaPods has already loaded, because a bare +# `use_frameworks!` / `use_frameworks! :linkage => :dynamic` in a consumer's +# Podfile does NOT export that env var — trusting the env var alone would take +# the static path and force-load Sentry into the app while RNSentry is really a +# dylib (a second Sentry copy in the app, and the framework link still strips +# the category). Reading the Podfile closes that gap. +# +# This is a best-effort proxy: the linkage that ultimately governs is +# RNSentry's own pod `build_type`, which the installer only computes after +# `pre_install` hooks run and which isn't available at podspec-eval time. We +# therefore read the declared target linkage, matching (and widening) what the +# env var proxied. Guarded like `stage_sentry_xcframework_in_pods`: outside a +# real `pod install` (`pod ipc spec`, `pod lib lint`) there is no Podfile, so +# we fall back to the static default, which is correct for the RN default. +def sentry_uses_dynamic_linkage + env = ENV['USE_FRAMEWORKS'] + return true if env == 'dynamic' + return false unless env.nil? || env.empty? + + podfile = (Pod::Config.instance.podfile if defined?(Pod::Config)) rescue nil + return false unless podfile + + podfile.target_definition_list.any? do |td| + !td.root? && (td.build_type.dynamic_framework? rescue false) + end +rescue StandardError, NotImplementedError + false +end