Skip to content

Build Variables Source Generator #4101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .generated.NoMobile.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
7 changes: 7 additions & 0 deletions Sentry.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
3 changes: 2 additions & 1 deletion SentryMobile.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -21,4 +22,4 @@
"test\\Sentry.Tests\\Sentry.Tests.csproj"
]
}
}
}
5 changes: 3 additions & 2 deletions SentryNoMobile.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -71,4 +72,4 @@
"test\\SingleFileTestApp\\SingleFileTestApp.csproj"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\..\src\Sentry\Sentry.csproj" />
<Using Include="Android.App.Activity" Alias="Activity" />

Expand Down Expand Up @@ -40,4 +43,6 @@
<AndroidLinkMode>full</AndroidLinkMode>
</PropertyGroup>

<!--In order to test our analyzers with this project, we manually reference our targets file. User do not need to do this-->
<Import Project="..\..\src\Sentry\buildTransitive\Sentry.SourceGenerators.targets" />
</Project>
6 changes: 6 additions & 0 deletions samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,11 @@
<ItemGroup>
<!-- <PackageReference Include="Sentry" Version="..." /> -->
<ProjectReference Include="..\..\src\Sentry\Sentry.csproj" />
<ProjectReference Include="..\..\src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup>

<!--In order to test our analyzers with this project, we manually reference our targets file. User do not need to do this-->
<Import Project="..\..\src\Sentry\buildTransitive\Sentry.SourceGenerators.targets" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
<ItemGroup>
<!-- <PackageReference Include="Sentry" Version="..." /> -->
<ProjectReference Include="..\..\src\Sentry\Sentry.csproj" />
<ProjectReference Include="..\..\src\Sentry.SourceGenerators\Sentry.SourceGenerators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup>

<!--In order to test our analyzers with this project, we manually reference our targets file. User do not need to do this-->
<Import Project="..\..\src\Sentry\buildTransitive\Sentry.SourceGenerators.targets" />
</Project>
5 changes: 5 additions & 0 deletions samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,15 @@

<ItemGroup>
<ProjectReference Include="..\..\src\Sentry.Maui\Sentry.Maui.csproj" />
<ProjectReference Include="..\..\src\Sentry.Analyzers\Sentry.Analyzers.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
</ItemGroup>

<!--In order to test our analyzers with this project, we manually reference our targets file. User do not need to do this-->
<Import Project="..\..\src\Sentry\buildTransitive\Sentry.SourceGenerators.targets" />
</Project>
92 changes: 92 additions & 0 deletions src/Sentry.SourceGenerators/BuildPropertySourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace Sentry.SourceGenerators;


/// <summary>
/// Generates the necessary msbuild variables
/// </summary>
[Generator(LanguageNames.CSharp)]
public class BuildPropertySourceGenerator : ISourceGenerator
{
/// <summary>
/// Initialize the source gen
/// </summary>
public void Initialize(GeneratorInitializationContext context)
{
}

/// <summary>
/// Execute the source gen
/// </summary>
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(
"""
// <auto-generated>
// Code generated by Sentry Source Generators
// Changes may cause incorrect behavior and will be lost if the code is
// regenerated.
// </auto-generated>

#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<string, string> {

"""
);

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("\\", "\\\\");
}
25 changes: 25 additions & 0 deletions src/Sentry.SourceGenerators/Sentry.SourceGenerators.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<ImplicitUsings>false</ImplicitUsings>

<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" PrivateAssets="all"/>
</ItemGroup>

<ItemGroup>
<Using Remove="System.Text.Json" />
<Using Remove="System.Text.Json.Serialization" />
</ItemGroup>
</Project>
26 changes: 26 additions & 0 deletions src/Sentry/CompilerServices/BuildProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Sentry.Internal.Extensions;

namespace Sentry.CompilerServices;

/// <summary>
/// This class is not meant for external usage
/// </summary>
public static class BuildProperties
{
/// <summary>
/// The Build Variables generated from you csproj file and initialized by the Sentry Source Generated Module Initializer
/// </summary>
public static IReadOnlyDictionary<string, string>? Values { get; private set; }

/// <summary>
/// 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
/// </summary>
/// <param name="properties"></param>
public static void Initialize(Dictionary<string, string> properties)
{
Values ??= properties.AsReadOnly();
}
}
23 changes: 22 additions & 1 deletion src/Sentry/Internal/AotHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Sentry.Protocol;
using Sentry.CompilerServices;

namespace Sentry.Internal;

Expand All @@ -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;
}
}
17 changes: 13 additions & 4 deletions src/Sentry/Sentry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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.
-->
<ItemGroup>
<None Include="buildTransitive\Sentry.targets" Pack="true" PackagePath="buildTransitive\Sentry.targets" />
<None Include="buildTransitive\Sentry.targets" Pack="true" PackagePath="build\Sentry.targets" />
</ItemGroup>

<ItemGroup>
<Using Include="Sentry.Protocol.Envelopes.ISerializable" Alias="ISerializable" />
Expand Down Expand Up @@ -196,4 +192,17 @@
<InternalsVisibleTo Include="Sentry.Analyzers.Tests" PublicKey="$(SentryPublicKey)" />
</ItemGroup>

<ItemGroup>
<None Include="buildTransitive\Sentry.targets" Pack="true" PackagePath="buildTransitive\Sentry.targets" />
<None Include="buildTransitive\Sentry.targets" Pack="true" PackagePath="build\Sentry.targets" />
<None Include="buildTransitive\Sentry.SourceGenerators.targets" Pack="true" PackagePath="buildTransitive\Sentry.SourceGenerators.targets" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Do both Sentry.SourceGenerators.targets work?

I thought that only build/{PACKAGE-ID}.props and build/{PACKAGE-ID}.targets are picked up automatically from NuGet packages.
I believe that - since we have all CompilerVisibleProperty in Sentry.targets too, we can remove Sentry.SourceGenerators.targets.

Or am I wrong here? I'm a bit uncertain.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has long been an unknown. Sentry included its targets in both. I've notoriously included my targets in both in the past due to past msbuild issues.

The targets for the dll of the source generator have to be there or compilervisibleproperty is not seen. It doesn't source gen in the test projects with the current targets, I had to add this. Considering this file doesn't hurt anything and seems to fix any corner cases I've ever had, I think we should just move forward with this.

<None Include="buildTransitive\Sentry.SourceGenerators.targets" Pack="true" PackagePath="build\Sentry.SourceGenerators.targets" />
</ItemGroup>

<ItemGroup>
<None Include="..\Sentry.SourceGenerators\bin\Release\netstandard2.0\Sentry.SourceGenerators.dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false" />
</ItemGroup>
</Project>
5 changes: 5 additions & 0 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
9 changes: 9 additions & 0 deletions src/Sentry/buildTransitive/Sentry.SourceGenerators.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project>
<ItemGroup>
<CompilerVisibleProperty Include="PublishAot" />
<CompilerVisibleProperty Include="UseDotNetNativeToolchain" />
<CompilerVisibleProperty Include="PublishTrimmed" />
<CompilerVisibleProperty Include="SentryDisableSourceGenerator" />
<CompilerVisibleProperty Include="Configuration" />
</ItemGroup>
</Project>
Loading