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 .