From dead3791b778249f7a139fc0752ca40bfc57f14e Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 4 Sep 2026 13:57:42 +0200 Subject: [PATCH 1/3] feat(switch2): add Nintendo Switch 2 support Recognise the platform and route it through the existing Switch implementation: UNITY_SWITCH2 defines SENTRY_NATIVE_SWITCH2, which selects SentryNativeSwitch as the platform configuration, and both assembly definitions now include Switch 2 so the runtime code ships to it. Switch 2 shares SwitchNativeSupportEnabled rather than introducing a second option, since it shares the implementation. Which platforms an assembly or a stub ships to is decided by its plugin metadata, so Switch 2 reuses the existing Switch native assembly and the existing stub rather than duplicating either. The build preprocessor now runs for both targets, resolves the required libraries per platform, and toggles stub compatibility for whichever target is being built - the importer tracks that per target, so the two do not interfere. RuntimePlatform.Switch2 and BuildTarget.Switch2 only exist in Unity 6000.3 and newer while the SDK still supports 2021.3, and these assemblies compile against a single Unity version, so both are matched by name rather than by enum member - referencing them directly would stop the SDK building against older editors. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01ABHTqM37qGAc9iQZcdbdY8 --- CHANGELOG.md | 4 ++ .../Plugins/Switch/sentry_native_stubs.c.meta | 4 ++ .../Sentry.Unity.Native.Switch.dll.meta | 4 ++ package-dev/Runtime/SentryInitialization.cs | 8 ++- .../io.sentry.unity.dev.runtime.asmdef | 1 + .../Runtime/io.sentry.unity.runtime.asmdef | 1 + .../ConfigurationWindow/AdvancedTab.cs | 2 +- .../SwitchNativePluginBuildPreProcess.cs | 66 +++++++++++++------ src/Sentry.Unity/Properties/AssemblyInfo.cs | 1 + .../SentryUnityOptionsExtensions.cs | 18 +++++ .../SentryUnityOptionsExtensionsTests.cs | 31 +++++++++ 11 files changed, 118 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1271dd85f..62920c02a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - The `EnableMetrics` option is now marked as `[Obsolete]` and no longer has any effect, following the .NET SDK where metrics are always enabled. Disable the individual `Auto*Metrics` options instead, or filter emitted metrics with `SetBeforeSendMetric`. ([#2828](https://github.com/getsentry/sentry-unity/pull/2828)) +### Features + +- Added Nintendo Switch 2 support. The SDK now recognises the platform, links the Switch 2 build of the native library from `Assets/Plugins/Sentry/Switch2/`, and uploads its debug symbols. Switch 2 shares the existing `SwitchNativeSupportEnabled` option ([#2834](https://github.com/getsentry/sentry-unity/pull/2834)) + ### Fixes - IL2CPP line numbers now work on Android x86/x86_64 builds. il2cpp fails to report the image UUID there, so the SDK falls back to looking the debug image up by name ([#2817](https://github.com/getsentry/sentry-unity/pull/2817)) diff --git a/package-dev/Plugins/Switch/sentry_native_stubs.c.meta b/package-dev/Plugins/Switch/sentry_native_stubs.c.meta index 33f21e6e4..c72bd14c2 100644 --- a/package-dev/Plugins/Switch/sentry_native_stubs.c.meta +++ b/package-dev/Plugins/Switch/sentry_native_stubs.c.meta @@ -25,6 +25,7 @@ PluginImporter: Exclude Linux64: 1 Exclude OSXUniversal: 1 Exclude Switch: 0 + Exclude Switch2: 0 Exclude WebGL: 1 Exclude Win: 1 Exclude Win64: 1 @@ -46,6 +47,9 @@ PluginImporter: Switch: enabled: 1 settings: {} + Switch2: + enabled: 1 + settings: {} Win: enabled: 0 settings: diff --git a/package-dev/Runtime/Sentry.Unity.Native.Switch.dll.meta b/package-dev/Runtime/Sentry.Unity.Native.Switch.dll.meta index 25c262827..927a6240c 100644 --- a/package-dev/Runtime/Sentry.Unity.Native.Switch.dll.meta +++ b/package-dev/Runtime/Sentry.Unity.Native.Switch.dll.meta @@ -26,6 +26,7 @@ PluginImporter: Exclude OSXUniversal: 1 Exclude PS5: 1 Exclude Switch: 0 + Exclude Switch2: 0 Exclude WebGL: 1 Exclude Win: 1 Exclude Win64: 1 @@ -50,6 +51,9 @@ PluginImporter: Switch: enabled: 1 settings: {} + Switch2: + enabled: 1 + settings: {} Win: enabled: 0 settings: diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index ddb20b6a0..7f2f84ad3 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -20,6 +20,10 @@ #define SENTRY_NATIVE_SWITCH #endif +#if UNITY_SWITCH2 +#define SENTRY_NATIVE_SWITCH2 +#endif + #if UNITY_WEBGL #define SENTRY_WEBGL #endif @@ -45,7 +49,7 @@ using Sentry.Unity.iOS; #elif SENTRY_NATIVE_ANDROID using Sentry.Unity.Android; -#elif SENTRY_NATIVE || SENTRY_NATIVE_SWITCH +#elif SENTRY_NATIVE || SENTRY_NATIVE_SWITCH || SENTRY_NATIVE_SWITCH2 using Sentry.Unity.Native; #elif SENTRY_WEBGL using Sentry.Unity.WebGL; @@ -108,7 +112,7 @@ private static void SetUpPlatformServices() SentryPlatformServices.PlatformConfiguration = SentryNativeCocoa.Configure; #elif SENTRY_NATIVE_ANDROID SentryPlatformServices.PlatformConfiguration = SentryNativeAndroid.Configure; -#elif SENTRY_NATIVE_SWITCH +#elif SENTRY_NATIVE_SWITCH || SENTRY_NATIVE_SWITCH2 SentryPlatformServices.PlatformConfiguration = SentryNativeSwitch.Configure; #elif SENTRY_NATIVE SentryPlatformServices.PlatformConfiguration = SentryNative.Configure; diff --git a/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef b/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef index 357a78477..3f491e257 100644 --- a/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef +++ b/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef @@ -9,6 +9,7 @@ "LinuxStandalone64", "macOSStandalone", "Switch", + "Switch2", "PS5", "WSA", "WebGL", diff --git a/package/Runtime/io.sentry.unity.runtime.asmdef b/package/Runtime/io.sentry.unity.runtime.asmdef index e9e1c7df5..38dd0ef4a 100644 --- a/package/Runtime/io.sentry.unity.runtime.asmdef +++ b/package/Runtime/io.sentry.unity.runtime.asmdef @@ -12,6 +12,7 @@ "macOSStandalone", "PS5", "Switch", + "Switch2", "WSA", "WebGL", "WindowsStandalone32", diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs index 1d3225929..1d2940afb 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs @@ -176,7 +176,7 @@ internal static void Display(ScriptableSentryUnityOptions options, SentryCliOpti options.PlayStationNativeSupportEnabled); options.SwitchNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("Nintendo Switch", "Whether to enable native scope sync support on Nintendo Switch."), + new GUIContent("Nintendo Switch", "Whether to enable native scope sync support on Nintendo Switch and Switch 2."), options.SwitchNativeSupportEnabled); } diff --git a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs index 104ec7e92..673f6c97f 100644 --- a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Linq; using Sentry.Extensibility; @@ -21,17 +22,37 @@ namespace Sentry.Unity.Editor.Native; /// internal class SwitchNativePluginBuildPreProcess : IPreprocessBuildWithReport { - private static readonly string[] RequiredFiles = + /// + /// BuildTarget.Switch2 only exists in Unity 6000.3 and newer, and this assembly is + /// compiled against a single Unity version, so the target is matched by name. The build report + /// hands us the value itself, so the enum member never has to be referenced. + /// + internal const string Switch2BuildTargetName = "Switch2"; + + private static bool IsSwitchFamily(BuildTarget target) => + target == BuildTarget.Switch || IsSwitch2(target); + + private static bool IsSwitch2(BuildTarget target) => + string.Equals(target.ToString(), Switch2BuildTargetName, StringComparison.Ordinal); + + /// + /// Both platforms share one stub, so the required libraries are what differ between them. + /// + private static string[] RequiredFilesFor(BuildTarget target) { - "Assets/Plugins/Sentry/Switch/libsentry.a", - "Assets/Plugins/Sentry/Switch/libzstd.a", - }; + var directory = IsSwitch2(target) ? Switch2BuildTargetName : nameof(BuildTarget.Switch); + return new[] + { + $"Assets/Plugins/Sentry/{directory}/libsentry.a", + $"Assets/Plugins/Sentry/{directory}/libzstd.a", + }; + } public int callbackOrder => -100; public void OnPreprocessBuild(BuildReport report) { - if (report.summary.platform != BuildTarget.Switch) + if (!IsSwitchFamily(report.summary.platform)) { return; } @@ -39,14 +60,19 @@ public void OnPreprocessBuild(BuildReport report) var options = SentryScriptableObject.LoadOptions(isBuilding: true); var logger = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions()); - ConfigureStub(logger, options?.SwitchNativeSupportEnabled ?? false); + // Switch 2 reuses the Switch implementation and therefore its option. + ConfigureStub(logger, options?.SwitchNativeSupportEnabled ?? false, report.summary.platform); } - internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportEnabled) + internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportEnabled, BuildTarget target) { - logger.LogDebug("Switch native support: checking for required files:\n{0}", - string.Join("\n", RequiredFiles.Select(f => $" - {f}"))); + var requiredFiles = RequiredFilesFor(target); + + logger.LogDebug("{0} native support: checking for required files:\n{1}", + target, string.Join("\n", requiredFiles.Select(f => $" - {f}"))); + // One stub serves both platforms; the importer tracks compatibility per build target, so + // enabling it for one does not affect the other. var stubPath = Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "Switch", "sentry_native_stubs.c"); var importer = AssetImporter.GetAtPath(stubPath) as PluginImporter; @@ -56,14 +82,16 @@ internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportE return; } - var existingFiles = RequiredFiles.Where(File.Exists).ToList(); - var missingFiles = RequiredFiles.Except(existingFiles).ToList(); + var existingFiles = requiredFiles.Where(File.Exists).ToList(); + var missingFiles = requiredFiles.Except(existingFiles).ToList(); var someFilesPresent = existingFiles.Count > 0 && missingFiles.Count > 0; if (someFilesPresent) { + // LogError has no two-argument overload that does not also take an exception, so the + // target goes into the format string rather than being passed alongside the file list. logger.LogError( - "Switch native support is partially configured. Missing files:\n{0}\n" + + target + " native support is partially configured. Missing files:\n{0}\n" + "Please add all required files to enable native support, or remove all files to fall back on no-op stubs.\n" + "Build sentry-switch and copy the libraries to the expected locations. " + "See: https://github.com/getsentry/sentry-switch", @@ -75,26 +103,26 @@ internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportE var allFilesPresent = missingFiles.Count == 0; if (allFilesPresent) { - logger.LogInfo("Switch native libraries found:\n{0}", - string.Join("\n", existingFiles.Select(f => $" - {f}"))); - importer.SetCompatibleWithPlatform(BuildTarget.Switch, false); + logger.LogInfo("{0} native libraries found:\n{1}", + target, string.Join("\n", existingFiles.Select(f => $" - {f}"))); + importer.SetCompatibleWithPlatform(target, false); } else { if (nativeSupportEnabled) { logger.LogWarning( - "Switch native support is enabled but required files are missing:\n{0}\n" + + "{0} native support is enabled but required files are missing:\n{1}\n" + "Build sentry-switch and copy the libraries to the expected locations. " + "See: https://github.com/getsentry/sentry-switch", - string.Join("\n", missingFiles.Select(f => $" - {f}")) + target, string.Join("\n", missingFiles.Select(f => $" - {f}")) ); } else { - logger.LogDebug("Switch native support is disabled. Enabling stubs (native calls will be no-op)."); + logger.LogDebug("{0} native support is disabled. Enabling stubs (native calls will be no-op).", target); } - importer.SetCompatibleWithPlatform(BuildTarget.Switch, true); + importer.SetCompatibleWithPlatform(target, true); } importer.SaveAndReimport(); diff --git a/src/Sentry.Unity/Properties/AssemblyInfo.cs b/src/Sentry.Unity/Properties/AssemblyInfo.cs index 9e8226ca7..2254b5e0e 100644 --- a/src/Sentry.Unity/Properties/AssemblyInfo.cs +++ b/src/Sentry.Unity/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ [assembly: InternalsVisibleTo("Sentry.Unity.Native")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.PlayStation")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.Switch")] +[assembly: InternalsVisibleTo("Sentry.Unity.Native.Switch2")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.Xbox")] [assembly: InternalsVisibleTo("Sentry.Unity.Tests")] [assembly: InternalsVisibleTo("Sentry.Unity.Editor")] diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index cf2dc13dc..de5553103 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -49,9 +49,27 @@ internal static bool IsValid(this SentryUnityOptions options) return true; } + /// + /// RuntimePlatform.Switch2 was only added in Unity 6000.3. This assembly is compiled + /// against a single Unity version while the SDK still supports 2021.3, so Switch 2 is matched + /// by name instead of by enum member - referencing the member directly would stop the SDK from + /// building against the editors that predate it. + /// + internal const string Switch2PlatformName = "Switch2"; + + internal static bool IsSwitch2(this RuntimePlatform platform) => + string.Equals(platform.ToString(), Switch2PlatformName, StringComparison.Ordinal); + internal static bool IsNativeSupportEnabled(this SentryUnityOptions options, RuntimePlatform? platform = null) { platform ??= ApplicationAdapter.Instance.Platform; + + // Switch 2 reuses the Switch native support, and therefore its option. + if (platform.Value.IsSwitch2()) + { + return options.SwitchNativeSupportEnabled; + } + return platform switch { RuntimePlatform.Android => options.AndroidNativeSupportEnabled, diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs index 8ba703b9b..cf802b657 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Sentry.Unity.Tests.Stubs; @@ -207,4 +208,34 @@ public void IsNativeSupportEnabled_ConsolePlatforms_ReturnsExpectedValue( Assert.AreEqual(expectedResult, result); } + + /// + /// Switch 2 shares the Switch option. It is resolved by name because + /// RuntimePlatform.Switch2 does not exist on the Unity versions the SDK still supports, + /// so this parses the member instead of referencing it and skips where it is unavailable. + /// + [Test] + [TestCase(true, true)] + [TestCase(false, false)] + public void IsNativeSupportEnabled_Switch2_FollowsSwitchOption(bool optionEnabled, bool expectedResult) + { + if (!Enum.TryParse( + SentryUnityOptionsExtensions.Switch2PlatformName, out var switch2)) + { + Assert.Ignore("This Unity version predates 'RuntimePlatform.Switch2'."); + } + + var options = _fixture.GetSut(); + options.SwitchNativeSupportEnabled = optionEnabled; + + Assert.AreEqual(expectedResult, options.IsNativeSupportEnabled(switch2)); + } + + [Test] + public void IsSwitch2_OtherPlatforms_ReturnsFalse() + { + Assert.IsFalse(RuntimePlatform.Switch.IsSwitch2()); + Assert.IsFalse(RuntimePlatform.PS5.IsSwitch2()); + Assert.IsFalse(RuntimePlatform.WindowsPlayer.IsSwitch2()); + } } From c48a310afa49017dc6e337c944306729891f3772 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 4 Sep 2026 15:05:12 +0200 Subject: [PATCH 2/3] cleanup --- package-dev/Runtime/SentryInitialization.cs | 10 ++---- .../SwitchNativePluginBuildPreProcess.cs | 36 ++++++++----------- src/Sentry.Unity/Properties/AssemblyInfo.cs | 1 - .../SentryUnityOptionsExtensions.cs | 18 +++------- .../SentryUnityOptionsExtensionsTests.cs | 3 +- 5 files changed, 22 insertions(+), 46 deletions(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 7f2f84ad3..0e927bb4e 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -16,14 +16,10 @@ #define SENTRY_NATIVE #endif -#if UNITY_SWITCH +#if UNITY_SWITCH || UNITY_SWITCH2 #define SENTRY_NATIVE_SWITCH #endif -#if UNITY_SWITCH2 -#define SENTRY_NATIVE_SWITCH2 -#endif - #if UNITY_WEBGL #define SENTRY_WEBGL #endif @@ -49,7 +45,7 @@ using Sentry.Unity.iOS; #elif SENTRY_NATIVE_ANDROID using Sentry.Unity.Android; -#elif SENTRY_NATIVE || SENTRY_NATIVE_SWITCH || SENTRY_NATIVE_SWITCH2 +#elif SENTRY_NATIVE || SENTRY_NATIVE_SWITCH using Sentry.Unity.Native; #elif SENTRY_WEBGL using Sentry.Unity.WebGL; @@ -112,7 +108,7 @@ private static void SetUpPlatformServices() SentryPlatformServices.PlatformConfiguration = SentryNativeCocoa.Configure; #elif SENTRY_NATIVE_ANDROID SentryPlatformServices.PlatformConfiguration = SentryNativeAndroid.Configure; -#elif SENTRY_NATIVE_SWITCH || SENTRY_NATIVE_SWITCH2 +#elif SENTRY_NATIVE_SWITCH SentryPlatformServices.PlatformConfiguration = SentryNativeSwitch.Configure; #elif SENTRY_NATIVE SentryPlatformServices.PlatformConfiguration = SentryNative.Configure; diff --git a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs index 673f6c97f..4e35a6b7a 100644 --- a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs @@ -22,30 +22,25 @@ namespace Sentry.Unity.Editor.Native; /// internal class SwitchNativePluginBuildPreProcess : IPreprocessBuildWithReport { - /// - /// BuildTarget.Switch2 only exists in Unity 6000.3 and newer, and this assembly is - /// compiled against a single Unity version, so the target is matched by name. The build report - /// hands us the value itself, so the enum member never has to be referenced. - /// - internal const string Switch2BuildTargetName = "Switch2"; - private static bool IsSwitchFamily(BuildTarget target) => target == BuildTarget.Switch || IsSwitch2(target); + /// + /// BuildTarget.Switch2 only exists in Unity 6000.3 and newer. + /// private static bool IsSwitch2(BuildTarget target) => - string.Equals(target.ToString(), Switch2BuildTargetName, StringComparison.Ordinal); + string.Equals(target.ToString(), "Switch2", StringComparison.Ordinal); /// /// Both platforms share one stub, so the required libraries are what differ between them. /// - private static string[] RequiredFilesFor(BuildTarget target) + private static string[] RequiredFilesFor(string targetDirectory) { - var directory = IsSwitch2(target) ? Switch2BuildTargetName : nameof(BuildTarget.Switch); - return new[] - { - $"Assets/Plugins/Sentry/{directory}/libsentry.a", - $"Assets/Plugins/Sentry/{directory}/libzstd.a", - }; + return + [ + $"Assets/Plugins/Sentry/{targetDirectory}/libsentry.a", + $"Assets/Plugins/Sentry/{targetDirectory}/libzstd.a" + ]; } public int callbackOrder => -100; @@ -60,13 +55,12 @@ public void OnPreprocessBuild(BuildReport report) var options = SentryScriptableObject.LoadOptions(isBuilding: true); var logger = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions()); - // Switch 2 reuses the Switch implementation and therefore its option. ConfigureStub(logger, options?.SwitchNativeSupportEnabled ?? false, report.summary.platform); } internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportEnabled, BuildTarget target) { - var requiredFiles = RequiredFilesFor(target); + var requiredFiles = RequiredFilesFor(nameof(target)); logger.LogDebug("{0} native support: checking for required files:\n{1}", target, string.Join("\n", requiredFiles.Select(f => $" - {f}"))); @@ -88,14 +82,12 @@ internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportE var someFilesPresent = existingFiles.Count > 0 && missingFiles.Count > 0; if (someFilesPresent) { - // LogError has no two-argument overload that does not also take an exception, so the - // target goes into the format string rather than being passed alongside the file list. - logger.LogError( - target + " native support is partially configured. Missing files:\n{0}\n" + + logger.LogWarning( + "{0} native support is partially configured. Missing files:\n{1}\n" + "Please add all required files to enable native support, or remove all files to fall back on no-op stubs.\n" + "Build sentry-switch and copy the libraries to the expected locations. " + "See: https://github.com/getsentry/sentry-switch", - string.Join("\n", missingFiles.Select(f => $" - {f}")) + target, string.Join("\n", missingFiles.Select(f => $" - {f}")) ); return; } diff --git a/src/Sentry.Unity/Properties/AssemblyInfo.cs b/src/Sentry.Unity/Properties/AssemblyInfo.cs index 2254b5e0e..9e8226ca7 100644 --- a/src/Sentry.Unity/Properties/AssemblyInfo.cs +++ b/src/Sentry.Unity/Properties/AssemblyInfo.cs @@ -3,7 +3,6 @@ [assembly: InternalsVisibleTo("Sentry.Unity.Native")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.PlayStation")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.Switch")] -[assembly: InternalsVisibleTo("Sentry.Unity.Native.Switch2")] [assembly: InternalsVisibleTo("Sentry.Unity.Native.Xbox")] [assembly: InternalsVisibleTo("Sentry.Unity.Tests")] [assembly: InternalsVisibleTo("Sentry.Unity.Editor")] diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index de5553103..6370cf816 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -50,26 +50,14 @@ internal static bool IsValid(this SentryUnityOptions options) } /// - /// RuntimePlatform.Switch2 was only added in Unity 6000.3. This assembly is compiled - /// against a single Unity version while the SDK still supports 2021.3, so Switch 2 is matched - /// by name instead of by enum member - referencing the member directly would stop the SDK from - /// building against the editors that predate it. + /// RuntimePlatform.Switch2 was only added in Unity 6000.3. /// - internal const string Switch2PlatformName = "Switch2"; - internal static bool IsSwitch2(this RuntimePlatform platform) => - string.Equals(platform.ToString(), Switch2PlatformName, StringComparison.Ordinal); + string.Equals(platform.ToString(), "Switch2", StringComparison.Ordinal); internal static bool IsNativeSupportEnabled(this SentryUnityOptions options, RuntimePlatform? platform = null) { platform ??= ApplicationAdapter.Instance.Platform; - - // Switch 2 reuses the Switch native support, and therefore its option. - if (platform.Value.IsSwitch2()) - { - return options.SwitchNativeSupportEnabled; - } - return platform switch { RuntimePlatform.Android => options.AndroidNativeSupportEnabled, @@ -80,6 +68,8 @@ internal static bool IsNativeSupportEnabled(this SentryUnityOptions options, Run RuntimePlatform.GameCoreXboxSeries or RuntimePlatform.GameCoreXboxOne => options.XboxNativeSupportEnabled, RuntimePlatform.PS5 => options.PlayStationNativeSupportEnabled, RuntimePlatform.Switch => options.SwitchNativeSupportEnabled, + // Switch 2 reuses the Switch native support. + _ when platform.Value.IsSwitch2() => options.SwitchNativeSupportEnabled, _ => false }; } diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs index cf802b657..14308c1a3 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs @@ -219,8 +219,7 @@ public void IsNativeSupportEnabled_ConsolePlatforms_ReturnsExpectedValue( [TestCase(false, false)] public void IsNativeSupportEnabled_Switch2_FollowsSwitchOption(bool optionEnabled, bool expectedResult) { - if (!Enum.TryParse( - SentryUnityOptionsExtensions.Switch2PlatformName, out var switch2)) + if (!Enum.TryParse("Switch2", out var switch2)) { Assert.Ignore("This Unity version predates 'RuntimePlatform.Switch2'."); } From e9c08ef38d4f4be87061ce648f23f977d7527d01 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 4 Sep 2026 15:43:05 +0200 Subject: [PATCH 3/3] make it work --- CHANGELOG.md | 3 +- .../io.sentry.unity.dev.runtime.asmdef | 23 ++----- .../Runtime/io.sentry.unity.runtime.asmdef | 23 ++----- .../Native/BuildPostProcess.cs | 64 +++++++++++++------ .../Native/SwitchBuildTargets.cs | 22 +++++++ .../SwitchNativePluginBuildPreProcess.cs | 23 ++----- src/Sentry.Unity.Native/SentryNativeBridge.cs | 3 +- src/Sentry.Unity/SentryUnityOptions.cs | 4 +- .../Native/SwitchNativeStubTests.cs | 36 +++++++++++ .../SentryUnityOptionsTests.cs | 33 ++++++++++ 10 files changed, 162 insertions(+), 72 deletions(-) create mode 100644 src/Sentry.Unity.Editor/Native/SwitchBuildTargets.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 62920c02a..d488ea94e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,11 @@ ### Behavioural Changes and Deprecations - The `EnableMetrics` option is now marked as `[Obsolete]` and no longer has any effect, following the .NET SDK where metrics are always enabled. Disable the individual `Auto*Metrics` options instead, or filter emitted metrics with `SetBeforeSendMetric`. ([#2828](https://github.com/getsentry/sentry-unity/pull/2828)) +- The runtime assembly is now compiled for any platform except CloudRendering, EmbeddedLinux, PS4, tvOS and Xbox One, instead of an explicit allowlist. Platforms without a dedicated integration, like visionOS and the dedicated server targets now run the SDK in managed-only mode. ([#2834](https://github.com/getsentry/sentry-unity/pull/2834)) ### Features -- Added Nintendo Switch 2 support. The SDK now recognises the platform, links the Switch 2 build of the native library from `Assets/Plugins/Sentry/Switch2/`, and uploads its debug symbols. Switch 2 shares the existing `SwitchNativeSupportEnabled` option ([#2834](https://github.com/getsentry/sentry-unity/pull/2834)) +- Added Nintendo Switch 2 support. The SDK now correctly recognises the platform, providing native support and automatic debug symbol upload. Switch 2 shares the existing `SwitchNativeSupportEnabled` option. ([#2834](https://github.com/getsentry/sentry-unity/pull/2834)) ### Fixes diff --git a/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef b/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef index 3f491e257..4e0605f2a 100644 --- a/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef +++ b/package-dev/Runtime/io.sentry.unity.dev.runtime.asmdef @@ -2,23 +2,14 @@ "name": "io.sentry.unity.dev.runtime", "rootNamespace": "", "references": [], - "includePlatforms": [ - "Android", - "Editor", - "iOS", - "LinuxStandalone64", - "macOSStandalone", - "Switch", - "Switch2", - "PS5", - "WSA", - "WebGL", - "WindowsStandalone32", - "WindowsStandalone64", - "GameCoreXboxOne", - "GameCoreScarlett" + "includePlatforms": [], + "excludePlatforms": [ + "CloudRendering", + "EmbeddedLinux", + "PS4", + "XboxOne", + "tvOS" ], - "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], diff --git a/package/Runtime/io.sentry.unity.runtime.asmdef b/package/Runtime/io.sentry.unity.runtime.asmdef index 38dd0ef4a..11acba8da 100644 --- a/package/Runtime/io.sentry.unity.runtime.asmdef +++ b/package/Runtime/io.sentry.unity.runtime.asmdef @@ -2,23 +2,14 @@ "name": "io.sentry.unity.runtime", "rootNamespace": "", "references": [], - "includePlatforms": [ - "Android", - "Editor", - "GameCoreScarlett", - "GameCoreXboxOne", - "iOS", - "LinuxStandalone64", - "macOSStandalone", - "PS5", - "Switch", - "Switch2", - "WSA", - "WebGL", - "WindowsStandalone32", - "WindowsStandalone64" + "includePlatforms": [], + "excludePlatforms": [ + "CloudRendering", + "EmbeddedLinux", + "PS4", + "XboxOne", + "tvOS" ], - "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], diff --git a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs index 371e8f4ea..3a3629fe2 100644 --- a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs @@ -19,10 +19,12 @@ public static class BuildPostProcess public static void OnPostProcessBuild(BuildTarget target, string executablePath) { var targetGroup = BuildPipeline.GetBuildTargetGroup(target); - if (targetGroup is not BuildTargetGroup.Standalone - and not BuildTargetGroup.GameCoreXboxSeries - and not BuildTargetGroup.PS5 - and not BuildTargetGroup.Switch) + var isSupportedGroup = targetGroup is BuildTargetGroup.Standalone + or BuildTargetGroup.GameCoreXboxSeries + or BuildTargetGroup.PS5 + or BuildTargetGroup.Switch + || targetGroup.IsSwitch2(); + if (!isSupportedGroup) { return; } @@ -58,6 +60,7 @@ and not BuildTargetGroup.PS5 BuildTargetGroup.GameCoreXboxSeries => executablePath, BuildTargetGroup.PS5 => executablePath, BuildTargetGroup.Switch => Path.GetDirectoryName(executablePath), + _ when targetGroup.IsSwitch2() => Path.GetDirectoryName(executablePath), _ => string.Empty }; @@ -131,6 +134,8 @@ and not BuildTargetGroup.PS5 BuildTarget.GameCoreXboxSeries or BuildTarget.GameCoreXboxOne => options.XboxNativeSupportEnabled, BuildTarget.PS5 => options.PlayStationNativeSupportEnabled, BuildTarget.Switch => options.SwitchNativeSupportEnabled, + // Switch 2 reuses the Switch native support. + _ when target.IsSwitch2() => options.SwitchNativeSupportEnabled, _ => false, }; @@ -238,6 +243,11 @@ private static IEnumerable GetNativePluginArtifact( // No standalone crash handler for Switch - uses Nintendo's crash reporter break; default: + if (target.IsSwitch2()) + { + // No standalone crash handler for Switch - uses Nintendo's crash reporter + break; + } throw new ArgumentException($"Unsupported build target: {target}"); } } @@ -526,31 +536,43 @@ private static void UploadDebugSymbols(IDiagnosticLogger logger, BuildTarget tar break; case BuildTarget.Switch: - // IL2CPP output, Managed DLLs/PDBs, and Symbols - foreach (var dir in Directory.GetDirectories(buildOutputDir, "*_BackUpThisFolder_*")) - { - AddPath(paths, dir, logger); - } + AddSwitchPaths(); + break; - // Burst - foreach (var dir in Directory.GetDirectories(buildOutputDir, "*_BurstDebugInformation_*")) + default: + if (target.IsSwitch2()) { - AddPath(paths, dir, logger); + AddSwitchPaths(); + break; } - // When exporting as an NSP the assemblies are bundled inside the package. So we're also checking the build cache. - var beePath = Path.Combine(projectDir, "Library", "Bee", "artifacts", "SwitchPlayerBuildProgram"); - AddPath(paths, beePath, logger); - - // User-provided Sentry plugin - AddPath(paths, Path.GetFullPath("Assets/Plugins/Sentry/"), logger); - break; - - default: logger.LogError("Symbol upload for '{0}' is currently not supported.", target); return; } + void AddSwitchPaths() + { + // IL2CPP output, Managed DLLs/PDBs, and Symbols + foreach (var dir in Directory.GetDirectories(buildOutputDir, "*_BackUpThisFolder_*")) + { + AddPath(paths, dir, logger); + } + + // Burst + foreach (var dir in Directory.GetDirectories(buildOutputDir, "*_BurstDebugInformation_*")) + { + AddPath(paths, dir, logger); + } + + // When exporting as an NSP the assemblies are bundled inside the package. So we're also checking the + // build cache. The build program is named after the build target, i.e. `SwitchPlayerBuildProgram`. + var beePath = Path.Combine(projectDir, "Library", "Bee", "artifacts", $"{target}PlayerBuildProgram"); + AddPath(paths, beePath, logger); + + // User-provided Sentry plugin + AddPath(paths, Path.GetFullPath("Assets/Plugins/Sentry/"), logger); + } + // Possible duplicate but check for the .pdb files that Unity stores for script assemblies in `./Temp/ManagedSymbols/`. var managedSymbolsDirectory = Path.Combine(projectDir, "Temp", "ManagedSymbols"); AddPath(paths, managedSymbolsDirectory, logger); diff --git a/src/Sentry.Unity.Editor/Native/SwitchBuildTargets.cs b/src/Sentry.Unity.Editor/Native/SwitchBuildTargets.cs new file mode 100644 index 000000000..27395bd68 --- /dev/null +++ b/src/Sentry.Unity.Editor/Native/SwitchBuildTargets.cs @@ -0,0 +1,22 @@ +using System; +using UnityEditor; + +namespace Sentry.Unity.Editor.Native; + +/// +/// BuildTarget.Switch2 and BuildTargetGroup.Switch2 only exist in Unity 6000.3 +/// and newer, so matching by name instead. +/// +internal static class SwitchBuildTargets +{ + private const string Switch2Name = "Switch2"; + + internal static bool IsSwitch2(this BuildTarget target) => + string.Equals(target.ToString(), Switch2Name, StringComparison.Ordinal); + + internal static bool IsSwitch2(this BuildTargetGroup group) => + string.Equals(group.ToString(), Switch2Name, StringComparison.Ordinal); + + internal static bool IsSwitchFamily(this BuildTarget target) => + target is BuildTarget.Switch || target.IsSwitch2(); +} diff --git a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs index 4e35a6b7a..ed03280e0 100644 --- a/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Native/SwitchNativePluginBuildPreProcess.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using System.Linq; using Sentry.Extensibility; @@ -22,24 +21,16 @@ namespace Sentry.Unity.Editor.Native; /// internal class SwitchNativePluginBuildPreProcess : IPreprocessBuildWithReport { - private static bool IsSwitchFamily(BuildTarget target) => - target == BuildTarget.Switch || IsSwitch2(target); - - /// - /// BuildTarget.Switch2 only exists in Unity 6000.3 and newer. - /// - private static bool IsSwitch2(BuildTarget target) => - string.Equals(target.ToString(), "Switch2", StringComparison.Ordinal); - /// - /// Both platforms share one stub, so the required libraries are what differ between them. + /// Both platforms share one stub, so the required libraries are what differ between them. The build target's + /// name doubles as the directory name, i.e. `Switch` and `Switch2`. /// - private static string[] RequiredFilesFor(string targetDirectory) + internal static string[] RequiredFilesFor(BuildTarget target) { return [ - $"Assets/Plugins/Sentry/{targetDirectory}/libsentry.a", - $"Assets/Plugins/Sentry/{targetDirectory}/libzstd.a" + $"Assets/Plugins/Sentry/{target}/libsentry.a", + $"Assets/Plugins/Sentry/{target}/libzstd.a" ]; } @@ -47,7 +38,7 @@ private static string[] RequiredFilesFor(string targetDirectory) public void OnPreprocessBuild(BuildReport report) { - if (!IsSwitchFamily(report.summary.platform)) + if (!report.summary.platform.IsSwitchFamily()) { return; } @@ -60,7 +51,7 @@ public void OnPreprocessBuild(BuildReport report) internal static void ConfigureStub(IDiagnosticLogger logger, bool nativeSupportEnabled, BuildTarget target) { - var requiredFiles = RequiredFilesFor(nameof(target)); + var requiredFiles = RequiredFilesFor(target); logger.LogDebug("{0} native support: checking for required files:\n{1}", target, string.Join("\n", requiredFiles.Select(f => $" - {f}"))); diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 2cf71b471..a01ddb6e7 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -31,7 +31,8 @@ public static bool Init(SentryUnityOptions options) UseLibC = Application.platform is RuntimePlatform.LinuxPlayer or RuntimePlatform.LinuxServer or RuntimePlatform.PS5 - or RuntimePlatform.Switch; + or RuntimePlatform.Switch + || Application.platform.IsSwitch2(); if ((Application.platform is RuntimePlatform.OSXPlayer or RuntimePlatform.OSXServer) && RuntimeInformation.ProcessArchitecture == Architecture.X64) diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 1cb15ad5b..4e2767342 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -638,6 +638,7 @@ or RuntimePlatform.PS5 // Special casing Xbox: `Application.persistentDataPath` returns an empty string on packaged builds. if (IsKnownPlatform(application.Platform) && application.Platform is not RuntimePlatform.Switch + && !application.Platform.IsSwitch2() && application.Platform is not RuntimePlatform.GameCoreXboxSeries && application.Platform is not RuntimePlatform.GameCoreXboxOne) { @@ -664,7 +665,8 @@ or RuntimePlatform.WebGLPlayer or RuntimePlatform.GameCoreXboxSeries or RuntimePlatform.GameCoreXboxOne or RuntimePlatform.PS5 - or RuntimePlatform.Switch; + or RuntimePlatform.Switch + || platform.Value.IsSwitch2(); } public override string ToString() diff --git a/test/Sentry.Unity.Editor.Tests/Native/SwitchNativeStubTests.cs b/test/Sentry.Unity.Editor.Tests/Native/SwitchNativeStubTests.cs index d967e2edc..a9b7371eb 100644 --- a/test/Sentry.Unity.Editor.Tests/Native/SwitchNativeStubTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Native/SwitchNativeStubTests.cs @@ -1,9 +1,12 @@ +using System; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using NUnit.Framework; +using Sentry.Unity.Editor.Native; +using UnityEditor; namespace Sentry.Unity.Editor.Tests.Native; @@ -41,4 +44,37 @@ public void Stub_ContainsEverySwitchNativeBinding() $"Switch binding '{entryPoint}' not found in {stubPath}"); } } + + [Test] + public void RequiredFilesFor_Switch_ProbesTheSwitchPluginDirectory() + { + var requiredFiles = SwitchNativePluginBuildPreProcess.RequiredFilesFor(BuildTarget.Switch); + + Assert.That(requiredFiles, Is.EquivalentTo(new[] + { + "Assets/Plugins/Sentry/Switch/libsentry.a", + "Assets/Plugins/Sentry/Switch/libzstd.a" + })); + } + + /// + /// Switch 2 is resolved by name because BuildTarget.Switch2 does not exist on the Unity versions the + /// SDK still supports, so this parses the member instead of referencing it and skips where it is unavailable. + /// + [Test] + public void RequiredFilesFor_Switch2_ProbesTheSwitch2PluginDirectory() + { + if (!Enum.TryParse("Switch2", out var switch2)) + { + Assert.Ignore("This Unity version predates 'BuildTarget.Switch2'."); + } + + var requiredFiles = SwitchNativePluginBuildPreProcess.RequiredFilesFor(switch2); + + Assert.That(requiredFiles, Is.EquivalentTo(new[] + { + "Assets/Plugins/Sentry/Switch2/libsentry.a", + "Assets/Plugins/Sentry/Switch2/libzstd.a" + })); + } } diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs index 091817e55..0959fa01c 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using NUnit.Framework; using Sentry.Unity.Integrations; @@ -55,6 +56,38 @@ public void Ctor_IfPlatformIsKnown_SetsCacheDirectoryPath(bool isKnownPlatform, Assert.AreEqual(sut.CacheDirectoryPath, expectedCacheDirectoryPath); } + /// + /// Switch 2 is resolved by name because RuntimePlatform.Switch2 does not exist on the Unity versions + /// the SDK still supports, so this parses the member instead of referencing it. + /// + private static RuntimePlatform GetSwitch2OrIgnore() + { + if (!Enum.TryParse("Switch2", out var switch2)) + { + Assert.Ignore("This Unity version predates 'RuntimePlatform.Switch2'."); + } + + return switch2; + } + + [Test] + public void IsKnownPlatform_Switch2_IsTrue() => + Assert.IsTrue(SentryUnityOptions.IsKnownPlatform(GetSwitch2OrIgnore())); + + /// + /// Just like on Switch, `Application.persistentDataPath` implicitly creates a directory and crashes. + /// + [Test] + public void Ctor_Switch2_DoesNotSetCacheDirectoryPath() + { + _fixture.Application.Platform = GetSwitch2OrIgnore(); + _fixture.Application.PersistentDataPath = "some/path"; + + var sut = _fixture.GetSut(); + + Assert.IsNull(sut.CacheDirectoryPath); + } + [Test] public void Ctor_IsGlobalModeEnabled_IsTrue() => Assert.IsTrue(_fixture.GetSut().IsGlobalModeEnabled);