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