Skip to content

Commit

Permalink
ModuleInitializer class to help resolve assemblies before attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
ByronMayne committed Apr 28, 2024
1 parent 34f4553 commit cd99a90
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 8 deletions.
11 changes: 3 additions & 8 deletions src/SourceGenerator.Foundations/HoistSourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using SGF.Models;
using SGF.Templates;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Runtime.CompilerServices;

namespace SGF
{

/// <summary>
/// Most basic generator used to generate base class for all the generator
/// classes defined within the project
Expand All @@ -37,10 +33,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
private void AddCoreTypes(SourceProductionContext context, AnalyzerConfigOptionsProvider optionsProvider)
{
string @namespace = "SGF";

context.AddSource("System_Runtime_CompilerServices_ModuleInitializerAttribute.g.cs", ModuleInitializerTemplate.Render());
context.AddSource("SgfSourceGeneratorHoist.g.cs", SourceGeneratorHoistBase.RenderTemplate(@namespace));
context.AddSource("SgfAssemblyResolver.g.cs", AssemblyResolverTemplate.Render(@namespace));

}

private static bool IsSyntaxTargetForGeneration(SyntaxNode s)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis.Text;
using System.Text;

namespace System.Runtime.CompilerServices
{
public static class ModuleInitializerTemplate
{
public static SourceText Render() => SourceText.From($$"""
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Used to indicate to the compiler that a method should be called
/// in its containing module's initializer.
/// </summary>
/// <remarks>
/// When one or more valid methods
/// with this attribute are found in a compilation, the compiler will
/// emit a module initializer which calls each of the attributed methods.
///
/// Certain requirements are imposed on any method targeted with this attribute:
/// - The method must be `static`.
/// - The method must be an ordinary member method, as opposed to a property accessor, constructor, local function, etc.
/// - The method must be parameterless.
/// - The method must return `void`.
/// - The method must not be generic or be contained in a generic type.
/// - The method's effective accessibility must be `internal` or `public`.
///
/// The specification for module initializers in the .NET runtime can be found here:
/// https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer
/// </remarks>
[global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false)]
internal sealed class ModuleInitializerAttribute : global::System.Attribute
{
public ModuleInitializerAttribute()
{
}
}
}
""", Encoding.UTF8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public static SourceText RenderTemplate(string @namespace) => SourceText.From($$
using SGF.Environments;
using SGF.Diagnostics;
using SGF.Diagnostics.Sinks;
using System.Runtime.CompilerServices;
namespace {{@namespace}}
{
Expand All @@ -28,13 +29,28 @@ namespace {{@namespace}}
/// </summary>
internal abstract class SourceGeneratorHoist
{
private static bool s_isInitialized;
private static readonly List<Assembly> s_assembliesWithResources;
private static readonly Dictionary<AssemblyName, Assembly> s_loadedAssemblies;
static SourceGeneratorHoist()
{
s_assembliesWithResources = new List<Assembly>();
s_loadedAssemblies = new Dictionary<AssemblyName, Assembly>(new AssemblyNameComparer());
Initialize();
}
/// <summary>
/// Used to initialize the source generators.
/// </summary>
[ModuleInitializer]
internal static void Initialize()
{
if(s_isInitialized)
{
return;
}
s_isInitialized = true;
// The assembly resolvers get added to multiple source generators
// so what we do here is only allow the first one defined to allow
Expand Down

0 comments on commit cd99a90

Please sign in to comment.