I was getting tired how many times I am reading from embedded resources – and finding it over and over the same code. So – what if I will transform the file into C# text ? Seems a work for Roslyn Source Code Generator – and the new one
IIncrementalGenerator
https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md,not the old ISourceGenerator https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview
So the code reads from additional files defined in .csproj
<ItemGroup>
<AdditionalFiles Include=”Second.gen.txt” />
<AdditionalFiles Include=”first.gen.txt” />
<AdditionalFiles Include=”test\Afirst.gen.txt” />
< /ItemGroup>
and then transforms in a const:
string x= MyAdditionalFiles.Second_gen_txt;
Now,how difficult is the code?
The first version was
public void Initialize(IncrementalGeneratorInitializationContext initContext) { IncrementalValuesProvider<AdditionalText> textFiles = initContext .AdditionalTextsProvider .Where(file => file.Path.Contains(".gen.")); // read their contents and save their name IncrementalValuesProvider<(string name,string content)> namesAndContents = textFiles .Select((text,cancellationToken) => (name: Path.GetFileName(text.Path),content: text.GetText(cancellationToken)!.ToString())); // generate a class that contains their values as const strings initContext.RegisterSourceOutput(namesAndContents,(spc,nameAndContent) => { string nameString=nameAndContent.name .Replace(".","_") .Replace("-","_") ; string[] lines = nameAndContent.content .Split('\n','\r') .Where(it => it.Trim().Length > 0) .Select(it => it.Replace("\\","\\\\")) .Select(it => it.Replace("\"","\\\"")) .Select(it => "\"" + it + "\\r\\n" + "\"") .ToArray(); string content = string.Join("\r\n+",lines); string hint = $"MyAdditionalFiles.{nameAndContent.name}"; var str = $@" public static partial class MyAdditionalFiles {{ //https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string public const string {nameString} = {content}; }}"; spc.AddSource(hint,str ); }); }
But it have performed poorly ( 3 minutes build for a file with 2MB ) . I was thinking that splitting into lines and replacing quotes and / was the faulty
So raw strings,https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string,to the help – the second version is
//v1 //string[] lines = nameAndContent.content // .Split('\n','\r') // .Where(it => it.Trim().Length > 0) // .Select(it => it.Replace("\\","\\\\")) // .Select(it=>it.Replace("\"","\\\"")) // .Select(it => "\"" + it +"\\r\\n"+ "\"") // .ToArray(); //string content = string.Join("\r\n+",lines); //v2 => .net 7 string content=nameAndContent.content; //too much,but otherwise start counting quotes var quotes = new string('"',10); content = quotes+"\r\n"+ content+"\r\n"+ quotes;
Additional details to be mentioned:
1.The Release with GitHUb Actions should also to be mentioned –the deployment to NUGET is a easy way now
2. Debugging is relative easy https://github.com/JoanComasFdz/dotnet-how-to-debug-source-generator-vs2022
3. It is a day of work to get rid of embedded files!
4. Does not work with binary files ( error with AdditionalFiles )
You can find the package at https://github.com/ignatandrei/RSCG_Utils .
Leave a Reply