Skip to content

Commit

Permalink
Avoid conflicts with multiple helper classes from multiple assemblies
Browse files Browse the repository at this point in the history
Since we were adding the EmbeddedResource helper as a global, project-wide class, if multiple projects happened to use resources, collisions would start to arise, with no way to disambiguate.

Since the code in the helper is actually pretty straightforward and will very likely be inlined into the IL in the end anyway, we instead just add its code as part of the resource area being emitted itself. This might result in a slightly larger assembly if there are a ton of areas, but that's a somewhat unlikely scenario.

Fixes #442
  • Loading branch information
kzu committed Feb 18, 2025
1 parent 96b2aa0 commit 8719e47
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/SponsorLink/SponsorLink.Analyzer.Tests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' == 'true'">
<PackageReference Include="Humanizer.Core" VersionOverride="2.14.1" PrivateAssets="all" Pack="false" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" PrivateAssets="all" Pack="false" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.4.0" PrivateAssets="all" Pack="false" />
</ItemGroup>

<ItemGroup Condition="'$(ManagePackageVersionsCentrally)' != 'true'">
<PackageReference Include="Humanizer.Core" Version="2.14.1" PrivateAssets="all" Pack="false" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.2.1" PrivateAssets="all" Pack="false" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.4.0" PrivateAssets="all" Pack="false" />
</ItemGroup>

<Target Name="AddSponsorLinkAnalyzerDependencies" BeforeTargets="CoreCompile" DependsOnTargets="ResolveLockFileCopyLocalFiles">
Expand Down
59 changes: 53 additions & 6 deletions src/ThisAssembly.Resources/CSharp.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,72 @@
{{~ end ~}}
public static partial class {{ $0.Name }}
{
#if DEBUG
static readonly string baseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "";
#endif

{{~ if $0.IsText ~}}
private static string text;

/// <summary>
/// Gets the resource as plain text.
/// </summary>
public static string Text =>
text ??= EmbeddedResource.GetContent(@"{{ $0.Path }}");
public static string Text => text ??= GetContent(@"{{ $0.Path }}");
{{~ end ~}}

/// <summary>
/// Gets the resource as a byte array.
/// </summary>
public static byte[] GetBytes() =>
EmbeddedResource.GetBytes(@"{{ $0.Path }}");
public static byte[] GetBytes() => GetBytes(@"{{ $0.Path }}");

/// <summary>
/// Gets the resource as a stream.
/// </summary>
public static Stream GetStream() =>
EmbeddedResource.GetStream(@"{{ $0.Path }}");
public static Stream GetStream() => GetStream(@"{{ $0.Path }}");

/// <summary>
/// Gets the content of the embedded resource at the specified relative path.
/// </summary>
static string GetContent(string relativePath)
{
using var stream = GetStream(relativePath);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}

/// <summary>
/// Gets the bytes of the embedded resource at the specified relative path.
/// </summary>
static byte[] GetBytes(string relativePath)
{
using var stream = GetStream(relativePath);
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return bytes;
}

/// <summary>
/// Gets the stream of the embedded resource at the specified relative path.
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
static Stream GetStream(string relativePath)
{
var baseName = Assembly.GetExecutingAssembly().GetName().Name;
var resourceName = relativePath
.TrimStart('.')
.Replace('/', '.')
.Replace('\\', '.');

var manifestResourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames().FirstOrDefault(x => x.EndsWith(resourceName, StringComparison.Ordinal));

if (string.IsNullOrEmpty(manifestResourceName))
throw new InvalidOperationException($"Did not find required resource ending in '{resourceName}' in assembly '{baseName}'.");

return
Assembly.GetExecutingAssembly().GetManifestResourceStream(manifestResourceName) ??
throw new InvalidOperationException($"Did not find required resource '{manifestResourceName}' in assembly '{baseName}'.");
}
}
{{ end }}
{{ func render }}
Expand All @@ -70,6 +115,8 @@

using System;
using System.IO;
using System.Linq;
using System.Reflection;
{{ if Namespace }}
namespace {{ Namespace }};
{{~ end ~}}
Expand Down
5 changes: 0 additions & 5 deletions src/ThisAssembly.Resources/ResourcesGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ public class ResourcesGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(
spc => spc.AddSource(
"ThisAssembly.EmbeddedResource.cs",
SourceText.From(EmbeddedResource.GetContent("EmbeddedResource.cs"), Encoding.UTF8)));

var files = context.AdditionalTextsProvider
.Combine(context.AnalyzerConfigOptionsProvider)
.Where(x =>
Expand Down

0 comments on commit 8719e47

Please sign in to comment.