Skip to content

Commit

Permalink
Merge pull request #14 from ByronMayne/feature/hoist-pattern
Browse files Browse the repository at this point in the history
Refactored the runtime to not require Mono.Cecil
  • Loading branch information
ByronMayne authored Feb 17, 2024
2 parents 303d9e3 + b7c7aec commit 627f33c
Show file tree
Hide file tree
Showing 33 changed files with 914 additions and 338 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ jobs:
- name: GitVersion
run: dotnet gitversion /output buildserver
# Build Dependencies
- name: Build | SourceGenerator.Foundations.Injector
run: dotnet build src\SourceGenerator.Foundations.Injector\SourceGenerator.Foundations.Injector.csproj -p:Version=${{env.GitVersion_AssemblySemVer}}
- name: Build | SourceGenerator.Foundations.MSBuild
run: dotnet build src\SourceGenerator.Foundations.MSBuild\SourceGenerator.Foundations.MSBuild.csproj -p:Version=${{env.GitVersion_AssemblySemVer}}
# Build Main
Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,28 @@ Source Generators are awesome but working with them can be a bit painful. This l

## Quick Start

To get started all you need to do is add the NuGet package reference. You may or may not have to restart Visual Studio for the new types to show up. Then implement the base class `IncrementalGenerator`.
To get started all you need to do is add the NuGet package reference. You may or may not have to restart Visual Studio for the new types to show up. Then implement the base class `IncrementalGenerator` and apply the `[SgfGenerator]` attribute to your type;

```cs
namespace Example
{
// IncrementalGenerator, is a generated type from `SourceGenerator.Foundations'
[SgfGenerator]
public class ExampleSourceGenerator : IncrementalGenerator
{
// Constructor can only take two arguments in this order
public ExampleSourceGenerator(
IGeneratorEnvironment generatorEnvironment,
ILogger logger) : base("ExampleSourceGenerator",
generatorPlatform, logger)
{

}

// SgfInitializationContext is a wrapper around IncrementalGeneratorInitializationContext
protected override void OnInitialize(SgfInitializationContext context)
{
// Attaches Visaul Studio debugger without prompt
// Attaches Visual Studio debugger without prompt
AttachDebugger();

// Writes output to Visual Studios output window
Expand All @@ -27,6 +37,10 @@ namespace Example
}
```

> 🛈 You don't apply the `SourceGenerator` attribute or implement the `IIncrementalGenerator` interface. Behind the scenes Source Generator Foundations is generating a class that will host your generator. This is done so that we can capture all errors and also resolve any type loading.


# What is inside?

There is a series of features this library hopes to provide.
Expand Down Expand Up @@ -178,12 +192,6 @@ A very small library that only appears to contain a C# analyzer for adding error

Contains common classes and utility methods that can be leveraged by source generators.

### Source.Generator.Foundations.Injector

Uses `Mono.Cecil` to inject a static call to the `AssemblyResolver` to initialize as soon as the module is loaded. This same effect could be achieved using the `[ModuleInitialize]` attribute. However, this has two downsides. One, if other classes in the referencing project also use this attribute, the order of loading will not be known. Two, this attribute is not part of .netStandard so it has to be injected, there is a chance of conflict.



### Source.Generator.Foundations.MSBuild

Contains a custom MSBuild C# target implementation used to figure out which assemblies should be embedded as resources and which should be ignored. For example this will not embed any resources that are part of `.netstandard`.
Expand Down
3 changes: 0 additions & 3 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
<!-- Paths | SGF Contracts -->
<SGFContractsProjectDir>$(SGFSourceDir)SourceGenerator.Foundations.Contracts\</SGFContractsProjectDir>
<SGFContractsProjectPath>$(SGFContractsProjectDir)SourceGenerator.Foundations.Contracts.csproj</SGFContractsProjectPath>
<!-- Paths | SGF Injector -->
<SGFInjectorProjectDir>$(SGFSourceDir)SourceGenerator.Foundations.Injector\</SGFInjectorProjectDir>
<SGFInjectorProjectPath>$(SGFInjectorProjectDir)SourceGenerator.Foundations.Injector.csproj</SGFInjectorProjectPath>
<!-- Paths | SGF MSBuild -->
<SGFMSBuildProjectDir>$(SGFSourceDir)SourceGenerator.Foundations.MSBuild\</SGFMSBuildProjectDir>
<SGFMSBuildProjectPath>$(SGFMSBuildProjectDir)SourceGenerator.Foundations.MSBuild.csproj</SGFMSBuildProjectPath>
Expand Down
4 changes: 0 additions & 4 deletions src/Nuget.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

<!-- Changes the content of the nuget package by manually adding files-->
<Target Name="AppendNugetContent">
<Message Importance="high" Text="Importing: $(SGFInjectorProjectPath)bin\$(Configuration)\net6.0\*.*"/>
<ItemGroup>
<TfmSpecificPackageFile Include="$(SGFContractsProjectDir)bin\$(Configuration)\netstandard2.0\SourceGenerator.Foundations.Contracts.dll">
<PackagePath>lib/netstandard2.0/SourceGenerator.Foundations.Contracts.dll</PackagePath>
Expand All @@ -31,9 +30,6 @@
<TfmSpecificPackageFile Include="$(SGFMSBuildProjectDir)bin\$(Configuration)netstandard2.0\SourceGenerator.Foundations.MSBuild.*">
<PackagePath>sgf/injector/</PackagePath>
</TfmSpecificPackageFile>
<TfmSpecificPackageFile Include="$(SGFInjectorProjectDir)bin\$(Configuration)\net6.0\**\*.*">
<PackagePath>build</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,21 @@ public void Write(LogLevel logLevel, string message)

if (logLevel >= LogLevel.Warning)
{
Write(message);
Write(message, m_buildOutput);
}

Write(message);
Write(message, m_sourceGeneratorOutput);
}


/// <summary>
/// Writes an entry to the output window if it has been initialized
/// </summary>
public void Write(string message)
public void Write(string message, OutputWindowPane? outputPain)
{
try
{
m_sourceGeneratorOutput?.OutputString(message);
outputPain?.OutputString(message);
}
catch (COMException)
{
Expand All @@ -122,11 +122,5 @@ public void Write(string message)
Console.WriteLine($"Exception was thrown while writing log. Please report this on gihub {exception}");
}
}

/// <summary>
/// Writes an entry to the output window if it has been initialized
/// </summary>
public void WriteLine(string message)
=> Write($"{message}{Environment.NewLine}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@
<PackageReference Include="System.Management" Version="7.0.0" GeneratePathProperty="true" />
<ProjectReference Include="$(SGFContractsProjectPath)" />
</ItemGroup>
<Import Project="$(SGFSharedProjectItemsPath)" Label="Shared" />
<Import Project="$(SGFTargetsPath)" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ namespace SGF
/// <summary>
/// Represents a enviroment where the user is authoring code in Visual Studio
/// </summary>
internal class WindowsDevelopmentPlatform : IDevelopmentPlatform
internal class WindowsDevelopmentPlatform : IGeneratorEnvironment
{
public PlatformType Type { get; }

public string Name { get; }


public WindowsDevelopmentPlatform()
{
Name = "VisualStudio";
Type = PlatformType.VisualStudio;

if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("VisualStudioVersion")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
<IsRoslynComponent>true</IsRoslynComponent>
<Platforms>AnyCPU;x64</Platforms>
<AssemblySearchPath_UseOutDir>true</AssemblySearchPath_UseOutDir>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SGFWindowsPluginProjectPath)" />
<ProjectReference Include="..\..\SourceGenerator.Foundations.Contracts\SourceGenerator.Foundations.Contracts.csproj" />
<ProjectReference Include="..\..\SourceGenerator.Foundations\SourceGenerator.Foundations.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<Import Project="..\..\SourceGenerator.Foundations.Shared\SourceGenerator.Foundations.Shared.projitems" Label="Shared" />
<Import Project="$(SGFTargetsPath)" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace ConsoleApp.SourceGenerator
{
[Generator]
[SgfGenerator]
internal class ConsoleAppSourceGenerator : IncrementalGenerator
{
public class Payload
Expand All @@ -15,10 +15,12 @@ public class Payload
public string? Version { get; set; }
}

public ConsoleAppSourceGenerator() : base("ConsoleAppSourceGenerator")
{ }
public ConsoleAppSourceGenerator(IGeneratorEnvironment generatorEnvironment, ILogger logger) : base("ConsoleAppSourceGenerator", generatorEnvironment, logger)
{

}

protected override void OnInitialize(SgfInitializationContext context)
public override void OnInitialize(SgfInitializationContext context)
{
Payload payload = new()
{
Expand Down
1 change: 1 addition & 0 deletions src/Sandbox/ConsoleApp/ConsoleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ConsoleApp.SourceGenerator\ConsoleApp.SourceGenerator.csproj">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
using System.Collections.Generic;
using System.Diagnostics;

namespace SGF.Platforms
namespace SGF.Environments
{
internal class GenericDevelopmentPlatform : IDevelopmentPlatform
public class GenericDevelopmentEnvironment : IGeneratorEnvironment
{
public string Name { get; }

public GenericDevelopmentEnvironment()
{
Name = "Generic";
}

public bool AttachDebugger(int processId)
{
return Debugger.Launch();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
namespace SGF
{
/// <summary>
/// Abstracts for development environments supported by the platform
/// Contains information about the enviroment that this source generator is running in. It allows you to
/// star tthe debuggger and get the custom platform loggers based on the context.
/// </summary>
public interface IDevelopmentPlatform
public interface IGeneratorEnvironment
{
/// <summary>
/// Gets the name of the environment
/// </summary>
string Name { get; }

/// <summary>
/// Attaches the debugger to the given process Id and returns back if it was successful or not. This can
/// fail if Visual Studio is not already running
Expand Down
90 changes: 90 additions & 0 deletions src/SourceGenerator.Foundations.Contracts/IncrementalGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
using SGF.Diagnostics;
using Microsoft.CodeAnalysis;

namespace SGF
{
/// <summary>
/// Used as a base class for creating your own source generator. This class provides some helper
/// methods and impoved debugging expereince. The generator that implements this must apply the
/// <see cref="GeneratorAttribute"/> but not inheirt from <see cref="IIncrementalGenerator"/>
/// </summary>
public abstract class IncrementalGenerator : IDisposable
{
private readonly IGeneratorEnvironment m_developmentPlatform;

/// <summary>
/// Gets the name of the source generator
/// </summary>
public string Name { get; }

/// <summary>
/// Gets the log that can allow you to output information to your
/// IDE of choice
/// </summary>
public ILogger Logger { get; }


/// <summary>
/// Initializes a new instance of the incremental generator with an optional name
/// </summary>
protected IncrementalGenerator(
string? name,
IGeneratorEnvironment developmentPlatform,
ILogger logger)
{
m_developmentPlatform = developmentPlatform;
Name = name ?? GetType().Name;
Logger = logger;
}

/// <summary>
/// Implement to initalize the incremental source generator
/// </ summary >
public abstract void OnInitialize(SgfInitializationContext context);

/// <summary>
/// Override to add logic for disposing this instance and free resources
/// </summary>
protected virtual void Dipose()
{ }

/// <summary>
/// Attaches the debugger automtically if you are running from Visual Studio. You have the option
/// to stop or just continue
/// </summary>
protected void AttachDebugger()
{
Process process = Process.GetCurrentProcess();
m_developmentPlatform.AttachDebugger(process.Id);
}

/// <summary>
/// Raised when one of the generator functions throws an unhandle exception. Override this to define your own behaviour
/// to handle the exception.
/// </summary>
/// <param name="exception">The exception that was thrown</param>
protected virtual void OnException(Exception exception)
{
Logger.Error(exception, $"Unhandled exception was throw while running the generator {Name}");
}

/// <summary>
/// Events raised when the exception is being thrown by the app domain
/// </summary>
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception exception)
{
OnException(exception);
}
}

/// <inheritdoc cref="IDisposable"/>
void IDisposable.Dispose()
{
Dipose();
}
}
}
14 changes: 14 additions & 0 deletions src/SourceGenerator.Foundations.Contracts/SgfGeneratorAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace SGF
{
/// <summary>
/// Applied a class the inheirts from <see cref="IncrementalGenerator"/>
/// that will have Source Generator Foundations wrapper generated around it. This adds
/// better error handling and logging to the given generator.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class SgfGeneratorAttribute : Attribute
{
}
}
Loading

0 comments on commit 627f33c

Please sign in to comment.