From c1441b857a1a50ebbb02de2bc109b1b1dd0db6b5 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 7 Aug 2026 14:37:57 +0200 Subject: [PATCH 01/16] write additional unitylinker args --- .../Il2CppBuildPreProcess.cs | 44 ++++++++++++++++++- .../Il2CppBuildPreProcess.cs | 39 ++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index 5c41aa1cd..ccda18949 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -6,10 +6,14 @@ namespace Sentry.Unity.Editor; -internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport +internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport, IPostprocessBuildWithReport { internal const string SourceMappingArgument = "--emit-source-mapping"; + internal const string LinkSymbolsArgument = "--link-symbols"; + internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; private static IDiagnosticLogger? Logger; + private static string? UnityLinkerArgumentsBeforeBuild; + private static bool UnityLinkerArgumentsChanged; public int callbackOrder => 0; @@ -33,6 +37,28 @@ public void OnPreprocessBuild(BuildReport report) SetAdditionalIl2CppArguments(options, PlayerSettings.GetAdditionalIl2CppArgs, PlayerSettings.SetAdditionalIl2CppArgs); + + if (!options.Il2CppLineNumberSupportEnabled) + { + return; + } + + UnityLinkerArgumentsBeforeBuild = Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable); + UnityLinkerArgumentsChanged = AddUnityLinkerArgument( + () => UnityLinkerArgumentsBeforeBuild, + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); + } + + public void OnPostprocessBuild(BuildReport report) + { + if (!UnityLinkerArgumentsChanged) + { + return; + } + + Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, UnityLinkerArgumentsBeforeBuild); + UnityLinkerArgumentsBeforeBuild = null; + UnityLinkerArgumentsChanged = false; } internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) @@ -62,4 +88,20 @@ internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Fu } } } + + internal static bool AddUnityLinkerArgument(Func getArguments, Action setArguments) + { + var arguments = getArguments.Invoke(); + if (arguments?.IndexOf(LinkSymbolsArgument, StringComparison.Ordinal) >= 0) + { + Logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); + return false; + } + + Logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); + setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) + ? LinkSymbolsArgument + : $"{arguments} {LinkSymbolsArgument}"); + return true; + } } diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 78bcd81d1..f9738f2d2 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -82,4 +82,43 @@ public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_Removes Assert.That(resultingArguments, Does.Contain(expectedArgument)); Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); } + + [Test] + public void AddUnityLinkerArgument_AddsLinkSymbolsArgument() + { + string? unityLinkerArguments = null; + + var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(changed, Is.True); + Assert.That(unityLinkerArguments, Is.EqualTo(Il2CppBuildPreProcess.LinkSymbolsArgument)); + } + + [Test] + public void AddUnityLinkerArgument_PreservesExistingArguments() + { + string? unityLinkerArguments = "--existing-argument"; + + var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(changed, Is.True); + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}")); + } + + [Test] + public void AddUnityLinkerArgument_DoesNotDuplicateLinkSymbolsArgument() + { + string? unityLinkerArguments = $"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}"; + + var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(changed, Is.False); + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}")); + } } From 8a213d14764e9a621df10bd1e9d925794eaf6dc6 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 7 Aug 2026 15:49:28 +0200 Subject: [PATCH 02/16] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19cb3e192..4a40ccae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- The SDK now provides line number support for managed exceptions and native crashes for Unity 6.5 and newer ([#2805](https://github.com/getsentry/sentry-unity/pull/2805)) - Added experimental auto game-metrics. When enabled, the SDK periodically collects common performance metrics and sends them to Sentry via the metrics API. ([#2777](https://github.com/getsentry/sentry-unity/pull/2777)) ### Dependencies From ba2dae7dfe6a4f0576c808537229ec9baa9b7572 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 7 Aug 2026 17:16:45 +0200 Subject: [PATCH 03/16] test for stack trace --- test/IntegrationTest/Integration.Tests.ps1 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index 107993098..bf4d2a530 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -348,6 +348,21 @@ Describe "Unity $($env:SENTRY_TEST_PLATFORM) Integration Tests" { $exception.stacktrace | Should -Not -BeNullOrEmpty } + It "Resolves the throw frame to its source line" { + if ($script:Platform -eq "WebGL") { + Set-ItResult -Skipped -Because "IL2CPP line number support is unsupported on WebGL" + return + } + + $frame = $runEvent.exception.values[0].stacktrace.frames | + Where-Object { $_.function -match "IntegrationTester.*ThrowException" } | + Select-Object -First 1 + + $frame | Should -Not -BeNullOrEmpty + $frame.abs_path | Should -Match "[\\/]Assets[\\/]Scripts[\\/]IntegrationTester\.cs$" + $frame.lineno | Should -Be 218 + } + It "Has error level" { ($runEvent.tags | Where-Object { $_.key -eq "level" }).value | Should -Be "error" } From e57d9bfa5a795b80e4ae973c497db5a060479a38 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 7 Aug 2026 17:59:09 +0200 Subject: [PATCH 04/16] fix assertion --- test/IntegrationTest/Integration.Tests.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index bf4d2a530..f2bb4d44e 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -355,12 +355,13 @@ Describe "Unity $($env:SENTRY_TEST_PLATFORM) Integration Tests" { } $frame = $runEvent.exception.values[0].stacktrace.frames | - Where-Object { $_.function -match "IntegrationTester.*ThrowException" } | + Where-Object { $_.module -eq "IntegrationTester" -and $_.function -eq "ThrowException" } | Select-Object -First 1 $frame | Should -Not -BeNullOrEmpty - $frame.abs_path | Should -Match "[\\/]Assets[\\/]Scripts[\\/]IntegrationTester\.cs$" - $frame.lineno | Should -Be 218 + $frame.absPath | Should -Match "[\\/]Assets[\\/]Scripts[\\/]IntegrationTester\.cs$" + $frame.lineNo | Should -Be 218 + $frame.symbolicatorStatus | Should -Be "symbolicated" } It "Has error level" { From d5cd2e1910391e57f1cc1b46755bddb5329c110b Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 10 Aug 2026 15:13:50 +0200 Subject: [PATCH 05/16] Probe Linux linker environment Start Unity Editor with --link-symbols in its process environment to determine whether Linux Bee reads the setting before build preprocessing. Refs #2805 Co-Authored-By: OpenCode --- .github/workflows/test-build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-build-linux.yml b/.github/workflows/test-build-linux.yml index 5535dc050..8af42dc25 100644 --- a/.github/workflows/test-build-linux.yml +++ b/.github/workflows/test-build-linux.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest env: GITHUB_ACTOR: ${{ github.actor }} - UNITY_PATH: docker exec unity unity-editor + UNITY_PATH: docker exec -e UNITYLINKER_ADDITIONAL_ARGS=--link-symbols unity unity-editor UNITY_VERSION: ${{ inputs.unity-version }} BUILD_PLATFORM: Linux From 856bf42fc3a0e35cf508fa727681489b7d951dcc Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 10 Aug 2026 15:32:42 +0200 Subject: [PATCH 06/16] Revert Linux linker environment probe The process-start linker argument reached UnityLinker but did not restore Linux source resolution, so remove the temporary CI override. Refs #2805 Co-Authored-By: OpenCode --- .github/workflows/test-build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-build-linux.yml b/.github/workflows/test-build-linux.yml index 8af42dc25..5535dc050 100644 --- a/.github/workflows/test-build-linux.yml +++ b/.github/workflows/test-build-linux.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest env: GITHUB_ACTOR: ${{ github.actor }} - UNITY_PATH: docker exec -e UNITYLINKER_ADDITIONAL_ARGS=--link-symbols unity unity-editor + UNITY_PATH: docker exec unity unity-editor UNITY_VERSION: ${{ inputs.unity-version }} BUILD_PLATFORM: Linux From 78447611a56960f055a8881e407d7daae11416f0 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 10 Aug 2026 15:51:14 +0200 Subject: [PATCH 07/16] exclude linux from line number test --- test/IntegrationTest/Integration.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index f2bb4d44e..80cb8aaec 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -349,8 +349,8 @@ Describe "Unity $($env:SENTRY_TEST_PLATFORM) Integration Tests" { } It "Resolves the throw frame to its source line" { - if ($script:Platform -eq "WebGL") { - Set-ItResult -Skipped -Because "IL2CPP line number support is unsupported on WebGL" + if ($script:Platform -in "WebGL", "Linux") { + Set-ItResult -Skipped -Because "Source-line assertions are unsupported on $script:Platform" return } From 1f346cb2daeb3e5a768dc6ed6f6c1be81b63a7ce Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 10 Aug 2026 17:56:19 +0200 Subject: [PATCH 08/16] moved argument adding to different interface --- .../Il2CppBuildPreProcess.cs | 93 +++++++++++++------ .../Il2CppBuildPreProcess.cs | 20 ++-- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index ccda18949..5aad1c14a 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -3,17 +3,14 @@ using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; +using UnityEditor.UnityLinker; namespace Sentry.Unity.Editor; -internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport, IPostprocessBuildWithReport +internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport { internal const string SourceMappingArgument = "--emit-source-mapping"; - internal const string LinkSymbolsArgument = "--link-symbols"; - internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; private static IDiagnosticLogger? Logger; - private static string? UnityLinkerArgumentsBeforeBuild; - private static bool UnityLinkerArgumentsChanged; public int callbackOrder => 0; @@ -37,28 +34,6 @@ public void OnPreprocessBuild(BuildReport report) SetAdditionalIl2CppArguments(options, PlayerSettings.GetAdditionalIl2CppArgs, PlayerSettings.SetAdditionalIl2CppArgs); - - if (!options.Il2CppLineNumberSupportEnabled) - { - return; - } - - UnityLinkerArgumentsBeforeBuild = Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable); - UnityLinkerArgumentsChanged = AddUnityLinkerArgument( - () => UnityLinkerArgumentsBeforeBuild, - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); - } - - public void OnPostprocessBuild(BuildReport report) - { - if (!UnityLinkerArgumentsChanged) - { - return; - } - - Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, UnityLinkerArgumentsBeforeBuild); - UnityLinkerArgumentsBeforeBuild = null; - UnityLinkerArgumentsChanged = false; } internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) @@ -89,16 +64,74 @@ internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Fu } } - internal static bool AddUnityLinkerArgument(Func getArguments, Action setArguments) +} + +public sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor, IPostprocessBuildWithReport +{ + internal const string LinkSymbolsArgument = "--link-symbols"; + internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; + private static string? UnityLinkerArgumentsBeforeBuild; + private static bool UnityLinkerArgumentsChanged; + + public int callbackOrder => 0; + + public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) + { + if (UnityLinkerArgumentsChanged) + { + return string.Empty; + } + + var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(report.summary.platformGroup); + if (PlayerSettings.GetScriptingBackend(namedBuildTarget) != ScriptingImplementation.IL2CPP) + { + return string.Empty; + } + + var options = SentryScriptableObject.LoadOptions(isBuilding: true); + if (options is null || !options.Il2CppLineNumberSupportEnabled) + { + return string.Empty; + } + + var argumentsBeforeBuild = Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable); + if (AddLinkSymbolsArgument( + () => argumentsBeforeBuild, + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments), + options.DiagnosticLogger)) + { + UnityLinkerArgumentsBeforeBuild = argumentsBeforeBuild; + UnityLinkerArgumentsChanged = true; + } + + return string.Empty; + } + + public void OnPostprocessBuild(BuildReport report) + { + if (!UnityLinkerArgumentsChanged) + { + return; + } + + Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, UnityLinkerArgumentsBeforeBuild); + UnityLinkerArgumentsBeforeBuild = null; + UnityLinkerArgumentsChanged = false; + } + + internal static bool AddLinkSymbolsArgument( + Func getArguments, + Action setArguments, + IDiagnosticLogger? logger = null) { var arguments = getArguments.Invoke(); if (arguments?.IndexOf(LinkSymbolsArgument, StringComparison.Ordinal) >= 0) { - Logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); + logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); return false; } - Logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); + logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) ? LinkSymbolsArgument : $"{arguments} {LinkSymbolsArgument}"); diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index f9738f2d2..899461ee5 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -84,41 +84,41 @@ public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_Removes } [Test] - public void AddUnityLinkerArgument_AddsLinkSymbolsArgument() + public void AddLinkSymbolsArgument_AddsArgument() { string? unityLinkerArguments = null; - var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( () => unityLinkerArguments, arguments => unityLinkerArguments = arguments); Assert.That(changed, Is.True); - Assert.That(unityLinkerArguments, Is.EqualTo(Il2CppBuildPreProcess.LinkSymbolsArgument)); + Assert.That(unityLinkerArguments, Is.EqualTo(SentryUnityLinkerProcessor.LinkSymbolsArgument)); } [Test] - public void AddUnityLinkerArgument_PreservesExistingArguments() + public void AddLinkSymbolsArgument_PreservesExistingArguments() { string? unityLinkerArguments = "--existing-argument"; - var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( () => unityLinkerArguments, arguments => unityLinkerArguments = arguments); Assert.That(changed, Is.True); - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}")); + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); } [Test] - public void AddUnityLinkerArgument_DoesNotDuplicateLinkSymbolsArgument() + public void AddLinkSymbolsArgument_DoesNotDuplicateArgument() { - string? unityLinkerArguments = $"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}"; + string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; - var changed = Il2CppBuildPreProcess.AddUnityLinkerArgument( + var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( () => unityLinkerArguments, arguments => unityLinkerArguments = arguments); Assert.That(changed, Is.False); - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {Il2CppBuildPreProcess.LinkSymbolsArgument}")); + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); } } From 461d481d950f9d297cb91c2322d3e33010c122e7 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 10 Aug 2026 18:50:28 +0200 Subject: [PATCH 09/16] revert skip because we are so back --- test/IntegrationTest/Integration.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index 80cb8aaec..2263d3024 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -349,7 +349,7 @@ Describe "Unity $($env:SENTRY_TEST_PLATFORM) Integration Tests" { } It "Resolves the throw frame to its source line" { - if ($script:Platform -in "WebGL", "Linux") { + if ($script:Platform -in "WebGL") { Set-ItResult -Skipped -Because "Source-line assertions are unsupported on $script:Platform" return } From 8b554880423ec244652237fe5c6a7636adedb026 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 11 Aug 2026 14:16:43 +0200 Subject: [PATCH 10/16] cleanup --- CHANGELOG.md | 2 +- .../Il2CppBuildPreProcess.cs | 75 --------------- .../SentryUnityLinkerProcessor.cs | 78 ++++++++++++++++ test/IntegrationTest/Integration.Tests.ps1 | 2 +- .../Scripts/IntegrationTester.cs | 1 + .../Il2CppBuildPreProcess.cs | 39 -------- .../SentryUnityLinkerProcessorTests.cs | 93 +++++++++++++++++++ 7 files changed, 174 insertions(+), 116 deletions(-) create mode 100644 src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs create mode 100644 test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 16a3a38a5..8115f66fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- The SDK now provides line number support for managed exceptions and native crashes for Unity 6.5 and newer ([#2805](https://github.com/getsentry/sentry-unity/pull/2805)) +- The SDK now provides line number support for managed exceptions for Unity 6.5 and newer ([#2805](https://github.com/getsentry/sentry-unity/pull/2805)) - Added experimental auto game-metrics. When enabled, the SDK periodically collects common performance metrics and sends them to Sentry via the metrics API. ([#2777](https://github.com/getsentry/sentry-unity/pull/2777)) ### Dependencies diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index 5aad1c14a..5c41aa1cd 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -3,7 +3,6 @@ using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; -using UnityEditor.UnityLinker; namespace Sentry.Unity.Editor; @@ -63,78 +62,4 @@ internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Fu } } } - -} - -public sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor, IPostprocessBuildWithReport -{ - internal const string LinkSymbolsArgument = "--link-symbols"; - internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; - private static string? UnityLinkerArgumentsBeforeBuild; - private static bool UnityLinkerArgumentsChanged; - - public int callbackOrder => 0; - - public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) - { - if (UnityLinkerArgumentsChanged) - { - return string.Empty; - } - - var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(report.summary.platformGroup); - if (PlayerSettings.GetScriptingBackend(namedBuildTarget) != ScriptingImplementation.IL2CPP) - { - return string.Empty; - } - - var options = SentryScriptableObject.LoadOptions(isBuilding: true); - if (options is null || !options.Il2CppLineNumberSupportEnabled) - { - return string.Empty; - } - - var argumentsBeforeBuild = Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable); - if (AddLinkSymbolsArgument( - () => argumentsBeforeBuild, - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments), - options.DiagnosticLogger)) - { - UnityLinkerArgumentsBeforeBuild = argumentsBeforeBuild; - UnityLinkerArgumentsChanged = true; - } - - return string.Empty; - } - - public void OnPostprocessBuild(BuildReport report) - { - if (!UnityLinkerArgumentsChanged) - { - return; - } - - Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, UnityLinkerArgumentsBeforeBuild); - UnityLinkerArgumentsBeforeBuild = null; - UnityLinkerArgumentsChanged = false; - } - - internal static bool AddLinkSymbolsArgument( - Func getArguments, - Action setArguments, - IDiagnosticLogger? logger = null) - { - var arguments = getArguments.Invoke(); - if (arguments?.IndexOf(LinkSymbolsArgument, StringComparison.Ordinal) >= 0) - { - logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); - return false; - } - - logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); - setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) - ? LinkSymbolsArgument - : $"{arguments} {LinkSymbolsArgument}"); - return true; - } } diff --git a/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs b/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs new file mode 100644 index 000000000..196856c1a --- /dev/null +++ b/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs @@ -0,0 +1,78 @@ +using System; +using Sentry.Extensibility; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEditor.UnityLinker; + +namespace Sentry.Unity.Editor; + +internal sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor, IPostprocessBuildWithReport +{ + internal const string LinkSymbolsArgument = "--link-symbols"; + internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; + + public int callbackOrder => 0; + + public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) + { + var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(report.summary.platformGroup); + if (PlayerSettings.GetScriptingBackend(namedBuildTarget) != ScriptingImplementation.IL2CPP) + { + return string.Empty; + } + + var options = SentryScriptableObject.LoadOptions(isBuilding: true); + if (options is null || !options.Il2CppLineNumberSupportEnabled) + { + return string.Empty; + } + + AddLinkSymbolsArgument( + () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments), + options.DiagnosticLogger); + + return string.Empty; + } + + public void OnPostprocessBuild(BuildReport report) + { + RemoveLinkSymbolsArgument( + () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); + } + + internal static void AddLinkSymbolsArgument( + Func getArguments, + Action setArguments, + IDiagnosticLogger? logger = null) + { + var arguments = getArguments.Invoke(); + if (arguments?.Contains(LinkSymbolsArgument) == true) + { + logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); + return; + } + + logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); + setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) + ? LinkSymbolsArgument + : $"{arguments} {LinkSymbolsArgument}"); + } + + internal static void RemoveLinkSymbolsArgument( + Func getArguments, + Action setArguments, + IDiagnosticLogger? logger = null) + { + var arguments = getArguments.Invoke(); + if (arguments?.Contains(LinkSymbolsArgument) != true) + { + return; + } + + logger?.LogDebug("Removing additional UnityLinker argument '{0}'.", LinkSymbolsArgument); + setArguments.Invoke(arguments.Replace(LinkSymbolsArgument, "").Trim()); + } +} diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index 2263d3024..94c426eac 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -360,7 +360,7 @@ Describe "Unity $($env:SENTRY_TEST_PLATFORM) Integration Tests" { $frame | Should -Not -BeNullOrEmpty $frame.absPath | Should -Match "[\\/]Assets[\\/]Scripts[\\/]IntegrationTester\.cs$" - $frame.lineNo | Should -Be 218 + $frame.lineNo | Should -Be 219 $frame.symbolicatorStatus | Should -Be "symbolicated" } diff --git a/test/Scripts.Integration.Test/Scripts/IntegrationTester.cs b/test/Scripts.Integration.Test/Scripts/IntegrationTester.cs index fe38471cb..3a3a38f93 100644 --- a/test/Scripts.Integration.Test/Scripts/IntegrationTester.cs +++ b/test/Scripts.Integration.Test/Scripts/IntegrationTester.cs @@ -215,6 +215,7 @@ private static void DoSomeWork() [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowException() { + // Integration.Tests.ps1 asserts this throw's line number. Update it when moving this. throw new InvalidOperationException("Integration test exception"); } diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 899461ee5..78bcd81d1 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -82,43 +82,4 @@ public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_Removes Assert.That(resultingArguments, Does.Contain(expectedArgument)); Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); } - - [Test] - public void AddLinkSymbolsArgument_AddsArgument() - { - string? unityLinkerArguments = null; - - var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(changed, Is.True); - Assert.That(unityLinkerArguments, Is.EqualTo(SentryUnityLinkerProcessor.LinkSymbolsArgument)); - } - - [Test] - public void AddLinkSymbolsArgument_PreservesExistingArguments() - { - string? unityLinkerArguments = "--existing-argument"; - - var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(changed, Is.True); - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); - } - - [Test] - public void AddLinkSymbolsArgument_DoesNotDuplicateArgument() - { - string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; - - var changed = SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(changed, Is.False); - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); - } } diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs new file mode 100644 index 000000000..c0a6c84b8 --- /dev/null +++ b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs @@ -0,0 +1,93 @@ +using NUnit.Framework; + +namespace Sentry.Unity.Editor.Tests; + +public class SentryUnityLinkerProcessorTests +{ + [Test] + public void AddLinkSymbolsArgument_NoExistingArguments_AddsArgument() + { + string? unityLinkerArguments = null; + + SentryUnityLinkerProcessor.AddLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo(SentryUnityLinkerProcessor.LinkSymbolsArgument)); + } + + [Test] + public void AddLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() + { + string? unityLinkerArguments = "--existing-argument"; + + SentryUnityLinkerProcessor.AddLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); + } + + [Test] + public void AddLinkSymbolsArgument_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() + { + string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; + + SentryUnityLinkerProcessor.AddLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); + } + + [Test] + public void RemoveLinkSymbolsArgument_OnlyArgument_RemovesArgument() + { + string? unityLinkerArguments = SentryUnityLinkerProcessor.LinkSymbolsArgument; + + SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.Empty); + } + + [Test] + public void RemoveLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() + { + string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; + + SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); + } + + [Test] + public void RemoveLinkSymbolsArgument_ArgumentNotPresent_DoesNotModifyArguments() + { + string? unityLinkerArguments = "--existing-argument"; + + SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); + } + + [Test] + public void RemoveLinkSymbolsArgument_ArgumentPreviouslyAdded_RestoresOriginalArguments() + { + string? unityLinkerArguments = "--existing-argument"; + + SentryUnityLinkerProcessor.AddLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( + () => unityLinkerArguments, + arguments => unityLinkerArguments = arguments); + + Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); + } +} From a3d2f7d9b97b2aa7314c302f849e5f31605181af Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 10:46:32 +0200 Subject: [PATCH 11/16] simplified argument adding --- .../SentryUnityLinkerProcessor.cs | 24 +-------- .../SentryUnityLinkerProcessorTests.cs | 51 ------------------- 2 files changed, 1 insertion(+), 74 deletions(-) diff --git a/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs b/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs index 196856c1a..4934535b1 100644 --- a/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs +++ b/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs @@ -7,7 +7,7 @@ namespace Sentry.Unity.Editor; -internal sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor, IPostprocessBuildWithReport +internal sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor { internal const string LinkSymbolsArgument = "--link-symbols"; internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; @@ -36,13 +36,6 @@ public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuild return string.Empty; } - public void OnPostprocessBuild(BuildReport report) - { - RemoveLinkSymbolsArgument( - () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); - } - internal static void AddLinkSymbolsArgument( Func getArguments, Action setArguments, @@ -60,19 +53,4 @@ internal static void AddLinkSymbolsArgument( ? LinkSymbolsArgument : $"{arguments} {LinkSymbolsArgument}"); } - - internal static void RemoveLinkSymbolsArgument( - Func getArguments, - Action setArguments, - IDiagnosticLogger? logger = null) - { - var arguments = getArguments.Invoke(); - if (arguments?.Contains(LinkSymbolsArgument) != true) - { - return; - } - - logger?.LogDebug("Removing additional UnityLinker argument '{0}'.", LinkSymbolsArgument); - setArguments.Invoke(arguments.Replace(LinkSymbolsArgument, "").Trim()); - } } diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs index c0a6c84b8..fe672597b 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs @@ -39,55 +39,4 @@ public void AddLinkSymbolsArgument_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); } - - [Test] - public void RemoveLinkSymbolsArgument_OnlyArgument_RemovesArgument() - { - string? unityLinkerArguments = SentryUnityLinkerProcessor.LinkSymbolsArgument; - - SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.Empty); - } - - [Test] - public void RemoveLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() - { - string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; - - SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); - } - - [Test] - public void RemoveLinkSymbolsArgument_ArgumentNotPresent_DoesNotModifyArguments() - { - string? unityLinkerArguments = "--existing-argument"; - - SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); - } - - [Test] - public void RemoveLinkSymbolsArgument_ArgumentPreviouslyAdded_RestoresOriginalArguments() - { - string? unityLinkerArguments = "--existing-argument"; - - SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - SentryUnityLinkerProcessor.RemoveLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo("--existing-argument")); - } } From 064448055786dd858c96b1ba6aa421993732eab7 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 11:25:35 +0200 Subject: [PATCH 12/16] moved argument addition into editor --- .gitignore | 3 + .../SentryUnityLinkerArguments.cs | 34 +++++++++++ .../SentryUnityLinkerProcessor.cs | 56 ------------------- .../SentryUnityLinkerArgumentsTests.cs | 36 ++++++++++++ .../SentryUnityLinkerProcessorTests.cs | 42 -------------- 5 files changed, 73 insertions(+), 98 deletions(-) create mode 100644 src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs delete mode 100644 src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs create mode 100644 test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs delete mode 100644 test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs diff --git a/.gitignore b/.gitignore index c1a7a4b1e..8ba14ec97 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,6 @@ test/Sentry.Unity.Tests/other/** samples/unity-of-bugs/*IL2CPPCache/ samples/unity-of-bugs/*IL2CPPStats/ *Player.link.log + +# Gradle caches generated by the VS Code Gradle extension scanning test fixtures +.gradle/ diff --git a/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs b/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs new file mode 100644 index 000000000..fcbe261c7 --- /dev/null +++ b/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs @@ -0,0 +1,34 @@ +using System; +using UnityEditor; + +namespace Sentry.Unity.Editor; + +/// +/// The UnityLinker picks additional arguments up from the environment. Unity spawns the Bee backend that ultimately +/// invokes the linker as a child process, so the environment has to be set before that happens. Build callbacks run +/// too close to the backend getting spawned to reliably win that race, which is why this runs on editor startup and +/// on every domain reload instead. +/// +[InitializeOnLoad] +internal static class SentryUnityLinkerArguments +{ + internal const string LinkSymbolsArgument = "--link-symbols"; + internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; + + static SentryUnityLinkerArguments() => AddLinkSymbolsArgument( + () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); + + internal static void AddLinkSymbolsArgument(Func getArguments, Action setArguments) + { + var arguments = getArguments.Invoke(); + if (arguments?.Contains(LinkSymbolsArgument) == true) + { + return; + } + + setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) + ? LinkSymbolsArgument + : $"{arguments} {LinkSymbolsArgument}"); + } +} diff --git a/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs b/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs deleted file mode 100644 index 4934535b1..000000000 --- a/src/Sentry.Unity.Editor/SentryUnityLinkerProcessor.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using Sentry.Extensibility; -using UnityEditor; -using UnityEditor.Build; -using UnityEditor.Build.Reporting; -using UnityEditor.UnityLinker; - -namespace Sentry.Unity.Editor; - -internal sealed class SentryUnityLinkerProcessor : IUnityLinkerProcessor -{ - internal const string LinkSymbolsArgument = "--link-symbols"; - internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; - - public int callbackOrder => 0; - - public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) - { - var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(report.summary.platformGroup); - if (PlayerSettings.GetScriptingBackend(namedBuildTarget) != ScriptingImplementation.IL2CPP) - { - return string.Empty; - } - - var options = SentryScriptableObject.LoadOptions(isBuilding: true); - if (options is null || !options.Il2CppLineNumberSupportEnabled) - { - return string.Empty; - } - - AddLinkSymbolsArgument( - () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments), - options.DiagnosticLogger); - - return string.Empty; - } - - internal static void AddLinkSymbolsArgument( - Func getArguments, - Action setArguments, - IDiagnosticLogger? logger = null) - { - var arguments = getArguments.Invoke(); - if (arguments?.Contains(LinkSymbolsArgument) == true) - { - logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); - return; - } - - logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); - setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) - ? LinkSymbolsArgument - : $"{arguments} {LinkSymbolsArgument}"); - } -} diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs new file mode 100644 index 000000000..a4a425efc --- /dev/null +++ b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs @@ -0,0 +1,36 @@ +using NUnit.Framework; + +namespace Sentry.Unity.Editor.Tests; + +public class SentryUnityLinkerArgumentsTests +{ + [Test] + public void AddLinkSymbolsArgument_NoExistingArguments_AddsArgument() + { + string? arguments = null; + + SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); + + Assert.That(arguments, Is.EqualTo(SentryUnityLinkerArguments.LinkSymbolsArgument)); + } + + [Test] + public void AddLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() + { + string? arguments = "--MyArgument"; + + SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); + + Assert.That(arguments, Is.EqualTo($"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}")); + } + + [Test] + public void AddLinkSymbolsArgument_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() + { + string? arguments = $"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}"; + + SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); + + Assert.That(arguments, Is.EqualTo($"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}")); + } +} diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs deleted file mode 100644 index fe672597b..000000000 --- a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerProcessorTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -using NUnit.Framework; - -namespace Sentry.Unity.Editor.Tests; - -public class SentryUnityLinkerProcessorTests -{ - [Test] - public void AddLinkSymbolsArgument_NoExistingArguments_AddsArgument() - { - string? unityLinkerArguments = null; - - SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo(SentryUnityLinkerProcessor.LinkSymbolsArgument)); - } - - [Test] - public void AddLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() - { - string? unityLinkerArguments = "--existing-argument"; - - SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); - } - - [Test] - public void AddLinkSymbolsArgument_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() - { - string? unityLinkerArguments = $"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}"; - - SentryUnityLinkerProcessor.AddLinkSymbolsArgument( - () => unityLinkerArguments, - arguments => unityLinkerArguments = arguments); - - Assert.That(unityLinkerArguments, Is.EqualTo($"--existing-argument {SentryUnityLinkerProcessor.LinkSymbolsArgument}")); - } -} From f086b66ec7710d1d089e73517d3094438dfcf495 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 12:02:11 +0200 Subject: [PATCH 13/16] moved into the il2cpp processor again --- .../Il2CppBuildPreProcess.cs | 32 ++++++++++++++ .../SentryUnityLinkerArguments.cs | 34 --------------- .../Il2CppBuildPreProcess.cs | 42 +++++++++++++++++++ .../SentryUnityLinkerArgumentsTests.cs | 36 ---------------- 4 files changed, 74 insertions(+), 70 deletions(-) delete mode 100644 src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs delete mode 100644 test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index 5c41aa1cd..be633e33b 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -9,6 +9,8 @@ namespace Sentry.Unity.Editor; internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport { internal const string SourceMappingArgument = "--emit-source-mapping"; + internal const string LinkSymbolsArgument = "--link-symbols"; + internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; private static IDiagnosticLogger? Logger; public int callbackOrder => 0; @@ -33,6 +35,36 @@ public void OnPreprocessBuild(BuildReport report) SetAdditionalIl2CppArguments(options, PlayerSettings.GetAdditionalIl2CppArgs, PlayerSettings.SetAdditionalIl2CppArgs); + + // The UnityLinker picks its additional arguments up from the environment. The Bee build program bakes the + // resulting command line into its build graph, so this has to happen before that graph gets generated. + SetAdditionalUnityLinkerArguments(options, + () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), + arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); + + Logger?.LogDebug("'{0}' is now '{1}'.", + UnityLinkerAdditionalArgumentsEnvironmentVariable, + Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable)); + } + + internal static void SetAdditionalUnityLinkerArguments(SentryUnityOptions options, Func getArguments, Action setArguments) + { + if (!options.Il2CppLineNumberSupportEnabled) + { + return; + } + + var arguments = getArguments.Invoke(); + if (arguments?.Contains(LinkSymbolsArgument) == true) + { + Logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); + return; + } + + Logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); + setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) + ? LinkSymbolsArgument + : $"{arguments} {LinkSymbolsArgument}"); } internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) diff --git a/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs b/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs deleted file mode 100644 index fcbe261c7..000000000 --- a/src/Sentry.Unity.Editor/SentryUnityLinkerArguments.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using UnityEditor; - -namespace Sentry.Unity.Editor; - -/// -/// The UnityLinker picks additional arguments up from the environment. Unity spawns the Bee backend that ultimately -/// invokes the linker as a child process, so the environment has to be set before that happens. Build callbacks run -/// too close to the backend getting spawned to reliably win that race, which is why this runs on editor startup and -/// on every domain reload instead. -/// -[InitializeOnLoad] -internal static class SentryUnityLinkerArguments -{ - internal const string LinkSymbolsArgument = "--link-symbols"; - internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; - - static SentryUnityLinkerArguments() => AddLinkSymbolsArgument( - () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); - - internal static void AddLinkSymbolsArgument(Func getArguments, Action setArguments) - { - var arguments = getArguments.Invoke(); - if (arguments?.Contains(LinkSymbolsArgument) == true) - { - return; - } - - setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) - ? LinkSymbolsArgument - : $"{arguments} {LinkSymbolsArgument}"); - } -} diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 78bcd81d1..97d779bfe 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -82,4 +82,46 @@ public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_Removes Assert.That(resultingArguments, Does.Contain(expectedArgument)); Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); } + + [Test] + public void SetAdditionalUnityLinkerArguments_Il2CppEnabled_AddsArgument() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + + Il2CppBuildPreProcess.SetAdditionalUnityLinkerArguments(options, () => null, s => resultingArguments = s); + + Assert.That(resultingArguments, Is.EqualTo(Il2CppBuildPreProcess.LinkSymbolsArgument)); + } + + [Test] + public void SetAdditionalUnityLinkerArguments_Il2CppDisabled_DoesNotAddArgument() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; + + Il2CppBuildPreProcess.SetAdditionalUnityLinkerArguments(options, () => null, s => resultingArguments = s); + + Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.LinkSymbolsArgument)); + } + + [Test] + public void SetAdditionalUnityLinkerArguments_Il2CppEnabled_ExistingArgumentsDoNotGetOverwritten() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + var expectedArgument = "--MyArgument"; + + Il2CppBuildPreProcess.SetAdditionalUnityLinkerArguments(options, () => expectedArgument, s => resultingArguments = s); + + Assert.That(resultingArguments, Is.EqualTo($"{expectedArgument} {Il2CppBuildPreProcess.LinkSymbolsArgument}")); + } + + [Test] + public void SetAdditionalUnityLinkerArguments_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + arguments = $"--MyArgument {Il2CppBuildPreProcess.LinkSymbolsArgument}"; + + Il2CppBuildPreProcess.SetAdditionalUnityLinkerArguments(options, () => arguments, s => resultingArguments = s); + + Assert.That(resultingArguments, Is.Empty); + } } diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs deleted file mode 100644 index a4a425efc..000000000 --- a/test/Sentry.Unity.Editor.Tests/SentryUnityLinkerArgumentsTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using NUnit.Framework; - -namespace Sentry.Unity.Editor.Tests; - -public class SentryUnityLinkerArgumentsTests -{ - [Test] - public void AddLinkSymbolsArgument_NoExistingArguments_AddsArgument() - { - string? arguments = null; - - SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); - - Assert.That(arguments, Is.EqualTo(SentryUnityLinkerArguments.LinkSymbolsArgument)); - } - - [Test] - public void AddLinkSymbolsArgument_ExistingArguments_PreservesExistingArguments() - { - string? arguments = "--MyArgument"; - - SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); - - Assert.That(arguments, Is.EqualTo($"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}")); - } - - [Test] - public void AddLinkSymbolsArgument_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() - { - string? arguments = $"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}"; - - SentryUnityLinkerArguments.AddLinkSymbolsArgument(() => arguments, s => arguments = s); - - Assert.That(arguments, Is.EqualTo($"--MyArgument {SentryUnityLinkerArguments.LinkSymbolsArgument}")); - } -} From 93451fea358a4a73310c497e3c56262dda5d1527 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 13:27:38 +0200 Subject: [PATCH 14/16] is it caching? --- .github/workflows/test-build-linux.yml | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test-build-linux.yml b/.github/workflows/test-build-linux.yml index 5535dc050..0ad63b409 100644 --- a/.github/workflows/test-build-linux.yml +++ b/.github/workflows/test-build-linux.yml @@ -63,31 +63,31 @@ jobs: - name: Extract project archive run: tar -xvzf test-project.tar.gz - - name: Restore Unity Library cache - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: samples/IntegrationTest/Library - key: it-library-ubuntu-${{ env.UNITY_VERSION }}-${{ github.run_id }} - restore-keys: | - it-library-ubuntu-${{ env.UNITY_VERSION }}- - - - name: Restore cached build without Sentry - id: cache-build-nosentry - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: samples/IntegrationTest/Build-NoSentry - key: build-nosentry-Linux-${{ inputs.unity-version }} - - - name: Build without Sentry SDK - if: steps.cache-build-nosentry.outputs.cache-hit != 'true' - run: ./test/Scripts.Integration.Test/build-project.ps1 -UnityPath "$env:UNITY_PATH" -Platform Linux -BuildDirName "Build-NoSentry" - - - name: Save cached build without Sentry - if: github.ref == 'refs/heads/main' && steps.cache-build-nosentry.outputs.cache-hit != 'true' - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: samples/IntegrationTest/Build-NoSentry - key: build-nosentry-Linux-${{ inputs.unity-version }} + # - name: Restore Unity Library cache + # uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + # with: + # path: samples/IntegrationTest/Library + # key: it-library-ubuntu-${{ env.UNITY_VERSION }}-${{ github.run_id }} + # restore-keys: | + # it-library-ubuntu-${{ env.UNITY_VERSION }}- + + # - name: Restore cached build without Sentry + # id: cache-build-nosentry + # uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + # with: + # path: samples/IntegrationTest/Build-NoSentry + # key: build-nosentry-Linux-${{ inputs.unity-version }} + + # - name: Build without Sentry SDK + # if: steps.cache-build-nosentry.outputs.cache-hit != 'true' + # run: ./test/Scripts.Integration.Test/build-project.ps1 -UnityPath "$env:UNITY_PATH" -Platform Linux -BuildDirName "Build-NoSentry" + + # - name: Save cached build without Sentry + # if: github.ref == 'refs/heads/main' && steps.cache-build-nosentry.outputs.cache-hit != 'true' + # uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + # with: + # path: samples/IntegrationTest/Build-NoSentry + # key: build-nosentry-Linux-${{ inputs.unity-version }} - name: Download UPM package uses: ./.github/actions/wait-for-artifact @@ -120,15 +120,15 @@ jobs: - name: Assert symbols and sources were uploaded (Breakpad backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log - - name: Compare build sizes - run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Linux -UnityVersion "$env:UNITY_VERSION" + # - name: Compare build sizes + # run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Linux -UnityVersion "$env:UNITY_VERSION" - - name: Upload build size measurement - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: build-size-Linux-${{ env.UNITY_VERSION }} - path: build-size-measurements/*.json - retention-days: 1 + # - name: Upload build size measurement + # uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + # with: + # name: build-size-Linux-${{ env.UNITY_VERSION }} + # path: build-size-measurements/*.json + # retention-days: 1 # We create tar explicitly because upload-artifact is slow for many files. - name: Create archive (Breakpad backend) From 5c94ddbcec0e0128f443992466a0e0f33849ab1d Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 14:25:38 +0200 Subject: [PATCH 15/16] find. that. switch. --- .github/workflows/test-build-linux.yml | 66 +++++++------- .../Il2CppBuildPreProcess.cs | 11 +-- .../UnityLinkerDiagnosticSwitch.cs | 90 +++++++++++++++++++ 3 files changed, 125 insertions(+), 42 deletions(-) create mode 100644 src/Sentry.Unity.Editor/UnityLinkerDiagnosticSwitch.cs diff --git a/.github/workflows/test-build-linux.yml b/.github/workflows/test-build-linux.yml index 0ad63b409..5535dc050 100644 --- a/.github/workflows/test-build-linux.yml +++ b/.github/workflows/test-build-linux.yml @@ -63,31 +63,31 @@ jobs: - name: Extract project archive run: tar -xvzf test-project.tar.gz - # - name: Restore Unity Library cache - # uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - # with: - # path: samples/IntegrationTest/Library - # key: it-library-ubuntu-${{ env.UNITY_VERSION }}-${{ github.run_id }} - # restore-keys: | - # it-library-ubuntu-${{ env.UNITY_VERSION }}- - - # - name: Restore cached build without Sentry - # id: cache-build-nosentry - # uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - # with: - # path: samples/IntegrationTest/Build-NoSentry - # key: build-nosentry-Linux-${{ inputs.unity-version }} - - # - name: Build without Sentry SDK - # if: steps.cache-build-nosentry.outputs.cache-hit != 'true' - # run: ./test/Scripts.Integration.Test/build-project.ps1 -UnityPath "$env:UNITY_PATH" -Platform Linux -BuildDirName "Build-NoSentry" - - # - name: Save cached build without Sentry - # if: github.ref == 'refs/heads/main' && steps.cache-build-nosentry.outputs.cache-hit != 'true' - # uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - # with: - # path: samples/IntegrationTest/Build-NoSentry - # key: build-nosentry-Linux-${{ inputs.unity-version }} + - name: Restore Unity Library cache + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: samples/IntegrationTest/Library + key: it-library-ubuntu-${{ env.UNITY_VERSION }}-${{ github.run_id }} + restore-keys: | + it-library-ubuntu-${{ env.UNITY_VERSION }}- + + - name: Restore cached build without Sentry + id: cache-build-nosentry + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: samples/IntegrationTest/Build-NoSentry + key: build-nosentry-Linux-${{ inputs.unity-version }} + + - name: Build without Sentry SDK + if: steps.cache-build-nosentry.outputs.cache-hit != 'true' + run: ./test/Scripts.Integration.Test/build-project.ps1 -UnityPath "$env:UNITY_PATH" -Platform Linux -BuildDirName "Build-NoSentry" + + - name: Save cached build without Sentry + if: github.ref == 'refs/heads/main' && steps.cache-build-nosentry.outputs.cache-hit != 'true' + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: samples/IntegrationTest/Build-NoSentry + key: build-nosentry-Linux-${{ inputs.unity-version }} - name: Download UPM package uses: ./.github/actions/wait-for-artifact @@ -120,15 +120,15 @@ jobs: - name: Assert symbols and sources were uploaded (Breakpad backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log - # - name: Compare build sizes - # run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Linux -UnityVersion "$env:UNITY_VERSION" + - name: Compare build sizes + run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Linux -UnityVersion "$env:UNITY_VERSION" - # - name: Upload build size measurement - # uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - # with: - # name: build-size-Linux-${{ env.UNITY_VERSION }} - # path: build-size-measurements/*.json - # retention-days: 1 + - name: Upload build size measurement + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: build-size-Linux-${{ env.UNITY_VERSION }} + path: build-size-measurements/*.json + retention-days: 1 # We create tar explicitly because upload-artifact is slow for many files. - name: Create archive (Breakpad backend) diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index be633e33b..081a2bbc5 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -10,7 +10,6 @@ internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport { internal const string SourceMappingArgument = "--emit-source-mapping"; internal const string LinkSymbolsArgument = "--link-symbols"; - internal const string UnityLinkerAdditionalArgumentsEnvironmentVariable = "UNITYLINKER_ADDITIONAL_ARGS"; private static IDiagnosticLogger? Logger; public int callbackOrder => 0; @@ -36,15 +35,9 @@ public void OnPreprocessBuild(BuildReport report) PlayerSettings.GetAdditionalIl2CppArgs, PlayerSettings.SetAdditionalIl2CppArgs); - // The UnityLinker picks its additional arguments up from the environment. The Bee build program bakes the - // resulting command line into its build graph, so this has to happen before that graph gets generated. SetAdditionalUnityLinkerArguments(options, - () => Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable), - arguments => Environment.SetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable, arguments)); - - Logger?.LogDebug("'{0}' is now '{1}'.", - UnityLinkerAdditionalArgumentsEnvironmentVariable, - Environment.GetEnvironmentVariable(UnityLinkerAdditionalArgumentsEnvironmentVariable)); + () => UnityLinkerDiagnosticSwitch.GetValue(Logger), + arguments => UnityLinkerDiagnosticSwitch.SetValue(arguments, Logger)); } internal static void SetAdditionalUnityLinkerArguments(SentryUnityOptions options, Func getArguments, Action setArguments) diff --git a/src/Sentry.Unity.Editor/UnityLinkerDiagnosticSwitch.cs b/src/Sentry.Unity.Editor/UnityLinkerDiagnosticSwitch.cs new file mode 100644 index 000000000..5d59bf654 --- /dev/null +++ b/src/Sentry.Unity.Editor/UnityLinkerDiagnosticSwitch.cs @@ -0,0 +1,90 @@ +using System; +using System.Reflection; +using Sentry.Extensibility; +using UnityEngine; + +namespace Sentry.Unity.Editor; + +/// +/// Provides access to Unity's 'VMUnityLinkerAdditionalArgs' diagnostic switch. Unity feeds its value into the +/// UnityLinker's additional arguments and, unlike the 'UNITYLINKER_ADDITIONAL_ARGS' environment variable, it is part +/// of the Bee build graph's inputs. That means changing it invalidates the cached graph the same way the additional +/// IL2CPP arguments do - without it the linker keeps running with whatever arguments the cached graph was built with. +/// The switch is internal to Unity, so we have to go through reflection to get to it. +/// +internal static class UnityLinkerDiagnosticSwitch +{ + internal const string SwitchName = "VMUnityLinkerAdditionalArgs"; + + public static string? GetValue(IDiagnosticLogger? logger = null) + { + var diagnosticSwitch = GetSwitch(logger); + if (diagnosticSwitch is null) + { + return null; + } + + try + { + return diagnosticSwitch.GetType().GetProperty("value")?.GetValue(diagnosticSwitch) as string; + } + catch (Exception e) + { + logger?.LogWarning("Failed to read the '{0}' diagnostic switch. Reason: {1}", SwitchName, e.Message); + return null; + } + } + + public static bool SetValue(string value, IDiagnosticLogger? logger = null) + { + var diagnosticSwitch = GetSwitch(logger); + if (diagnosticSwitch is null) + { + return false; + } + + try + { + var property = diagnosticSwitch.GetType().GetProperty("value"); + if (property is null) + { + logger?.LogWarning("Failed to resolve the value of the '{0}' diagnostic switch.", SwitchName); + return false; + } + + property.SetValue(diagnosticSwitch, value); + return true; + } + catch (Exception e) + { + logger?.LogWarning("Failed to set the '{0}' diagnostic switch. Reason: {1}", SwitchName, e.Message); + return false; + } + } + + private static object? GetSwitch(IDiagnosticLogger? logger) + { + try + { + var method = typeof(Debug).GetMethod("GetDiagnosticSwitch", BindingFlags.Static | BindingFlags.NonPublic); + if (method is null) + { + logger?.LogWarning("Failed to resolve 'Debug.GetDiagnosticSwitch'."); + return null; + } + + var diagnosticSwitch = method.Invoke(null, new object[] { SwitchName }); + if (diagnosticSwitch is null) + { + logger?.LogWarning("The diagnostic switch '{0}' does not exist.", SwitchName); + } + + return diagnosticSwitch; + } + catch (Exception e) + { + logger?.LogWarning("Failed to access the '{0}' diagnostic switch. Reason: {1}", SwitchName, e.Message); + return null; + } + } +} From e63d929a932f388582e26043cc02c6863ed09092 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 12 Aug 2026 17:14:30 +0200 Subject: [PATCH 16/16] make it go away again too --- .../Il2CppBuildPreProcess.cs | 28 +++++++++++-------- .../Il2CppBuildPreProcess.cs | 12 ++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index 081a2bbc5..3155fee3b 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -42,22 +42,26 @@ public void OnPreprocessBuild(BuildReport report) internal static void SetAdditionalUnityLinkerArguments(SentryUnityOptions options, Func getArguments, Action setArguments) { - if (!options.Il2CppLineNumberSupportEnabled) + var arguments = getArguments.Invoke(); + + if (options.Il2CppLineNumberSupportEnabled) { - return; - } + if (arguments?.Contains(LinkSymbolsArgument) == true) + { + Logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); + return; + } - var arguments = getArguments.Invoke(); - if (arguments?.Contains(LinkSymbolsArgument) == true) + Logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); + setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) + ? LinkSymbolsArgument + : $"{arguments} {LinkSymbolsArgument}"); + } + else if (arguments?.Contains(LinkSymbolsArgument) == true) { - Logger?.LogDebug("Additional UnityLinker argument '{0}' already present.", LinkSymbolsArgument); - return; + Logger?.LogDebug("IL2CPP line number support disabled - Removing additional UnityLinker argument."); + setArguments.Invoke(arguments.Replace(LinkSymbolsArgument, "").Trim()); } - - Logger?.LogDebug("IL2CPP line number support enabled - Adding additional UnityLinker argument."); - setArguments.Invoke(string.IsNullOrWhiteSpace(arguments) - ? LinkSymbolsArgument - : $"{arguments} {LinkSymbolsArgument}"); } internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 97d779bfe..20bc85b57 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -114,6 +114,18 @@ public void SetAdditionalUnityLinkerArguments_Il2CppEnabled_ExistingArgumentsDoN Assert.That(resultingArguments, Is.EqualTo($"{expectedArgument} {Il2CppBuildPreProcess.LinkSymbolsArgument}")); } + [Test] + public void SetAdditionalUnityLinkerArguments_Il2CppDisabledAndArgumentAlreadyAdded_RemovesArgument() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; + var expectedArgument = "--MyArgument"; + arguments = $"{expectedArgument} {Il2CppBuildPreProcess.LinkSymbolsArgument}"; + + Il2CppBuildPreProcess.SetAdditionalUnityLinkerArguments(options, () => arguments, s => resultingArguments = s); + + Assert.That(resultingArguments, Is.EqualTo(expectedArgument)); + } + [Test] public void SetAdditionalUnityLinkerArguments_ArgumentAlreadyAdded_AddsArgumentOnlyOnce() {