diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 62290abdc9..09f5325835 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -197,6 +197,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.TrimTest", "test\Sen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.MauiTrimTest", "test\Sentry.MauiTrimTest\Sentry.MauiTrimTest.csproj", "{DF92E098-822C-4B94-B96B-56BFB91FBB54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators", "src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj", "{E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -532,6 +534,10 @@ Global {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.Build.0 = Release|Any CPU + {E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -619,5 +625,6 @@ Global {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {6030B748-0000-43B5-B8A8-399AA42F5229} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {DF92E098-822C-4B94-B96B-56BFB91FBB54} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {E9B3DA59-E0FB-4CA6-93AC-05F0F5AD83E1} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} EndGlobalSection EndGlobal diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b7ee7380..75618163a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- New source generator allows Sentry to see true build variables like PublishAot and PublishTrimmed to properly adapt checks in the Sentry SDK ([#4101](https://github.com/getsentry/sentry-dotnet/pull/4101)) ### Features - Custom SessionReplay masks in MAUI Android apps ([#4121](https://github.com/getsentry/sentry-dotnet/pull/4121)) diff --git a/Sentry.sln b/Sentry.sln index 3ef6fd24aa..40806dc53b 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -197,6 +197,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.TrimTest", "test\Sen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.MauiTrimTest", "test\Sentry.MauiTrimTest\Sentry.MauiTrimTest.csproj", "{DF92E098-822C-4B94-B96B-56BFB91FBB54}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.SourceGenerators", "src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj", "{C3CDF61C-3E28-441C-A9CE-011C89D11719}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -532,6 +534,10 @@ Global {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF92E098-822C-4B94-B96B-56BFB91FBB54}.Release|Any CPU.Build.0 = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3CDF61C-3E28-441C-A9CE-011C89D11719}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -619,5 +625,6 @@ Global {D7DF0B26-AD43-4F8B-9BFE-C4471CCC9821} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {6030B748-0000-43B5-B8A8-399AA42F5229} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} {DF92E098-822C-4B94-B96B-56BFB91FBB54} = {6987A1CC-608E-4868-A02C-09D30C8B7B2D} + {C3CDF61C-3E28-441C-A9CE-011C89D11719} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} EndGlobalSection EndGlobal diff --git a/SentryMobile.slnf b/SentryMobile.slnf index c38fa573cc..8f14531395 100644 --- a/SentryMobile.slnf +++ b/SentryMobile.slnf @@ -12,6 +12,7 @@ "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", "src\\Sentry.Extensions.Logging\\Sentry.Extensions.Logging.csproj", "src\\Sentry.Maui\\Sentry.Maui.csproj", + "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.Extensions.Logging.Tests\\Sentry.Extensions.Logging.Tests.csproj", @@ -21,4 +22,4 @@ "test\\Sentry.Tests\\Sentry.Tests.csproj" ] } -} +} \ No newline at end of file diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index e6e27c6ee7..3960b05c00 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -32,10 +32,10 @@ "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Analyzers\\Sentry.Analyzers.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.Azure.Functions.Worker\\Sentry.Azure.Functions.Worker.csproj", "src\\Sentry.DiagnosticSource\\Sentry.DiagnosticSource.csproj", "src\\Sentry.EntityFramework\\Sentry.EntityFramework.csproj", @@ -47,6 +47,7 @@ "src\\Sentry.OpenTelemetry\\Sentry.OpenTelemetry.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry.Serilog\\Sentry.Serilog.csproj", + "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", @@ -71,4 +72,4 @@ "test\\SingleFileTestApp\\SingleFileTestApp.csproj" ] } -} +} \ No newline at end of file diff --git a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj index ace5748bae..61fcb9bc31 100644 --- a/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj +++ b/samples/Sentry.Samples.Android/Sentry.Samples.Android.csproj @@ -12,6 +12,9 @@ + @@ -40,4 +43,6 @@ full + + diff --git a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj index 0b0adbbe72..25f4b66c46 100644 --- a/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj +++ b/samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj @@ -44,5 +44,11 @@ + + + + diff --git a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj index deacfb2b37..e48c894e34 100644 --- a/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj +++ b/samples/Sentry.Samples.MacCatalyst/Sentry.Samples.MacCatalyst.csproj @@ -44,6 +44,11 @@ + + + diff --git a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj index d570b75527..b71221612f 100644 --- a/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj +++ b/samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj @@ -103,10 +103,15 @@ + + + diff --git a/src/Sentry.SourceGenerators/BuildPropertySourceGenerator.cs b/src/Sentry.SourceGenerators/BuildPropertySourceGenerator.cs new file mode 100644 index 0000000000..d7f843d66e --- /dev/null +++ b/src/Sentry.SourceGenerators/BuildPropertySourceGenerator.cs @@ -0,0 +1,92 @@ +using System; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace Sentry.SourceGenerators; + + +/// +/// Generates the necessary msbuild variables +/// +[Generator(LanguageNames.CSharp)] +public class BuildPropertySourceGenerator : ISourceGenerator +{ + /// + /// Initialize the source gen + /// + public void Initialize(GeneratorInitializationContext context) + { + } + + /// + /// Execute the source gen + /// + public void Execute(GeneratorExecutionContext context) + { + var opts = context.AnalyzerConfigOptions.GlobalOptions; + var properties = opts.Keys.Where(x => x.StartsWith("build_property.")).ToList(); + if (properties.Count == 0) + return; + + if (opts.TryGetValue("build_property.SentryDisableSourceGenerator", out var disable) && disable.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + return; + + // we only want to generate code where host setup takes place + if (!opts.TryGetValue("build_property.outputtype", out var outputType)) + return; + + if (!outputType.Equals("exe", StringComparison.InvariantCultureIgnoreCase)) + return; + + var sb = new StringBuilder(); + sb + .Append( +""" +// +// Code generated by Sentry Source Generators +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +#if NET8_0_OR_GREATER +using System; +using System.Collections.Generic; + +namespace Sentry; + +[global::System.Runtime.CompilerServices.CompilerGenerated] +public static class BuildVariableInitializer +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary { + +""" + ); + + foreach (var property in properties) + { + if (opts.TryGetValue(property, out var value)) + { + var pn = EscapeString(property.Replace("build_property.", "")); + var ev = EscapeString(value); + sb + .Append("\t\t\t{") + .Append($"\"{pn}\", \"{ev}\"") + .AppendLine("},"); + } + } + + sb + .AppendLine("\t\t});") // close dictionary + .AppendLine("\t}") + .AppendLine("}") + .AppendLine("#endif"); + + context.AddSource("__BuildProperties.g.cs", sb.ToString()); + } + + + private static string EscapeString(string value) => value.Replace("\\", "\\\\"); +} diff --git a/src/Sentry.SourceGenerators/Sentry.SourceGenerators.csproj b/src/Sentry.SourceGenerators/Sentry.SourceGenerators.csproj new file mode 100644 index 0000000000..047ca032ed --- /dev/null +++ b/src/Sentry.SourceGenerators/Sentry.SourceGenerators.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + false + enable + false + + true + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/src/Sentry/CompilerServices/BuildProperties.cs b/src/Sentry/CompilerServices/BuildProperties.cs new file mode 100644 index 0000000000..d4cd7db07e --- /dev/null +++ b/src/Sentry/CompilerServices/BuildProperties.cs @@ -0,0 +1,26 @@ +using Sentry.Internal.Extensions; + +namespace Sentry.CompilerServices; + +/// +/// This class is not meant for external usage +/// +public static class BuildProperties +{ + /// + /// The Build Variables generated from you csproj file and initialized by the Sentry Source Generated Module Initializer + /// + public static IReadOnlyDictionary? Values { get; private set; } + + /// + /// This is called by a Sentry Source-Generator module initializers to help us determine things like + /// Is your app AOT + /// Has your application been trimmed + /// What build configuration is being used + /// + /// + public static void Initialize(Dictionary properties) + { + Values ??= properties.AsReadOnly(); + } +} diff --git a/src/Sentry/Internal/AotHelper.cs b/src/Sentry/Internal/AotHelper.cs index 53165c81ca..83a500f9d5 100644 --- a/src/Sentry/Internal/AotHelper.cs +++ b/src/Sentry/Internal/AotHelper.cs @@ -1,4 +1,4 @@ -using Sentry.Protocol; +using Sentry.CompilerServices; namespace Sentry.Internal; @@ -13,10 +13,31 @@ static AotHelper() IsTrimmed = CheckIsTrimmed(); } + [UnconditionalSuppressMessage("Trimming", "IL2026: RequiresUnreferencedCode", Justification = AvoidAtRuntime)] private static bool CheckIsTrimmed() { + if (Check("publishtrimmed")) + return true; + + if (Check("publishaot")) + return true; + + // fallback check var stackTrace = new StackTrace(false); return stackTrace.GetFrame(0)?.GetMethod() is null; } + + private static bool Check(string key) + { + if (BuildProperties.Values?.TryGetValue(key, out var aotValue) ?? false) + { + if (bool.TryParse(aotValue, out var result)) + { + return result; + } + } + + return false; + } } diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 6f37cfec99..5a4eaccb44 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -107,10 +107,6 @@ This file contains targets that are invoked during the end-user's build. The same file is included twice, so it ends up being used for both direct and transitive package references to Sentry. --> - - - - @@ -196,4 +192,17 @@ + + + + + + + + + + diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 401a0fa6f0..bd49f4e5f4 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -1,6 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using Sentry.Extensibility; using Sentry.Infrastructure; using Sentry.Internal; +using Sentry.Internal.Extensions; using Sentry.Protocol.Envelopes; namespace Sentry; diff --git a/src/Sentry/buildTransitive/Sentry.SourceGenerators.targets b/src/Sentry/buildTransitive/Sentry.SourceGenerators.targets new file mode 100644 index 0000000000..292d49e34b --- /dev/null +++ b/src/Sentry/buildTransitive/Sentry.SourceGenerators.targets @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Sentry/buildTransitive/Sentry.targets b/src/Sentry/buildTransitive/Sentry.targets index 01ac89fb2a..01c3e0a4fe 100644 --- a/src/Sentry/buildTransitive/Sentry.targets +++ b/src/Sentry/buildTransitive/Sentry.targets @@ -5,6 +5,14 @@ + + + + + + + + Sentry.Attributes$(MSBuildProjectExtension.Replace('proj', '')) diff --git a/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_BadStrings.verified.txt b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_BadStrings.verified.txt new file mode 100644 index 0000000000..23d83df1b0 --- /dev/null +++ b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_BadStrings.verified.txt @@ -0,0 +1,412 @@ +{ + Generator: {}, + GeneratedSources: [ + { + SyntaxTree: { + FilePath: Sentry.SourceGenerators/Sentry.SourceGenerators.BuildPropertySourceGenerator/__BuildProperties.g.cs, + Encoding: utf-8, + Length: 688, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + SourceText: { + Encoding: utf-8, + Source: +// +// Code generated by Sentry Source Generators +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +#if NET8_0_OR_GREATER +using System; +using System.Collections.Generic; + +namespace Sentry; + +[global::System.Runtime.CompilerServices.CompilerGenerated] +public static class BuildVariableInitializer +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary { + {"My\\Key", "test\\test"}, + {"OutputType", "exe"}, + }); + } +} +#endif +, + Length: 688, + ChecksumAlgorithm: Sha1, + CanBeEmbedded: true, + Container: {}, + Lines: [ + { + End: 19, + EndIncludingLineBreak: 20, + Span: { + Length: 19 + }, + SpanIncludingLineBreak: { + Length: 20 + } + }, + { + LineNumber: 1, + Start: 20, + End: 65, + EndIncludingLineBreak: 66, + Span: { + Start: 20, + Length: 45 + }, + SpanIncludingLineBreak: { + Start: 20, + Length: 46 + } + }, + { + LineNumber: 2, + Start: 66, + End: 137, + EndIncludingLineBreak: 138, + Span: { + Start: 66, + Length: 71 + }, + SpanIncludingLineBreak: { + Start: 66, + Length: 72 + } + }, + { + LineNumber: 3, + Start: 138, + End: 153, + EndIncludingLineBreak: 154, + Span: { + Start: 138, + Length: 15 + }, + SpanIncludingLineBreak: { + Start: 138, + Length: 16 + } + }, + { + LineNumber: 4, + Start: 154, + End: 174, + EndIncludingLineBreak: 175, + Span: { + Start: 154, + Length: 20 + }, + SpanIncludingLineBreak: { + Start: 154, + Length: 21 + } + }, + { + LineNumber: 5, + Start: 175, + End: 175, + EndIncludingLineBreak: 176, + Span: { + Start: 175 + }, + SpanIncludingLineBreak: { + Start: 175, + Length: 1 + } + }, + { + LineNumber: 6, + Start: 176, + End: 197, + EndIncludingLineBreak: 198, + Span: { + Start: 176, + Length: 21 + }, + SpanIncludingLineBreak: { + Start: 176, + Length: 22 + } + }, + { + LineNumber: 7, + Start: 198, + End: 211, + EndIncludingLineBreak: 212, + Span: { + Start: 198, + Length: 13 + }, + SpanIncludingLineBreak: { + Start: 198, + Length: 14 + } + }, + { + LineNumber: 8, + Start: 212, + End: 245, + EndIncludingLineBreak: 246, + Span: { + Start: 212, + Length: 33 + }, + SpanIncludingLineBreak: { + Start: 212, + Length: 34 + } + }, + { + LineNumber: 9, + Start: 246, + End: 246, + EndIncludingLineBreak: 247, + Span: { + Start: 246 + }, + SpanIncludingLineBreak: { + Start: 246, + Length: 1 + } + }, + { + LineNumber: 10, + Start: 247, + End: 264, + EndIncludingLineBreak: 265, + Span: { + Start: 247, + Length: 17 + }, + SpanIncludingLineBreak: { + Start: 247, + Length: 18 + } + }, + { + LineNumber: 11, + Start: 265, + End: 265, + EndIncludingLineBreak: 266, + Span: { + Start: 265 + }, + SpanIncludingLineBreak: { + Start: 265, + Length: 1 + } + }, + { + LineNumber: 12, + Start: 266, + End: 325, + EndIncludingLineBreak: 326, + Span: { + Start: 266, + Length: 59 + }, + SpanIncludingLineBreak: { + Start: 266, + Length: 60 + } + }, + { + LineNumber: 13, + Start: 326, + End: 370, + EndIncludingLineBreak: 371, + Span: { + Start: 326, + Length: 44 + }, + SpanIncludingLineBreak: { + Start: 326, + Length: 45 + } + }, + { + LineNumber: 14, + Start: 371, + End: 372, + EndIncludingLineBreak: 373, + Span: { + Start: 371, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 371, + Length: 2 + } + }, + { + LineNumber: 15, + Start: 373, + End: 436, + EndIncludingLineBreak: 437, + Span: { + Start: 373, + Length: 63 + }, + SpanIncludingLineBreak: { + Start: 373, + Length: 64 + } + }, + { + LineNumber: 16, + Start: 437, + End: 472, + EndIncludingLineBreak: 473, + Span: { + Start: 437, + Length: 35 + }, + SpanIncludingLineBreak: { + Start: 437, + Length: 36 + } + }, + { + LineNumber: 17, + Start: 473, + End: 478, + EndIncludingLineBreak: 479, + Span: { + Start: 473, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 473, + Length: 6 + } + }, + { + LineNumber: 18, + Start: 479, + End: 613, + EndIncludingLineBreak: 614, + Span: { + Start: 479, + Length: 134 + }, + SpanIncludingLineBreak: { + Start: 479, + Length: 135 + } + }, + { + LineNumber: 19, + Start: 614, + End: 643, + EndIncludingLineBreak: 644, + Span: { + Start: 614, + Length: 29 + }, + SpanIncludingLineBreak: { + Start: 614, + Length: 30 + } + }, + { + LineNumber: 20, + Start: 644, + End: 669, + EndIncludingLineBreak: 670, + Span: { + Start: 644, + Length: 25 + }, + SpanIncludingLineBreak: { + Start: 644, + Length: 26 + } + }, + { + LineNumber: 21, + Start: 670, + End: 675, + EndIncludingLineBreak: 676, + Span: { + Start: 670, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 670, + Length: 6 + } + }, + { + LineNumber: 22, + Start: 676, + End: 678, + EndIncludingLineBreak: 679, + Span: { + Start: 676, + Length: 2 + }, + SpanIncludingLineBreak: { + Start: 676, + Length: 3 + } + }, + { + LineNumber: 23, + Start: 679, + End: 680, + EndIncludingLineBreak: 681, + Span: { + Start: 679, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 679, + Length: 2 + } + }, + { + LineNumber: 24, + Start: 681, + End: 687, + EndIncludingLineBreak: 688, + Span: { + Start: 681, + Length: 6 + }, + SpanIncludingLineBreak: { + Start: 681, + Length: 7 + } + }, + { + LineNumber: 25, + Start: 688, + End: 688, + EndIncludingLineBreak: 688, + Span: { + Start: 688 + }, + SpanIncludingLineBreak: { + Start: 688 + } + } + ] + }, + HintName: __BuildProperties.g.cs + } + ], + Diagnostics: null +} \ No newline at end of file diff --git a/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Expect_None.verified.txt b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Expect_None.verified.txt new file mode 100644 index 0000000000..f2b746dde0 --- /dev/null +++ b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Expect_None.verified.txt @@ -0,0 +1,5 @@ +{ + Generator: {}, + GeneratedSources: null, + Diagnostics: null +} \ No newline at end of file diff --git a/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Publish_AotTrue.verified.txt b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Publish_AotTrue.verified.txt new file mode 100644 index 0000000000..0114136523 --- /dev/null +++ b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Publish_AotTrue.verified.txt @@ -0,0 +1,412 @@ +{ + Generator: {}, + GeneratedSources: [ + { + SyntaxTree: { + FilePath: Sentry.SourceGenerators/Sentry.SourceGenerators.BuildPropertySourceGenerator/__BuildProperties.g.cs, + Encoding: utf-8, + Length: 685, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + SourceText: { + Encoding: utf-8, + Source: +// +// Code generated by Sentry Source Generators +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +#if NET8_0_OR_GREATER +using System; +using System.Collections.Generic; + +namespace Sentry; + +[global::System.Runtime.CompilerServices.CompilerGenerated] +public static class BuildVariableInitializer +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary { + {"PublishAot", "true"}, + {"OutputType", "exe"}, + }); + } +} +#endif +, + Length: 685, + ChecksumAlgorithm: Sha1, + CanBeEmbedded: true, + Container: {}, + Lines: [ + { + End: 19, + EndIncludingLineBreak: 20, + Span: { + Length: 19 + }, + SpanIncludingLineBreak: { + Length: 20 + } + }, + { + LineNumber: 1, + Start: 20, + End: 65, + EndIncludingLineBreak: 66, + Span: { + Start: 20, + Length: 45 + }, + SpanIncludingLineBreak: { + Start: 20, + Length: 46 + } + }, + { + LineNumber: 2, + Start: 66, + End: 137, + EndIncludingLineBreak: 138, + Span: { + Start: 66, + Length: 71 + }, + SpanIncludingLineBreak: { + Start: 66, + Length: 72 + } + }, + { + LineNumber: 3, + Start: 138, + End: 153, + EndIncludingLineBreak: 154, + Span: { + Start: 138, + Length: 15 + }, + SpanIncludingLineBreak: { + Start: 138, + Length: 16 + } + }, + { + LineNumber: 4, + Start: 154, + End: 174, + EndIncludingLineBreak: 175, + Span: { + Start: 154, + Length: 20 + }, + SpanIncludingLineBreak: { + Start: 154, + Length: 21 + } + }, + { + LineNumber: 5, + Start: 175, + End: 175, + EndIncludingLineBreak: 176, + Span: { + Start: 175 + }, + SpanIncludingLineBreak: { + Start: 175, + Length: 1 + } + }, + { + LineNumber: 6, + Start: 176, + End: 197, + EndIncludingLineBreak: 198, + Span: { + Start: 176, + Length: 21 + }, + SpanIncludingLineBreak: { + Start: 176, + Length: 22 + } + }, + { + LineNumber: 7, + Start: 198, + End: 211, + EndIncludingLineBreak: 212, + Span: { + Start: 198, + Length: 13 + }, + SpanIncludingLineBreak: { + Start: 198, + Length: 14 + } + }, + { + LineNumber: 8, + Start: 212, + End: 245, + EndIncludingLineBreak: 246, + Span: { + Start: 212, + Length: 33 + }, + SpanIncludingLineBreak: { + Start: 212, + Length: 34 + } + }, + { + LineNumber: 9, + Start: 246, + End: 246, + EndIncludingLineBreak: 247, + Span: { + Start: 246 + }, + SpanIncludingLineBreak: { + Start: 246, + Length: 1 + } + }, + { + LineNumber: 10, + Start: 247, + End: 264, + EndIncludingLineBreak: 265, + Span: { + Start: 247, + Length: 17 + }, + SpanIncludingLineBreak: { + Start: 247, + Length: 18 + } + }, + { + LineNumber: 11, + Start: 265, + End: 265, + EndIncludingLineBreak: 266, + Span: { + Start: 265 + }, + SpanIncludingLineBreak: { + Start: 265, + Length: 1 + } + }, + { + LineNumber: 12, + Start: 266, + End: 325, + EndIncludingLineBreak: 326, + Span: { + Start: 266, + Length: 59 + }, + SpanIncludingLineBreak: { + Start: 266, + Length: 60 + } + }, + { + LineNumber: 13, + Start: 326, + End: 370, + EndIncludingLineBreak: 371, + Span: { + Start: 326, + Length: 44 + }, + SpanIncludingLineBreak: { + Start: 326, + Length: 45 + } + }, + { + LineNumber: 14, + Start: 371, + End: 372, + EndIncludingLineBreak: 373, + Span: { + Start: 371, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 371, + Length: 2 + } + }, + { + LineNumber: 15, + Start: 373, + End: 436, + EndIncludingLineBreak: 437, + Span: { + Start: 373, + Length: 63 + }, + SpanIncludingLineBreak: { + Start: 373, + Length: 64 + } + }, + { + LineNumber: 16, + Start: 437, + End: 472, + EndIncludingLineBreak: 473, + Span: { + Start: 437, + Length: 35 + }, + SpanIncludingLineBreak: { + Start: 437, + Length: 36 + } + }, + { + LineNumber: 17, + Start: 473, + End: 478, + EndIncludingLineBreak: 479, + Span: { + Start: 473, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 473, + Length: 6 + } + }, + { + LineNumber: 18, + Start: 479, + End: 613, + EndIncludingLineBreak: 614, + Span: { + Start: 479, + Length: 134 + }, + SpanIncludingLineBreak: { + Start: 479, + Length: 135 + } + }, + { + LineNumber: 19, + Start: 614, + End: 640, + EndIncludingLineBreak: 641, + Span: { + Start: 614, + Length: 26 + }, + SpanIncludingLineBreak: { + Start: 614, + Length: 27 + } + }, + { + LineNumber: 20, + Start: 641, + End: 666, + EndIncludingLineBreak: 667, + Span: { + Start: 641, + Length: 25 + }, + SpanIncludingLineBreak: { + Start: 641, + Length: 26 + } + }, + { + LineNumber: 21, + Start: 667, + End: 672, + EndIncludingLineBreak: 673, + Span: { + Start: 667, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 667, + Length: 6 + } + }, + { + LineNumber: 22, + Start: 673, + End: 675, + EndIncludingLineBreak: 676, + Span: { + Start: 673, + Length: 2 + }, + SpanIncludingLineBreak: { + Start: 673, + Length: 3 + } + }, + { + LineNumber: 23, + Start: 676, + End: 677, + EndIncludingLineBreak: 678, + Span: { + Start: 676, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 676, + Length: 2 + } + }, + { + LineNumber: 24, + Start: 678, + End: 684, + EndIncludingLineBreak: 685, + Span: { + Start: 678, + Length: 6 + }, + SpanIncludingLineBreak: { + Start: 678, + Length: 7 + } + }, + { + LineNumber: 25, + Start: 685, + End: 685, + EndIncludingLineBreak: 685, + Span: { + Start: 685 + }, + SpanIncludingLineBreak: { + Start: 685 + } + } + ] + }, + HintName: __BuildProperties.g.cs + } + ], + Diagnostics: null +} \ No newline at end of file diff --git a/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Success.verified.txt b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Success.verified.txt new file mode 100644 index 0000000000..db6ee0f7a7 --- /dev/null +++ b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.RunResult_Success.verified.txt @@ -0,0 +1,412 @@ +{ + Generator: {}, + GeneratedSources: [ + { + SyntaxTree: { + FilePath: Sentry.SourceGenerators/Sentry.SourceGenerators.BuildPropertySourceGenerator/__BuildProperties.g.cs, + Encoding: utf-8, + Length: 686, + HasCompilationUnitRoot: true, + Options: { + LanguageVersion: CSharp12, + Language: C#, + DocumentationMode: Parse, + Errors: null + } + }, + SourceText: { + Encoding: utf-8, + Source: +// +// Code generated by Sentry Source Generators +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +#if NET8_0_OR_GREATER +using System; +using System.Collections.Generic; + +namespace Sentry; + +[global::System.Runtime.CompilerServices.CompilerGenerated] +public static class BuildVariableInitializer +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary { + {"PublishAot", "false"}, + {"OutputType", "exe"}, + }); + } +} +#endif +, + Length: 686, + ChecksumAlgorithm: Sha1, + CanBeEmbedded: true, + Container: {}, + Lines: [ + { + End: 19, + EndIncludingLineBreak: 20, + Span: { + Length: 19 + }, + SpanIncludingLineBreak: { + Length: 20 + } + }, + { + LineNumber: 1, + Start: 20, + End: 65, + EndIncludingLineBreak: 66, + Span: { + Start: 20, + Length: 45 + }, + SpanIncludingLineBreak: { + Start: 20, + Length: 46 + } + }, + { + LineNumber: 2, + Start: 66, + End: 137, + EndIncludingLineBreak: 138, + Span: { + Start: 66, + Length: 71 + }, + SpanIncludingLineBreak: { + Start: 66, + Length: 72 + } + }, + { + LineNumber: 3, + Start: 138, + End: 153, + EndIncludingLineBreak: 154, + Span: { + Start: 138, + Length: 15 + }, + SpanIncludingLineBreak: { + Start: 138, + Length: 16 + } + }, + { + LineNumber: 4, + Start: 154, + End: 174, + EndIncludingLineBreak: 175, + Span: { + Start: 154, + Length: 20 + }, + SpanIncludingLineBreak: { + Start: 154, + Length: 21 + } + }, + { + LineNumber: 5, + Start: 175, + End: 175, + EndIncludingLineBreak: 176, + Span: { + Start: 175 + }, + SpanIncludingLineBreak: { + Start: 175, + Length: 1 + } + }, + { + LineNumber: 6, + Start: 176, + End: 197, + EndIncludingLineBreak: 198, + Span: { + Start: 176, + Length: 21 + }, + SpanIncludingLineBreak: { + Start: 176, + Length: 22 + } + }, + { + LineNumber: 7, + Start: 198, + End: 211, + EndIncludingLineBreak: 212, + Span: { + Start: 198, + Length: 13 + }, + SpanIncludingLineBreak: { + Start: 198, + Length: 14 + } + }, + { + LineNumber: 8, + Start: 212, + End: 245, + EndIncludingLineBreak: 246, + Span: { + Start: 212, + Length: 33 + }, + SpanIncludingLineBreak: { + Start: 212, + Length: 34 + } + }, + { + LineNumber: 9, + Start: 246, + End: 246, + EndIncludingLineBreak: 247, + Span: { + Start: 246 + }, + SpanIncludingLineBreak: { + Start: 246, + Length: 1 + } + }, + { + LineNumber: 10, + Start: 247, + End: 264, + EndIncludingLineBreak: 265, + Span: { + Start: 247, + Length: 17 + }, + SpanIncludingLineBreak: { + Start: 247, + Length: 18 + } + }, + { + LineNumber: 11, + Start: 265, + End: 265, + EndIncludingLineBreak: 266, + Span: { + Start: 265 + }, + SpanIncludingLineBreak: { + Start: 265, + Length: 1 + } + }, + { + LineNumber: 12, + Start: 266, + End: 325, + EndIncludingLineBreak: 326, + Span: { + Start: 266, + Length: 59 + }, + SpanIncludingLineBreak: { + Start: 266, + Length: 60 + } + }, + { + LineNumber: 13, + Start: 326, + End: 370, + EndIncludingLineBreak: 371, + Span: { + Start: 326, + Length: 44 + }, + SpanIncludingLineBreak: { + Start: 326, + Length: 45 + } + }, + { + LineNumber: 14, + Start: 371, + End: 372, + EndIncludingLineBreak: 373, + Span: { + Start: 371, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 371, + Length: 2 + } + }, + { + LineNumber: 15, + Start: 373, + End: 436, + EndIncludingLineBreak: 437, + Span: { + Start: 373, + Length: 63 + }, + SpanIncludingLineBreak: { + Start: 373, + Length: 64 + } + }, + { + LineNumber: 16, + Start: 437, + End: 472, + EndIncludingLineBreak: 473, + Span: { + Start: 437, + Length: 35 + }, + SpanIncludingLineBreak: { + Start: 437, + Length: 36 + } + }, + { + LineNumber: 17, + Start: 473, + End: 478, + EndIncludingLineBreak: 479, + Span: { + Start: 473, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 473, + Length: 6 + } + }, + { + LineNumber: 18, + Start: 479, + End: 613, + EndIncludingLineBreak: 614, + Span: { + Start: 479, + Length: 134 + }, + SpanIncludingLineBreak: { + Start: 479, + Length: 135 + } + }, + { + LineNumber: 19, + Start: 614, + End: 641, + EndIncludingLineBreak: 642, + Span: { + Start: 614, + Length: 27 + }, + SpanIncludingLineBreak: { + Start: 614, + Length: 28 + } + }, + { + LineNumber: 20, + Start: 642, + End: 667, + EndIncludingLineBreak: 668, + Span: { + Start: 642, + Length: 25 + }, + SpanIncludingLineBreak: { + Start: 642, + Length: 26 + } + }, + { + LineNumber: 21, + Start: 668, + End: 673, + EndIncludingLineBreak: 674, + Span: { + Start: 668, + Length: 5 + }, + SpanIncludingLineBreak: { + Start: 668, + Length: 6 + } + }, + { + LineNumber: 22, + Start: 674, + End: 676, + EndIncludingLineBreak: 677, + Span: { + Start: 674, + Length: 2 + }, + SpanIncludingLineBreak: { + Start: 674, + Length: 3 + } + }, + { + LineNumber: 23, + Start: 677, + End: 678, + EndIncludingLineBreak: 679, + Span: { + Start: 677, + Length: 1 + }, + SpanIncludingLineBreak: { + Start: 677, + Length: 2 + } + }, + { + LineNumber: 24, + Start: 679, + End: 685, + EndIncludingLineBreak: 686, + Span: { + Start: 679, + Length: 6 + }, + SpanIncludingLineBreak: { + Start: 679, + Length: 7 + } + }, + { + LineNumber: 25, + Start: 686, + End: 686, + EndIncludingLineBreak: 686, + Span: { + Start: 686 + }, + SpanIncludingLineBreak: { + Start: 686 + } + } + ] + }, + HintName: __BuildProperties.g.cs + } + ], + Diagnostics: null +} \ No newline at end of file diff --git a/test/Sentry.Tests/BuildPropertySourceGeneratorTests.cs b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.cs new file mode 100644 index 0000000000..218524c0d9 --- /dev/null +++ b/test/Sentry.Tests/BuildPropertySourceGeneratorTests.cs @@ -0,0 +1,109 @@ +#if !__MOBILE__ && (NET8_0 || NET9_0) +#nullable enable +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.VisualStudio.TestPlatform.TestHost; +using Sentry.SourceGenerators; + +namespace Sentry.Tests; + + +public class BuildPropertySourceGeneratorTests +{ + [Fact] + public Task RunResult_Success() + { + var driver = BuildDriver(typeof(Program).Assembly, ("PublishAot", "false"), ("OutputType", "exe")); + var result = driver.GetRunResult().Results.FirstOrDefault(); + result.Exception.Should().BeNull(); + result.GeneratedSources.Length.Should().Be(1); + result.GeneratedSources.First().HintName.Should().Be("__BuildProperties.g.cs"); + return Verify(result); + } + + + [Fact] + public Task RunResult_BadStrings() + { + // we're hijacking PublishAot to make life easy + var driver = BuildDriver(typeof(Program).Assembly, ("My\\Key", "test\\test"), ("OutputType", "exe")); + var result = driver.GetRunResult().Results.FirstOrDefault(); + result.Exception.Should().BeNull(); + result.GeneratedSources.Length.Should().Be(1); + result.GeneratedSources.First().HintName.Should().Be("__BuildProperties.g.cs"); + return Verify(result); + } + + + [Fact] + public Task RunResult_Publish_AotTrue() + { + var driver = BuildDriver(typeof(Program).Assembly, ("PublishAot", "true"), ("OutputType", "exe")); + var result = driver.GetRunResult().Results.FirstOrDefault(); + result.Exception.Should().BeNull(); + result.GeneratedSources.Length.Should().Be(1); + result.GeneratedSources.First().HintName.Should().Be("__BuildProperties.g.cs"); + return Verify(result); + } + + + [Theory] + [InlineData("no", true)] + [InlineData("true", false)] + [InlineData("false", true)] + public void RunResult_SentryDisableSourceGenerator_Values(string value, bool sourceGenExpected) + { + var driver = BuildDriver(typeof(Program).Assembly, ("SentryDisableSourceGenerator", value), ("OutputType", "exe")); + var result = driver.GetRunResult().Results.FirstOrDefault(); + result.Exception.Should().BeNull(); + + var generated = result.GeneratedSources.Any(x => x.HintName.Equals("__BuildProperties.g.cs")); + generated.Should().Be(sourceGenExpected); + } + + + [Fact] + public Task RunResult_Expect_None() + { + var driver = BuildDriver(typeof(Program).Assembly, ("PublishAot", "false")); + var result = driver.GetRunResult().Results.FirstOrDefault(); + result.Exception.Should().BeNull(); + result.GeneratedSources.Length.Should().Be(0); + + return Verify(result); + } + + + private static GeneratorDriver BuildDriver(Assembly metadataAssembly, params IEnumerable<(string Key, string Value)> buildProperties) + { + var metadataReference = MetadataReference.CreateFromFile(metadataAssembly.Location); + var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + var compilation = CSharpCompilation.Create("TestAssembly", [], [metadataReference], options); + var generator = new BuildPropertySourceGenerator(); + + var dict = buildProperties.ToDictionary(x => "build_property." + x.Key, x => x.Value, comparer: StringComparer.InvariantCultureIgnoreCase); + var provider = new MockAnalyzerConfigOptionsProvider(dict); + + var driver = CSharpGeneratorDriver.Create([generator], optionsProvider: provider); + return driver.RunGenerators(compilation); + } +} + +file class MockAnalyzerConfigOptionsProvider(Dictionary buildProperties) : AnalyzerConfigOptionsProvider +{ + private readonly MockAnalyzerConfigOptions _options = new(buildProperties); + + public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => _options; + public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => _options; + public override AnalyzerConfigOptions GlobalOptions => _options; +} + +file class MockAnalyzerConfigOptions(Dictionary values) : AnalyzerConfigOptions +{ + public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) + => values.TryGetValue(key, out value); + + public override IEnumerable Keys => values.Keys; +} +#endif diff --git a/test/Sentry.Tests/Sentry.Tests.csproj b/test/Sentry.Tests/Sentry.Tests.csproj index d2c5db0dfc..5642a0910e 100644 --- a/test/Sentry.Tests/Sentry.Tests.csproj +++ b/test/Sentry.Tests/Sentry.Tests.csproj @@ -43,4 +43,10 @@ + + + + + +