Search code examples
c#sourcegenerators

C# IncrementalSourceGenerator EmitCompilerGeneratedFiles fails on second compile as members is already defined


I am trying to implement a C# source generator that can take Template files in a project and turn those into C# Methods, e.g. (Simplified)

MyTemplate.html =>

internal partial class HtmlTemplates {
  public static string MyTemplate( ...some args... ) => ... template code ...;
}

I basically tried to be inspired by

And I am ending up with the problem described in part 6 of that last one: https://andrewlock.net/creating-a-source-generator-part-6-saving-source-generator-output-in-source-control/

ColoursExtensions_EnumExtensions.g.cs(31,28): error CS0111: Type 'ColoursExtensions' already defines a member called 'IsDefined' with the same parameter types ColoursExtensions_EnumExtensions.g.cs(40,28): error CS0111: Type 'ColoursExtensions' already defines a member called 'TryParse' with the same parameter types  

My problem is that if I apply the proposed fix:

<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />

I am no longer able to call my Templating methods.

For now I have boiled the code back down to: (I figured that the actual process of parsing and turning a template into code is not really important here, so that is omitted, simple dummy code can be done for all of it)

[Generator(LanguageNames.CSharp)]
public class TemplateGenerator : IIncrementalGenerator
{
    private readonly StringTemplateBuilder builder = new StringTemplateBuilder();

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {

        IncrementalValuesProvider<AdditionalText> templateFiles = context.AdditionalTextsProvider
            .Where(static file => file.Path.EndsWith(".html")); //Note hardcoded filetype for now.

        IncrementalValuesProvider<StringTemplate> templates = templateFiles
            .SelectMany(builder.Build);

        context.RegisterSourceOutput(templates, (spc, template) => {
            spc.AddSource($"HtmlTemplates.{template.Name}.g.cs", template.ToString());
        });
    }
}

And then working with a combination of using:

<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/**/*.cs" />

But I can't figure out how to get this working.


Solution

  • For my usecase it turned out that not using any of the suggested settings works out best. And it would seem that the reasons I could not get that to work was more to do with instability when developing a source generator.

    At the same time there also seem to be some instability around consuming them from nuget combined with updating from SourceControl etc.