Skip to content

fix: BuildProperty-Generator and AOT compilation detection #4333

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

Merged
merged 22 commits into from
Jul 13, 2025
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Sentry now decompresses Request bodies in ASP.NET Core when RequestDecompression middleware is enabled ([#4315](https://github.com/getsentry/sentry-dotnet/pull/4315))
- Custom ISentryEventProcessors are now run for native iOS events ([#4318](https://github.com/getsentry/sentry-dotnet/pull/4318))
- Crontab validation when capturing checkins ([#4314](https://github.com/getsentry/sentry-dotnet/pull/4314))
- Fixed an issue with the way Sentry detects build settings. This was causing Sentry to produce code that would could fail at runtime in AOT compiled applications. ([#4333](https://github.com/getsentry/sentry-dotnet/pull/4333))
- Native AOT: link to static `lzma` on Linux/MUSL ([#4326](https://github.com/getsentry/sentry-dotnet/pull/4326))
- AppDomain.CurrentDomain.ProcessExit hook is now removed on shutdown ([#4323](https://github.com/getsentry/sentry-dotnet/pull/4323))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,4 @@
<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>
2 changes: 0 additions & 2 deletions samples/Sentry.Samples.Ios/Sentry.Samples.Ios.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,4 @@
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 @@ -49,6 +49,4 @@
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>
2 changes: 0 additions & 2 deletions samples/Sentry.Samples.Maui/Sentry.Samples.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,4 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
</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>
47 changes: 23 additions & 24 deletions src/Sentry.SourceGenerators/BuildPropertySourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

namespace Sentry.SourceGenerators;


/// <summary>
/// Generates the necessary msbuild variables
/// </summary>
[Generator(LanguageNames.CSharp)]
public class BuildPropertySourceGenerator : ISourceGenerator
public sealed class BuildPropertySourceGenerator : ISourceGenerator
{
/// <summary>
/// Initialize the source gen
Expand All @@ -23,6 +22,8 @@ public void Initialize(GeneratorInitializationContext context)
/// </summary>
public void Execute(GeneratorExecutionContext context)
{
const string tabString = " ";

var opts = context.AnalyzerConfigOptions.GlobalOptions;
var properties = opts.Keys.Where(x => x.StartsWith("build_property.")).ToList();
if (properties.Count == 0)
Expand All @@ -37,36 +38,34 @@ public void Execute(GeneratorExecutionContext context)
}

// 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))
if (!context.Compilation.Options.OutputKind.IsExe())
{
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.
// This code was generated by Sentry.SourceGenerators.
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.
// </auto-generated>

#if NET8_0_OR_GREATER
namespace Sentry;
#if NET5_0_OR_GREATER

#nullable enable

[global::System.Runtime.CompilerServices.CompilerGenerated]
public static class BuildVariableInitializer
namespace Sentry.Generated
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{GeneratedCodeText.Tool}}", "{{GeneratedCodeText.Version}}")]
internal static class BuildPropertyInitializer
{
global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary<string, string> {
[global::System.Runtime.CompilerServices.ModuleInitializerAttribute]
internal static void Initialize()
{
global::Sentry.CompilerServices.BuildProperties.Initialize(new global::System.Collections.Generic.Dictionary<string, string>(global::System.StringComparer.OrdinalIgnoreCase)
{

"""
);
Expand All @@ -78,21 +77,21 @@ public static void Initialize()
var pn = EscapeString(property.Replace("build_property.", ""));
var ev = EscapeString(value);
sb
.Append("\t\t\t{")
.Append($"{tabString}{tabString}{tabString}{tabString}{{")
.Append($"\"{pn}\", \"{ev}\"")
.AppendLine("},");
}
}

sb
.AppendLine("\t\t});") // close dictionary
.AppendLine("\t}")
.AppendLine($"{tabString}{tabString}{tabString}}});") // close dictionary
.AppendLine($"{tabString}{tabString}}}")
.AppendLine($"{tabString}}}")
.AppendLine("}")
.AppendLine("#endif");

context.AddSource("__BuildProperties.g.cs", sb.ToString());
context.AddSource("Sentry.Generated.BuildPropertyInitializer.g.cs", sb.ToString());
}


private static string EscapeString(string value) => value.Replace("\\", "\\\\");
}
7 changes: 7 additions & 0 deletions src/Sentry.SourceGenerators/GeneratedCodeText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Sentry.SourceGenerators;

internal static class GeneratedCodeText
{
public static string? Tool { get; } = typeof(BuildPropertySourceGenerator).Assembly.GetName().Name;
public static string? Version { get; } = typeof(BuildPropertySourceGenerator).Assembly.GetName().Version?.ToString();
}
20 changes: 20 additions & 0 deletions src/Sentry.SourceGenerators/OutputKindExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.CodeAnalysis;

namespace Sentry.SourceGenerators;

internal static class OutputKindExtensions
{
internal static bool IsExe(this OutputKind outputKind)
{
return outputKind switch
{
OutputKind.ConsoleApplication => true,
OutputKind.WindowsApplication => true,
OutputKind.DynamicallyLinkedLibrary => false,
OutputKind.NetModule => false,
OutputKind.WindowsRuntimeMetadata => false,
OutputKind.WindowsRuntimeApplication => true,
_ => false,
};
}
}
32 changes: 22 additions & 10 deletions src/Sentry/Internal/AotHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using Sentry.CompilerServices;
using Sentry.Extensibility;

namespace Sentry.Internal;

Expand All @@ -13,31 +15,41 @@ static AotHelper()
IsTrimmed = CheckIsTrimmed();
}


[UnconditionalSuppressMessage("Trimming", "IL2026: RequiresUnreferencedCode", Justification = AvoidAtRuntime)]
private static bool CheckIsTrimmed()
internal static bool CheckIsTrimmed(IDiagnosticLogger? logger = null)
{
if (TryGetBoolean("publishtrimmed", out var trimmed))
if (TryGetBoolean("_IsPublishing", out var isPublishing) && isPublishing)
{
return trimmed;
}
logger?.LogDebug("Detected _IsPublishing");
if (TryGetBoolean("PublishSelfContained", out var selfContained) && selfContained)
{
logger?.LogDebug("Detected PublishSelfContained");
if (TryGetBoolean("PublishTrimmed", out var trimmed))
{
logger?.LogDebug("Detected PublishTrimmed");
return trimmed;
}
}

if (TryGetBoolean("publishaot", out var aot))
{
return aot;
if (TryGetBoolean("PublishAot", out var aot))
{
logger?.LogDebug($"Detected PublishAot: {aot}");
return aot;
}
}

// fallback check
logger?.LogDebug("Stacktrace fallback");
var stackTrace = new StackTrace(false);
return stackTrace.GetFrame(0)?.GetMethod() is null;
}

private static bool TryGetBoolean(string key, out bool value)
{
value = false;
if (BuildProperties.Values?.TryGetValue(key, out var aotValue) ?? false)
if (BuildProperties.Values?.TryGetValue(key, out string? stringValue) ?? false)
{
if (bool.TryParse(aotValue, out var result))
if (bool.TryParse(stringValue, out var result))
{
value = result;
return true;
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Sentry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@
<None Include="buildTransitive\Sentry.props" Pack="true" PackagePath="build\Sentry.props" />
<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" />
<None Include="buildTransitive\Sentry.SourceGenerators.targets" Pack="true" PackagePath="build\Sentry.SourceGenerators.targets" />
</ItemGroup>

<ItemGroup>
Expand All @@ -198,4 +196,5 @@
PackagePath="analyzers/dotnet/cs"
Visible="false" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ internal static IHub InitHub(SentryOptions options)

#pragma warning disable CS0162 // Unreachable code detected
#pragma warning disable 0162 // Unreachable code on old .NET frameworks
AotHelper.CheckIsTrimmed(options.DiagnosticLogger);
options.LogDebug(AotHelper.IsTrimmed
? "This looks like a Native AOT application build."
: "This doesn't look like a Native AOT application build."
Expand Down
9 changes: 0 additions & 9 deletions src/Sentry/buildTransitive/Sentry.SourceGenerators.targets

This file was deleted.

5 changes: 4 additions & 1 deletion src/Sentry/buildTransitive/Sentry.targets
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
<Using Include="Sentry" />
</ItemGroup>

<!-- Consumed by Sentry.SourceGenerators -->
<ItemGroup>
<CompilerVisibleProperty Include="_IsPublishing" />
<CompilerVisibleProperty Include="PublishAot" />
<CompilerVisibleProperty Include="UseDotNetNativeToolchain" />
<CompilerVisibleProperty Include="PublishTrimmed" />
<CompilerVisibleProperty Include="PublishSelfContained" />
<CompilerVisibleProperty Include="UseDotNetNativeToolchain" />
<CompilerVisibleProperty Include="SentryDisableSourceGenerator" />
<CompilerVisibleProperty Include="Configuration" />
</ItemGroup>
Expand Down
Loading
Loading