diff --git a/.generated.NoMobile.slnx b/.generated.NoMobile.slnx index d6fd58d14f..babff9ea4d 100644 --- a/.generated.NoMobile.slnx +++ b/.generated.NoMobile.slnx @@ -54,7 +54,6 @@ - diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index 5a5fe3c5c0..ec35ae3f12 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -37,7 +37,6 @@ on: - 'lib/sentry-android-supplemental/**' - 'modules/sentry-native/**' - 'modules/sentry-cocoa/**' - - 'modules/sentry-cocoa.properties' # Build configuration (affects all builds) - 'global.json' - 'Directory.Build.props' diff --git a/.github/workflows/device-tests-ios.yml b/.github/workflows/device-tests-ios.yml index 7d7bfdb0b1..749827140b 100644 --- a/.github/workflows/device-tests-ios.yml +++ b/.github/workflows/device-tests-ios.yml @@ -37,7 +37,6 @@ on: - 'lib/sentry-android-supplemental/**' - 'modules/sentry-native/**' - 'modules/sentry-cocoa/**' - - 'modules/sentry-cocoa.properties' # Build configuration (affects all builds) - 'global.json' - 'Directory.Build.props' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc60035091..7553b5caad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -194,27 +194,26 @@ should be updated from the main branch and the `modules/make-internal.sh` script should reference the most recent commit on the `internal` branch of Ben.Demystifier then (functionally identical to the main branch - the only difference being the changes to member visibility). -## Local Sentry Cocoa SDK checkout +## Sentry Cocoa SDK checkout -By default, `Sentry.Bindings.Cocoa` downloads a pre-built Sentry Cocoa SDK from -GitHub Releases. The version is specified in `modules/sentry-cocoa.properties`. +`Sentry.Bindings.Cocoa` always builds the Sentry Cocoa SDK from source, from the +[getsentry/sentry-cocoa](https://github.com/getsentry/sentry-cocoa/) submodule at +`modules/sentry-cocoa` (`scripts/build-sentry-cocoa.sh`, invoked automatically by +the build). Pre-built release artifacts can't be used: the `SentryObjC` hybrid-API +frameworks are only published as self-contained bundles that would embed a second +copy of the SDK alongside `Sentry.framework` (see +[#5331](https://github.com/getsentry/sentry-dotnet/issues/5331)). -If you want to build an unreleased Sentry Cocoa SDK version from source instead, -replace the pre-built SDK with [getsentry/sentry-cocoa](https://github.com/getsentry/sentry-cocoa/) -by cloning it into the `modules/sentry-cocoa` directory: +To build against a different Cocoa SDK version, check out the desired ref in the +submodule **and stage it** — the solution build automatically runs +`git submodule update` (see `before.Sentry.sln.targets`), which reverts the +submodule to the pinned commit unless the index already records your ref: ```sh -$ rm -rf modules/sentry-cocoa -$ gh repo clone getsentry/sentry-cocoa modules/sentry-cocoa -$ dotnet build ... # uses modules/sentry-cocoa as is -``` - -To switch back to the pre-built SDK, delete the `modules/sentry-cocoa` directory -and let the next build download the pre-built SDK again: - -```sh -$ rm -rf modules/sentry-cocoa -$ dotnet build ... # downloads pre-built Cocoa SDK into modules/sentry-cocoa +$ git -C modules/sentry-cocoa fetch origin +$ git -C modules/sentry-cocoa checkout +$ git add modules/sentry-cocoa # otherwise the build restores the pinned commit +$ dotnet build ... # rebuilds the Cocoa SDK from the new ref ``` ## Local Sentry Android SDK checkout diff --git a/Sentry.slnx b/Sentry.slnx index d6fd58d14f..babff9ea4d 100644 --- a/Sentry.slnx +++ b/Sentry.slnx @@ -54,7 +54,6 @@ - diff --git a/integration-test/cli.Tests.ps1 b/integration-test/cli.Tests.ps1 index d03d5c235a..e02e69b66d 100644 --- a/integration-test/cli.Tests.ps1 +++ b/integration-test/cli.Tests.ps1 @@ -191,7 +191,9 @@ Describe 'MAUI ()' -ForEach @( 'Microsoft.Maui.Essentials.pdb', 'Microsoft.Maui.Graphics.pdb', 'Microsoft.Maui.pdb', - 'Sentry' + 'Sentry', + 'SentryObjC', + 'SentryObjCCompat' ) # The specific number of debug information files seems to change with different SDK - so we just check for non-zero $nonZeroNumberRegex = '[1-9][0-9]*'; diff --git a/scripts/build-sentry-cocoa.sh b/scripts/build-sentry-cocoa.sh index f3ef2b7558..4c78b66728 100755 --- a/scripts/build-sentry-cocoa.sh +++ b/scripts/build-sentry-cocoa.sh @@ -1,6 +1,11 @@ #!/bin/bash set -euo pipefail +# Include this script's own hash in the build stamp so cached output is rebuilt whenever the +# recipe changes (e.g. when new frameworks are added to the build), not just when the +# sentry-cocoa submodule moves. Mirrors the cache key used for sentry-native in CI. +script_checksum=$(shasum -a 256 "$0" | cut -d ' ' -f 1) + pushd "$(dirname "$0")" >/dev/null cd ../modules/sentry-cocoa @@ -24,8 +29,8 @@ while ! ln "$TMP_FILE" "$PID_FILE" 2>/dev/null; do done rm -f "$TMP_FILE" -current_sha=$(git rev-parse HEAD) -if [[ -f Carthage/.built-from-sha ]] && [[ "$(cat Carthage/.built-from-sha)" == "$current_sha" ]]; then +build_stamp="$(git rev-parse HEAD) $script_checksum" +if [[ -f Carthage/.built-from-sha ]] && [[ "$(cat Carthage/.built-from-sha)" == "$build_stamp" ]]; then popd >/dev/null exit 0 fi @@ -67,6 +72,38 @@ xcodebuild -create-xcframework \ -output ./Carthage/Build-ios/Sentry.xcframework echo "::endgroup::" +# The SentryObjC scheme adds the structured hybrid API (SentryObjCSDK.internal), which the .NET +# bindings use in place of the deprecated PrivateSentrySDKOnly. It produces two thin frameworks - +# SentryObjC and SentryObjCCompat - that dynamically link the Sentry.framework built above (they do +# not embed their own copy of the SDK), so we bundle them alongside Sentry.xcframework. We build +# these from source rather than downloading the pre-built SentryObjC-Dynamic.xcframework because +# that release artifact is self-contained (it embeds the whole SDK) and would duplicate Sentry. +echo "::group::Building SentryObjC for iOS and iOS simulator" +xcodebuild archive -project Sentry.xcodeproj \ + -scheme SentryObjC \ + -configuration Release \ + -sdk "$ios_sdk" \ + -archivePath ./Carthage/output-objc-ios.xcarchive \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + GCC_PREPROCESSOR_DEFINITIONS='$(inherited) SENTRY_CRASH_MANAGED_RUNTIME=1' +./scripts/remove-architectures.sh ./Carthage/output-objc-ios.xcarchive arm64e +xcodebuild archive -project Sentry.xcodeproj \ + -scheme SentryObjC \ + -configuration Release \ + -sdk "$ios_simulator_sdk" \ + -archivePath ./Carthage/output-objc-iossimulator.xcarchive \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + GCC_PREPROCESSOR_DEFINITIONS='$(inherited) SENTRY_CRASH_MANAGED_RUNTIME=1' +for fw in SentryObjC SentryObjCCompat; do + xcodebuild -create-xcframework \ + -framework "./Carthage/output-objc-ios.xcarchive/Products/Library/Frameworks/$fw.framework" \ + -framework "./Carthage/output-objc-iossimulator.xcarchive/Products/Library/Frameworks/$fw.framework" \ + -output "./Carthage/Build-ios/$fw.xcframework" +done +echo "::endgroup::" + # Separately, build for Mac Catalyst echo "::group::Building sentry-cocoa for Mac Catalyst" xcodebuild archive -project Sentry.xcodeproj \ @@ -83,15 +120,41 @@ xcodebuild -create-xcframework \ -output ./Carthage/Build-maccatalyst/Sentry.xcframework echo "::endgroup::" +echo "::group::Building SentryObjC for Mac Catalyst" +xcodebuild archive -project Sentry.xcodeproj \ + -scheme SentryObjC \ + -configuration Release \ + -destination 'generic/platform=macOS,variant=Mac Catalyst' \ + -archivePath ./Carthage/output-objc-maccatalyst.xcarchive \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + GCC_PREPROCESSOR_DEFINITIONS='$(inherited) SENTRY_CRASH_MANAGED_RUNTIME=1' +./scripts/remove-architectures.sh ./Carthage/output-objc-maccatalyst.xcarchive arm64e +for fw in SentryObjC SentryObjCCompat; do + xcodebuild -create-xcframework \ + -framework "./Carthage/output-objc-maccatalyst.xcarchive/Products/Library/Frameworks/$fw.framework" \ + -output "./Carthage/Build-maccatalyst/$fw.xcframework" +done +echo "::endgroup::" + +# Xcode embeds each framework's dynamic dependencies under .framework/Frameworks/ (e.g. +# SentryObjC.framework/Frameworks/SentryObjCCompat.framework/Frameworks/Sentry.framework). We bundle +# Sentry, SentryObjCCompat and SentryObjC as separate NativeReferences - each embedded into the +# consuming app - so those nested copies are redundant, and their deep paths blow past NuGet's path +# length limit (NU5123). To fix that, we strip them. The frameworks resolve each other via @rpath at the app level instead/anyway. +find Carthage/Build-*/SentryObjC*.xcframework -type d -name Frameworks -prune -exec rm -rf {} + + # Copy headers - used for generating bindings mkdir Carthage/Headers find Carthage/Build-ios/Sentry.xcframework/ios-arm64 -name '*.h' -exec cp {} Carthage/Headers \; +find Carthage/Build-ios/SentryObjC.xcframework/ios-arm64 -name '*.h' -exec cp {} Carthage/Headers \; +find Carthage/Build-ios/SentryObjCCompat.xcframework/ios-arm64 -name '*.h' -exec cp {} Carthage/Headers \; # Remove anything we don't want to bundle in the nuget package. find Carthage/Build* \( -name Headers -o -name PrivateHeaders -o -name Modules \) -exec rm -rf {} + rm -rf Carthage/output-* -echo "$current_sha" > Carthage/.built-from-sha +echo "$build_stamp" > Carthage/.built-from-sha echo "" popd >/dev/null diff --git a/scripts/generate-cocoa-bindings.ps1 b/scripts/generate-cocoa-bindings.ps1 index 1c102f3b17..e1e368d73a 100644 --- a/scripts/generate-cocoa-bindings.ps1 +++ b/scripts/generate-cocoa-bindings.ps1 @@ -6,18 +6,9 @@ $PSNativeCommandUseErrorActionPreference = $true $RootPath = (Get-Item $PSScriptRoot).Parent.FullName $CocoaSdkPath = "$RootPath/modules/sentry-cocoa" -if (Test-Path "$CocoaSdkPath/.git") -{ - # Cocoa SDK cloned to modules/sentry-cocoa for local development - $HeadersPath = "$CocoaSdkPath/Carthage/Headers" - $PrivateHeadersPath = "$CocoaSdkPath/Carthage/Headers" -} -else -{ - # Cocoa SDK downloaded from GitHub releases and extracted into modules/sentry-cocoa - $HeadersPath = "$CocoaSdkPath/Sentry.framework/Headers" - $PrivateHeadersPath = "$CocoaSdkPath/Sentry.framework/PrivateHeaders" -} +# The Cocoa SDK is built from source from the modules/sentry-cocoa submodule; +# build-sentry-cocoa.sh copies the headers here. +$HeadersPath = "$CocoaSdkPath/Carthage/Headers" $BindingsPath = "$RootPath/src/Sentry.Bindings.Cocoa" $BackupPath = "$BindingsPath/obj/_unpatched" @@ -164,12 +155,18 @@ else } # Generate bindings +# The SentryObjC*.h headers expose the structured hybrid API (SentryObjCSDK.internal) Write-Output 'Generating bindings with Objective Sharpie.' sharpie bind -sdk $iPhoneSdkVersion ` -scope "$CocoaSdkPath" ` "$HeadersPath/Sentry.h" ` "$HeadersPath/Sentry-Swift.h" ` - "$HeadersPath/PrivateSentrySDKOnly.h" ` + "$HeadersPath/SentryObjCSDK.h" ` + "$HeadersPath/SentryObjCInternalApi.h" ` + "$HeadersPath/SentryObjCInternalSdkApi.h" ` + "$HeadersPath/SentryObjCInternalProfilingApi.h" ` + "$HeadersPath/SentryObjCId.h" ` + "$HeadersPath/SentryObjCSpanId.h" ` -o $BindingsPath ` -c -Wno-objc-property-no-attribute ` -F"$iPhoneSdkPath/System/Library/SubFrameworks" # needed for UIUtilities.framework in Xcode 26+ diff --git a/scripts/patch-cocoa-bindings.cs b/scripts/patch-cocoa-bindings.cs index 6d6eb801e7..b9edea2de5 100644 --- a/scripts/patch-cocoa-bindings.cs +++ b/scripts/patch-cocoa-bindings.cs @@ -57,7 +57,6 @@ .PropertyToMethod("Sentry*", "Serialize") .PropertyToMethod("SentrySpan", "ToTraceHeader") .PropertyToMethod("SentryTraceContext", "ToBaggage") - .PropertyToMethod("PrivateSentrySDKOnly", "Capture*") // Verify the rest .VerifyProperty("*Sentry*", "*", "MethodToProperty") // TODO: replace broad patterns with one-by-one verification .VerifyProperty("SentryOptions", "*Targets", "StronglyTypedNSArray") @@ -81,11 +80,6 @@ .WithAttribute("SentryBeforeBreadcrumbCallback", "return: NullAllowed") .WithAttribute("SentryBeforeSendEventCallback", "return: NullAllowed") .WithAttribute("SentryTracesSamplerCallback", "return: NullAllowed") - // Fix nullable return attributes - .RemoveAttribute("PrivateSentrySDKOnly", "CaptureScreenshots", "NullAllowed") - .RemoveAttribute("PrivateSentrySDKOnly", "CaptureViewHierarchy", "NullAllowed") - .WithAttribute("PrivateSentrySDKOnly", "CaptureScreenshots", "return: NullAllowed") - .WithAttribute("PrivateSentrySDKOnly", "CaptureViewHierarchy", "return: NullAllowed") // Fix nullable property attributes .WithPropertyAttribute("SentryOptions", "OnCrashedLastRun", "NullAllowed") // Fix nullable generic type arguments @@ -104,10 +98,6 @@ .RemoveMethod("Sentry*", "CopyWithZone") // error CS0111: Type 'SentryAttribute' already defines a member called 'Constructor' with the same parameter types .RemoveMethod("SentryLog", "SetAttribute") - // SentryEnvelope* is not whitelisted - .RemoveMethod("PrivateSentrySDKOnly", "CaptureEnvelope") - .RemoveMethod("PrivateSentrySDKOnly", "EnvelopeWithData") - .RemoveMethod("PrivateSentrySDKOnly", "StoreEnvelope") // SentryLoggerDelegate and SentryCurrentDateProvider are not whitelisted .RemoveMethod("SentryLogger", "Constructor") // SentryAppStartMeasurement is not whitelisted @@ -116,17 +106,24 @@ .RemoveDelegate("SentryUserFeedbackConfigurationBlock") // error CS0114: 'SentryXxx.Description' hides inherited member 'NSObject.Description'. .RemoveProperty("Sentry*", "Description") - // SentryAppStartMeasurement is not whitelisted - .RemoveProperty("PrivateSentrySDKOnly", "*AppStartMeasurement*") // Minimize SentryDependencyContainer .RemoveMethod("SentryDependencyContainer", "*") .KeepProperties("SentryDependencyContainer", "SharedInstance", "DebugImageProvider") // SentryUserFeedbackConfiguration is not whitelisted .RemoveProperty("SentryOptions", "ConfigureUserFeedback") .RemoveProperty("SentryOptions", "UserFeedbackConfiguration") + // SentryObjCSDK.internal is the entry point to the API for hybrid SDKs + // We only bind the `internal` accessor; every other SentryObjCSDK member references SentryObjC* + // types we don't whitelist. + .KeepProperties("SentryObjCSDK", "Internal") + .RemoveMethod("SentryObjCSDK", "*") + .KeepProperties("SentryObjCInternalApi", "Sdk", "Profiling") + .KeepMethods("SentryObjCInternalApi", "SetTrace", "IgnoreNextSignal") + // Sharpie generates enums for types the SentryObjC headers reference, but the members that used + // them are trimmed above - drop the dead enums + .RemoveEnum("SentryObjC*") .KeepInterfaces( "ISentryRRWebEvent", - "PrivateSentrySDKOnly", "SentryAttachment", "SentryBaggage", "SentryBreadcrumb", @@ -155,6 +152,12 @@ "SentryMechanismContext", "SentryMessage", "SentryNSError", + "SentryObjCId", + "SentryObjCInternalApi", + "SentryObjCInternalProfilingApi", + "SentryObjCInternalSdkApi", + "SentryObjCSDK", + "SentryObjCSpanId", "SentryOptions", "SentryProfileOptions", "SentryRedactOptions", @@ -251,6 +254,13 @@ public static CompilationUnitSyntax RemoveClass( return root.RemoveByPredicate(node => node.Identifier.Matches(name)); } + public static CompilationUnitSyntax RemoveEnum( + this CompilationUnitSyntax root, + string name) + { + return root.RemoveByPredicate(node => node.Identifier.Matches(name)); + } + public static CompilationUnitSyntax RemoveDelegate( this CompilationUnitSyntax root, string name) diff --git a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs index 8b0245cfbd..f9d79d19db 100644 --- a/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs +++ b/src/Sentry.Bindings.Cocoa/ApiDefinitions.cs @@ -1517,172 +1517,6 @@ interface SentryUser : SentrySerializable nuint Hash { get; } } -// @interface PrivateSentrySDKOnly : NSObject -[BaseType(typeof(NSObject))] -[Internal] -interface PrivateSentrySDKOnly -{ - - // +(void)setSdkName:(NSString * _Nonnull)sdkName andVersionString:(NSString * _Nonnull)versionString; - [Static] - [Export("setSdkName:andVersionString:")] - void SetSdkName(string sdkName, string versionString); - - // +(void)setSdkName:(NSString * _Nonnull)sdkName; - [Static] - [Export("setSdkName:")] - void SetSdkName(string sdkName); - - // +(NSString * _Nonnull)getSdkName; - [Static] - [Export("getSdkName")] - string SdkName { get; } - - // +(NSString * _Nonnull)getSdkVersionString; - [Static] - [Export("getSdkVersionString")] - string SdkVersionString { get; } - - // +(void)addSdkPackage:(NSString * _Nonnull)name version:(NSString * _Nonnull)version; - [Static] - [Export("addSdkPackage:version:")] - void AddSdkPackage(string name, string version); - - // +(NSDictionary * _Nonnull)getExtraContext; - [Static] - [Export("getExtraContext")] - NSDictionary ExtraContext { get; } - - // +(void)setTrace:(SentryId * _Nonnull)traceId spanId:(SentrySpanId * _Nonnull)spanId; - [Static] - [Export("setTrace:spanId:")] - void SetTrace(SentryId traceId, SentrySpanId spanId); - - // +(uint64_t)startProfilerForTrace:(SentryId * _Nonnull)traceId; - [Static] - [Export("startProfilerForTrace:")] - ulong StartProfilerForTrace(SentryId traceId); - - // +(NSMutableDictionary * _Nullable)collectProfileBetween:(uint64_t)startSystemTime and:(uint64_t)endSystemTime forTrace:(SentryId * _Nonnull)traceId; - [Static] - [Export("collectProfileBetween:and:forTrace:")] - [return: NullAllowed] - NSMutableDictionary CollectProfileBetween(ulong startSystemTime, ulong endSystemTime, SentryId traceId); - - // +(void)discardProfilerForTrace:(SentryId * _Nonnull)traceId; - [Static] - [Export("discardProfilerForTrace:")] - void DiscardProfilerForTrace(SentryId traceId); - - // @property (readonly, copy, nonatomic, class) NSString * _Nonnull installationID; - [Static] - [Export("installationID")] - string InstallationID { get; } - - // @property (readonly, copy, nonatomic, class) SentryOptions * _Nonnull options; - [Static] - [Export("options", ArgumentSemantic.Copy)] - SentryOptions Options { get; } - - // @property (assign, nonatomic, class) BOOL framesTrackingMeasurementHybridSDKMode; - [Static] - [Export("framesTrackingMeasurementHybridSDKMode")] - bool FramesTrackingMeasurementHybridSDKMode { get; set; } - - // @property (readonly, assign, nonatomic, class) BOOL isFramesTrackingRunning; - [Static] - [Export("isFramesTrackingRunning")] - bool IsFramesTrackingRunning { get; } - - // @property (readonly, assign, nonatomic, class) SentryScreenFrames * _Nonnull currentScreenFrames; - [Static] - [Export("currentScreenFrames", ArgumentSemantic.Assign)] - SentryScreenFrames CurrentScreenFrames { get; } - - // +(NSArray * _Nullable)captureScreenshots; - [Static] - [Export("captureScreenshots")] - [return: NullAllowed] - NSData[] CaptureScreenshots(); - - // +(NSData * _Nullable)captureViewHierarchy; - [Static] - [Export("captureViewHierarchy")] - [return: NullAllowed] - NSData CaptureViewHierarchy(); - - // +(void)setCurrentScreen:(NSString * _Nullable)screenName; - [Static] - [Export("setCurrentScreen:")] - void SetCurrentScreen([NullAllowed] string screenName); - - // +(void)configureSessionReplayWith:(id _Nullable)breadcrumbConverter screenshotProvider:(id _Nullable)screenshotProvider; - [Static] - [Export("configureSessionReplayWith:screenshotProvider:")] - void ConfigureSessionReplayWith([NullAllowed] SentryReplayBreadcrumbConverter breadcrumbConverter, [NullAllowed] SentryViewScreenshotProvider screenshotProvider); - - // +(void)captureReplay; - [Static] - [Export("captureReplay")] - void CaptureReplay(); - - // +(NSString * _Nullable)getReplayId; - [Static] - [NullAllowed, Export("getReplayId")] - string ReplayId { get; } - - // +(void)addReplayIgnoreClasses:(NSArray * _Nonnull)classes; - [Static] - [Export("addReplayIgnoreClasses:")] - void AddReplayIgnoreClasses(Class[] classes); - - // +(void)addReplayRedactClasses:(NSArray * _Nonnull)classes; - [Static] - [Export("addReplayRedactClasses:")] - void AddReplayRedactClasses(Class[] classes); - - // +(void)setIgnoreContainerClass:(Class _Nonnull)containerClass; - [Static] - [Export("setIgnoreContainerClass:")] - void SetIgnoreContainerClass(Class containerClass); - - // +(void)setRedactContainerClass:(Class _Nonnull)containerClass; - [Static] - [Export("setRedactContainerClass:")] - void SetRedactContainerClass(Class containerClass); - - // +(void)setReplayTags:(NSDictionary * _Nonnull)tags; - [Static] - [Export("setReplayTags:")] - void SetReplayTags(NSDictionary tags); - - // +(SentryUser * _Nonnull)userWithDictionary:(NSDictionary * _Nonnull)dictionary; - [Static] - [Export("userWithDictionary:")] - SentryUser UserWithDictionary(NSDictionary dictionary); - - // +(SentryBreadcrumb * _Nonnull)breadcrumbWithDictionary:(NSDictionary * _Nonnull)dictionary; - [Static] - [Export("breadcrumbWithDictionary:")] - SentryBreadcrumb BreadcrumbWithDictionary(NSDictionary dictionary); - - // +(SentryOptions * _Nullable)optionsWithDictionary:(NSDictionary * _Nonnull)options didFailWithError:(NSError * _Nullable * _Nullable)error; - [Static] - [Export("optionsWithDictionary:didFailWithError:")] - [return: NullAllowed] - SentryOptions OptionsWithDictionary(NSDictionary options, [NullAllowed] out NSError error); - - // +(void)setLogOutput:(void (^ _Nonnull)(NSString * _Nonnull))output; - [Static] - [Export("setLogOutput:")] - void SetLogOutput(Action output); - - // +(void)ignoreNextSignal:(int)signum; - [Static] - [Export("ignoreNextSignal:")] - void IgnoreNextSignal(int signum); -} - // @interface SentryOptions : NSObject [BaseType(typeof(NSObject))] [Internal] @@ -3024,3 +2858,135 @@ interface SentryViewScreenshotOptions : SentryRedactOptions [DesignatedInitializer] NativeHandle Constructor(bool enableViewRendererV2, bool enableFastViewRendering, bool maskAllText, bool maskAllImages, Class[] maskedViewClasses, Class[] unmaskedViewClasses, NSSet excludedViewClasses, NSSet includedViewClasses); } + +// @interface SentryObjCSDK : NSObject +[BaseType(typeof(NSObject))] +[Internal] +interface SentryObjCSDK +{ + + // @property (readonly, nonatomic, class) SentryObjCInternalApi * _Nonnull internal; + [Static] + [Export("internal")] + SentryObjCInternalApi Internal { get; } +} + +// @interface SentryObjCInternalApi : NSObject +[BaseType(typeof(NSObject))] +[DisableDefaultCtor] +[Internal] +interface SentryObjCInternalApi +{ + // @property (readonly, nonatomic) SentryObjCInternalSdkApi * _Nonnull sdk; + [Export("sdk")] + SentryObjCInternalSdkApi Sdk { get; } + + // @property (readonly, nonatomic) SentryObjCInternalProfilingApi * _Nonnull profiling; + [Export("profiling")] + SentryObjCInternalProfilingApi Profiling { get; } + + // -(void)setTrace:(SentryObjCId * _Nonnull)traceId spanId:(SentryObjCSpanId * _Nonnull)spanId; + [Export("setTrace:spanId:")] + void SetTrace(SentryObjCId traceId, SentryObjCSpanId spanId); + + // -(void)ignoreNextSignal:(int)signum; + [Export("ignoreNextSignal:")] + void IgnoreNextSignal(int signum); +} + +// @interface SentryObjCInternalSdkApi : NSObject +[BaseType(typeof(NSObject))] +[DisableDefaultCtor] +[Internal] +interface SentryObjCInternalSdkApi +{ + // @property (copy, nonatomic) NSString * _Nonnull name; + [Export("name")] + string Name { get; set; } + + // @property (copy, nonatomic) NSString * _Nonnull versionString; + [Export("versionString")] + string VersionString { get; set; } + + // -(void)setName:(NSString * _Nonnull)name version:(NSString * _Nonnull)version; + [Export("setName:version:")] + void SetName(string name, string version); + + // -(void)addPackageName:(NSString * _Nonnull)name version:(NSString * _Nonnull)version; + [Export("addPackageName:version:")] + void AddPackageName(string name, string version); + + // @property (readonly, copy, nonatomic) NSDictionary * _Nonnull extraContext; + [Export("extraContext", ArgumentSemantic.Copy)] + NSDictionary ExtraContext { get; } + + // @property (readonly, copy, nonatomic) NSString * _Nonnull installationID; + [Export("installationID")] + string InstallationID { get; } +} + +// @interface SentryObjCInternalProfilingApi : NSObject +[BaseType(typeof(NSObject))] +[DisableDefaultCtor] +[Internal] +interface SentryObjCInternalProfilingApi +{ + // -(uint64_t)startFor:(SentryObjCId * _Nonnull)traceId; + [Export("startFor:")] + ulong StartFor(SentryObjCId traceId); + + // -(NSDictionary * _Nullable)collectBetweenStartTime:(uint64_t)startTime andEndTime:(uint64_t)endTime forTraceId:(SentryObjCId * _Nonnull)traceId; + [Export("collectBetweenStartTime:andEndTime:forTraceId:")] + [return: NullAllowed] + NSDictionary CollectBetweenStartTime(ulong startTime, ulong endTime, SentryObjCId traceId); + + // -(void)discardFor:(SentryObjCId * _Nonnull)traceId; + [Export("discardFor:")] + void DiscardFor(SentryObjCId traceId); +} + +// @interface SentryObjCId : NSObject +[BaseType(typeof(NSObject))] +[Internal] +interface SentryObjCId +{ + // @property (readonly, copy, nonatomic) NSString * _Nonnull sentryIdString; + [Export("sentryIdString")] + string SentryIdString { get; } + + // @property (readonly, nonatomic, strong, class) SentryObjCId * _Nonnull empty; + [Static] + [Export("empty", ArgumentSemantic.Strong)] + SentryObjCId Empty { get; } + + // -(instancetype _Nonnull)initWithUuid:(NSUUID * _Nonnull)uuid; + [Export("initWithUuid:")] + NativeHandle Constructor(NSUuid uuid); + + // -(instancetype _Nonnull)initWithUUIDString:(NSString * _Nonnull)uuidString; + [Export("initWithUUIDString:")] + NativeHandle Constructor(string uuidString); +} + +// @interface SentryObjCSpanId : NSObject +[BaseType(typeof(NSObject))] +[Internal] +interface SentryObjCSpanId +{ + // @property (readonly, copy, nonatomic) NSString * _Nonnull sentrySpanIdString; + [Export("sentrySpanIdString")] + string SentrySpanIdString { get; } + + // @property (readonly, nonatomic, strong, class) SentryObjCSpanId * _Nonnull empty; + [Static] + [Export("empty", ArgumentSemantic.Strong)] + SentryObjCSpanId Empty { get; } + + // -(instancetype _Nonnull)initWithUuid:(NSUUID * _Nonnull)uuid; + [Export("initWithUuid:")] + NativeHandle Constructor(NSUuid uuid); + + // -(instancetype _Nonnull)initWithValue:(NSString * _Nonnull)value; + [Export("initWithValue:")] + NativeHandle Constructor(string value); +} diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 96bc4b2d0c..6f23ff8266 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -8,22 +8,19 @@ true .NET Bindings for the Sentry Cocoa SDK ..\..\modules\sentry-cocoa\ - $(MSBuildThisFileDirectory)..\..\modules\sentry-cocoa.properties - $(SentryCocoaCache)Sentry.framework\ $(NoWarn);CS0108 - - - $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(SentryCocoaProperties)')), 'version\s*=\s*([^\s]+)').Groups[1].Value) - $(SentryCocoaCache)Sentry-$(SentryCocoaVersion).xcframework - $(SentryCocoaProperties);../../scripts/generate-cocoa-bindings.ps1;$(SentryCocoaFrameworkHeaders)**/*.h - - - + $(SentryCocoaCache)Carthage\Build-$(TargetPlatformIdentifier)\Sentry.xcframework + + $(SentryCocoaCache)Carthage\Build-$(TargetPlatformIdentifier)\SentryObjC.xcframework + $(SentryCocoaCache)Carthage\Build-$(TargetPlatformIdentifier)\SentryObjCCompat.xcframework ../../scripts/generate-cocoa-bindings.ps1;$(SentryCocoaCache)Carthage/.built-from-sha;$(SentryCocoaCache)Carthage/**/*.h $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), $(SentryCocoaCache).git)) @@ -54,6 +51,8 @@ + + @@ -66,44 +65,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -135,8 +100,7 @@ Condition="$([MSBuild]::IsOSPlatform('OSX'))" /> - - + @@ -152,15 +116,6 @@ - - - - - - - - diff --git a/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets b/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets index 4b74ad10fe..fcedf5b1b7 100644 --- a/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets +++ b/src/Sentry.Bindings.Cocoa/buildTransitive/Sentry.Bindings.Cocoa.targets @@ -45,7 +45,9 @@ --> - + + diff --git a/src/Sentry/Platforms/Cocoa/CocoaProfiler.cs b/src/Sentry/Platforms/Cocoa/CocoaProfiler.cs index 3bbed89c7b..98e368357f 100644 --- a/src/Sentry/Platforms/Cocoa/CocoaProfiler.cs +++ b/src/Sentry/Platforms/Cocoa/CocoaProfiler.cs @@ -9,12 +9,12 @@ internal class CocoaProfiler : ITransactionProfiler { private readonly SentryOptions _options; private readonly SentryId _traceId; - private readonly CocoaSdk.SentryId _cocoaTraceId; + private readonly CocoaSdk.SentryObjCId _cocoaTraceId; private readonly ulong _startTimeNs; private ulong _endTimeNs; private readonly SentryStopwatch _stopwatch; - public CocoaProfiler(SentryOptions options, ulong startTimeNs, SentryId traceId, CocoaSdk.SentryId cocoaTraceId) + public CocoaProfiler(SentryOptions options, ulong startTimeNs, SentryId traceId, CocoaSdk.SentryObjCId cocoaTraceId) { _stopwatch = SentryStopwatch.StartNew(); _options = options; @@ -36,14 +36,17 @@ public void Finish() public ISerializable? Collect(SentryTransaction transaction) { - var payload = SentryCocoaHybridSdk.CollectProfileBetween(_startTimeNs, _endTimeNs, _cocoaTraceId); - if (payload is null) + var collected = SentryCocoaHybridSdk.Internal.Profiling.CollectBetweenStartTime(_startTimeNs, _endTimeNs, _cocoaTraceId); + if (collected is null) { _options.LogWarning("Trace {0} collected profile payload is null", _traceId); return null; } _options.LogDebug("Trace {0} profile payload collected", _traceId); + // The SentryObjCSDK.internal profiling API returns an immutable NSDictionary, so copy it before mutating below. + var payload = (NSMutableDictionary)collected.MutableCopy(); + var payloadTx = payload["transaction"]?.MutableCopy() as NSMutableDictionary; if (payloadTx is null) { diff --git a/src/Sentry/Platforms/Cocoa/CocoaProfilerFactory.cs b/src/Sentry/Platforms/Cocoa/CocoaProfilerFactory.cs index 2d49446bdd..246ad5b516 100644 --- a/src/Sentry/Platforms/Cocoa/CocoaProfilerFactory.cs +++ b/src/Sentry/Platforms/Cocoa/CocoaProfilerFactory.cs @@ -15,8 +15,8 @@ internal CocoaProfilerFactory(SentryOptions options) /// public ITransactionProfiler? Start(ITransactionTracer tracer, CancellationToken cancellationToken) { - var traceId = tracer.TraceId.ToCocoaSentryId(); - var startTime = SentryCocoaHybridSdk.StartProfilerForTrace(traceId); + var traceId = tracer.TraceId.ToCocoaObjCId(); + var startTime = SentryCocoaHybridSdk.Internal.Profiling.StartFor(traceId); return new CocoaProfiler(_options, startTime, tracer.TraceId, traceId); } } diff --git a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs index 46f8aebf5e..98746e5f15 100644 --- a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs +++ b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs @@ -112,7 +112,7 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) { try { - SentryCocoaHybridSdk.SetTrace(traceId.ToCocoaSentryId(), parentSpanId.ToCocoaSpanId()); + SentryCocoaHybridSdk.Internal.SetTrace(traceId.ToCocoaObjCId(), parentSpanId.ToCocoaObjCSpanId()); } finally { diff --git a/src/Sentry/Platforms/Cocoa/Extensions/MiscExtensions.cs b/src/Sentry/Platforms/Cocoa/Extensions/MiscExtensions.cs index 777d09caf1..c23b5bf8d3 100644 --- a/src/Sentry/Platforms/Cocoa/Extensions/MiscExtensions.cs +++ b/src/Sentry/Platforms/Cocoa/Extensions/MiscExtensions.cs @@ -4,9 +4,11 @@ internal static class MiscExtensions { public static SentryId ToSentryId(this CocoaSdk.SentryId sentryId) => new(Guid.Parse(sentryId.SentryIdString)); - public static CocoaSdk.SentryId ToCocoaSentryId(this SentryId sentryId) => new(sentryId.ToString()); - public static SpanId ToSpanId(this CocoaSdk.SentrySpanId spanId) => new(spanId.SentrySpanIdString); - public static CocoaSdk.SentrySpanId ToCocoaSpanId(this SpanId spanId) => new(spanId.ToString()); + // The SentryObjCSDK.internal hybrid API uses its own id types (SentryObjCId / SentryObjCSpanId) + // rather than the Sentry.framework SentryId / SentrySpanId used elsewhere. + public static CocoaSdk.SentryObjCId ToCocoaObjCId(this SentryId sentryId) => new(sentryId.ToString()); + + public static CocoaSdk.SentryObjCSpanId ToCocoaObjCSpanId(this SpanId spanId) => new(spanId.ToString()); } diff --git a/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs b/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs index 21e9200201..c7534a0f63 100644 --- a/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs +++ b/src/Sentry/Platforms/Cocoa/RuntimeAdapter.cs @@ -25,7 +25,7 @@ private RuntimeAdapter() public bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; - public void IgnoreNextSignal(int signal) => SentryCocoaHybridSdk.IgnoreNextSignal(signal); + public void IgnoreNextSignal(int signal) => SentryCocoaHybridSdk.Internal.IgnoreNextSignal(signal); [SecurityCritical] private void OnMarshalManagedException(object sender, MarshalManagedExceptionEventArgs e) => MarshalManagedException?.Invoke(this, e); diff --git a/src/Sentry/Platforms/Cocoa/Sentry.Cocoa.props b/src/Sentry/Platforms/Cocoa/Sentry.Cocoa.props index 3c98f1fc4e..7f02ed04c3 100644 --- a/src/Sentry/Platforms/Cocoa/Sentry.Cocoa.props +++ b/src/Sentry/Platforms/Cocoa/Sentry.Cocoa.props @@ -8,7 +8,7 @@ - + diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index 9fd3f62ef5..2b0df2a7e3 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -166,7 +166,7 @@ private static void InitSentryCocoaSdk(SentryOptions options) } // Set hybrid SDK name - SentryCocoaHybridSdk.SetSdkName("sentry.cocoa.dotnet"); + SentryCocoaHybridSdk.Internal.Sdk.Name = "sentry.cocoa.dotnet"; // Now initialize the Cocoa SDK SentryCocoaSdk.StartWithOptions(nativeOptions);