File to CSharp Const
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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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 {{ 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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | //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