Skip to content

Commit 3626c81

Browse files
committed
Run dotnet source generators on files grouped by projects
1 parent bef556e commit 3626c81

File tree

5 files changed

+85
-18
lines changed

5 files changed

+85
-18
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorBase.cs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
35
using Semmle.Util;
46
using Semmle.Util.Logging;
57

@@ -31,8 +33,7 @@ public DotnetSourceGeneratorBase(
3133

3234
protected override IEnumerable<string> Run()
3335
{
34-
var additionalFiles = AdditionalFiles;
35-
if (additionalFiles.Count == 0)
36+
if (AdditionalFiles.Count == 0)
3637
{
3738
logger.LogDebug($"No {FileType} files found. Skipping source generation.");
3839
return [];
@@ -44,16 +45,51 @@ protected override IEnumerable<string> Run()
4445
return [];
4546
}
4647

47-
logger.LogInfo($"Generating source files from {additionalFiles.Count} {FileType} files...");
48+
if (fileProvider.Projects.Count == 0)
49+
{
50+
logger.LogInfo($"No projects found. Skipping source generation from {FileType} files.");
51+
return [];
52+
}
53+
54+
logger.LogInfo($"Generating source files from {AdditionalFiles.Count} {FileType} files...");
55+
56+
// group additional files by closes project file:
57+
var projects = fileProvider.Projects
58+
.Select(p => (File: p, Directory: SafeGetDirectoryName(p)))
59+
.Where(p => p.Directory.Length > 0);
60+
61+
var groupedFiles = new Dictionary<string, List<string>>();
62+
63+
foreach (var additionalFile in AdditionalFiles)
64+
{
65+
var project = projects
66+
.Where(p => additionalFile.StartsWith(p.Directory))
67+
.OrderByDescending(p => p.Directory.Length)
68+
.FirstOrDefault();
69+
70+
if (project == default)
71+
{
72+
logger.LogDebug($"Failed to find project file for {additionalFile}");
73+
continue;
74+
}
75+
76+
if (!groupedFiles.TryGetValue(project.File, out var files))
77+
{
78+
files = [];
79+
groupedFiles[project.File] = files;
80+
}
81+
82+
files.Add(additionalFile);
83+
}
4884

4985
try
5086
{
5187
var sdk = new Sdk(dotnet, logger);
5288
var sourceGenerator = GetSourceGenerator(sdk);
5389
var targetDir = GetTemporaryWorkingDirectory(FileType.ToLowerInvariant());
54-
// todo: run the below in a loop, on groups of files belonging to the same project:
55-
var generatedFiles = sourceGenerator.RunSourceGenerator(additionalFiles, references, targetDir);
56-
return generatedFiles ?? [];
90+
91+
return groupedFiles
92+
.SelectMany(group => sourceGenerator.RunSourceGenerator(group.Value, group.Key, references, targetDir));
5793
}
5894
catch (Exception ex)
5995
{
@@ -63,6 +99,30 @@ protected override IEnumerable<string> Run()
6399
}
64100
}
65101

102+
private string SafeGetDirectoryName(string fileName)
103+
{
104+
try
105+
{
106+
var dir = Path.GetDirectoryName(fileName);
107+
if (dir is null)
108+
{
109+
return "";
110+
}
111+
112+
if (!dir.EndsWith(Path.DirectorySeparatorChar))
113+
{
114+
dir += Path.DirectorySeparatorChar;
115+
}
116+
117+
return dir;
118+
}
119+
catch (Exception ex)
120+
{
121+
logger.LogDebug($"Failed to get directory name for {fileName}: {ex.Message}");
122+
return "";
123+
}
124+
}
125+
66126
protected abstract ICollection<string> AdditionalFiles { get; }
67127

68128
protected abstract string FileType { get; }

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ public DotnetSourceGeneratorWrapper(
3131
this.cscPath = sdk.CscPath;
3232
}
3333

34-
protected abstract void GenerateAnalyzerConfig(IEnumerable<string> additionalFiles, string analyzerConfigPath);
34+
protected abstract void GenerateAnalyzerConfig(IEnumerable<string> additionalFiles, string csprojFile, string analyzerConfigPath);
3535

36-
public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFiles, IEnumerable<string> references, string targetDir)
36+
public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFiles, string csprojFile, IEnumerable<string> references, string targetDir)
3737
{
3838
var name = Guid.NewGuid().ToString("N").ToUpper();
3939
var tempPath = FileUtils.GetTemporaryWorkingDirectory(out var shouldCleanUp);
@@ -46,7 +46,7 @@ public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFile
4646
try
4747
{
4848
logger.LogInfo("Producing analyzer config content.");
49-
GenerateAnalyzerConfig(additionalFiles, analyzerConfig);
49+
GenerateAnalyzerConfig(additionalFiles, csprojFile, analyzerConfig);
5050

5151
logger.LogDebug($"Analyzer config content: {File.ReadAllText(analyzerConfig)}");
5252

@@ -86,6 +86,11 @@ public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFile
8686

8787
return files;
8888
}
89+
catch (Exception ex)
90+
{
91+
logger.LogInfo($"Failed to generate source files from {FileType} files: {ex.Message}");
92+
return [];
93+
}
8994
finally
9095
{
9196
if (shouldCleanUp)

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Razor(Sdk sdk, IDotNet dotNet, ILogger logger) : base(sdk, dotNet, logger
2929
}
3030
}
3131

32-
protected override void GenerateAnalyzerConfig(IEnumerable<string> cshtmls, string analyzerConfigPath)
32+
protected override void GenerateAnalyzerConfig(IEnumerable<string> cshtmls, string csprojFile, string analyzerConfigPath)
3333
{
3434
using var sw = new StreamWriter(analyzerConfigPath);
3535
sw.WriteLine("is_global = true");

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/Resx.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ public Resx(Sdk sdk, IDotNet dotNet, ILogger logger, string? sourceGeneratorFold
2222
SourceGeneratorFolder = sourceGeneratorFolder;
2323
}
2424

25-
protected override void GenerateAnalyzerConfig(IEnumerable<string> resources, string analyzerConfigPath)
25+
protected override void GenerateAnalyzerConfig(IEnumerable<string> resources, string csprojFile, string analyzerConfigPath)
2626
{
2727
using var sw = new StreamWriter(analyzerConfigPath);
2828
sw.WriteLine("is_global = true");
29-
sw.WriteLine("build_property.RootNamespace = abc"); // todo: fix the namespace
29+
30+
var rootNamespace = Path.GetFileNameWithoutExtension(csprojFile);
31+
sw.WriteLine($"build_property.RootNamespace = {rootNamespace}");
3032

3133
foreach (var f in resources.Select(f => f.Replace('\\', '/')))
3234
{
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
| Program | Program.<Main>$ |
22
| Program | Program.Program |
3-
| abc.test | abc.test.Culture |
4-
| abc.test | abc.test.GetResourceString |
5-
| abc.test | abc.test.Key123 |
6-
| abc.test | abc.test.Key456 |
7-
| abc.test | abc.test.ResourceManager |
8-
| abc.test | abc.test.s_resourceManager |
3+
| resx.test | resx.test.Culture |
4+
| resx.test | resx.test.GetResourceString |
5+
| resx.test | resx.test.Key123 |
6+
| resx.test | resx.test.Key456 |
7+
| resx.test | resx.test.ResourceManager |
8+
| resx.test | resx.test.s_resourceManager |

0 commit comments

Comments
 (0)