Skip to content

Commit 9671e05

Browse files
authored
Merge pull request github#16213 from tamasvajk/buildless/source-generators
C#: Move source code generators to dedicated classes
2 parents ff55ed8 + 13a71a4 commit 9671e05

File tree

7 files changed

+216
-114
lines changed

7 files changed

+216
-114
lines changed

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -149,24 +149,20 @@ void exitCallback(int ret, string msg, bool silent)
149149
logger.LogDebug($"Unresolved reference {r.Key} in project {r.Value}");
150150
}
151151

152-
var webViewExtractionOption = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WebViewGeneration);
153-
if (webViewExtractionOption == null ||
154-
bool.TryParse(webViewExtractionOption, out var shouldExtractWebViews) &&
155-
shouldExtractWebViews)
152+
var sourceGenerators = new ISourceGenerator[]
156153
{
157-
CompilationInfos.Add(("WebView extraction enabled", "1"));
158-
GenerateSourceFilesFromWebViews();
159-
}
160-
else
154+
new ImplicitUsingsGenerator(fileContent, logger, tempWorkingDirectory),
155+
new WebViewGenerator(fileProvider, fileContent, dotnet, this, logger, tempWorkingDirectory, usedReferences.Keys)
156+
};
157+
158+
foreach (var sourceGenerator in sourceGenerators)
161159
{
162-
CompilationInfos.Add(("WebView extraction enabled", "0"));
160+
this.generatedSources.AddRange(sourceGenerator.Generate());
163161
}
164162

165163
CompilationInfos.Add(("UseWPF set", fileContent.UseWpf ? "1" : "0"));
166164
CompilationInfos.Add(("UseWindowsForms set", fileContent.UseWindowsForms ? "1" : "0"));
167165

168-
GenerateSourceFileFromImplicitUsings();
169-
170166
const int align = 6;
171167
logger.LogInfo("");
172168
logger.LogInfo("Build analysis summary:");
@@ -365,14 +361,9 @@ private void RemoveNugetPackageReference(string packagePrefix, ISet<AssemblyLook
365361
}
366362
}
367363

368-
private bool IsAspNetCoreDetected()
369-
{
370-
return fileContent.IsNewProjectStructureUsed && fileContent.UseAspNetCoreDlls;
371-
}
372-
373364
private void AddAspNetCoreFrameworkDlls(ISet<AssemblyLookupLocation> dllLocations, ISet<string> frameworkLocations)
374365
{
375-
if (!IsAspNetCoreDetected())
366+
if (!fileContent.IsAspNetCoreDetected)
376367
{
377368
return;
378369
}
@@ -413,103 +404,6 @@ private void AddMicrosoftWindowsDesktopDlls(ISet<AssemblyLookupLocation> dllLoca
413404
.FullName;
414405
}
415406

416-
private void GenerateSourceFileFromImplicitUsings()
417-
{
418-
var usings = new HashSet<string>();
419-
if (!fileContent.UseImplicitUsings)
420-
{
421-
return;
422-
}
423-
424-
// Hardcoded values from https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview#implicit-using-directives
425-
usings.UnionWith([ "System", "System.Collections.Generic", "System.IO", "System.Linq", "System.Net.Http", "System.Threading",
426-
"System.Threading.Tasks" ]);
427-
428-
if (fileContent.UseAspNetCoreDlls)
429-
{
430-
usings.UnionWith([ "System.Net.Http.Json", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Hosting",
431-
"Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Routing", "Microsoft.Extensions.Configuration",
432-
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.Hosting", "Microsoft.Extensions.Logging" ]);
433-
}
434-
435-
if (fileContent.UseWindowsForms)
436-
{
437-
usings.UnionWith(["System.Drawing", "System.Windows.Forms"]);
438-
}
439-
440-
usings.UnionWith(fileContent.CustomImplicitUsings);
441-
442-
logger.LogInfo($"Generating source file for implicit usings. Namespaces: {string.Join(", ", usings.OrderBy(u => u))}");
443-
444-
if (usings.Count > 0)
445-
{
446-
var tempDir = GetTemporaryWorkingDirectory("implicitUsings");
447-
var path = Path.Combine(tempDir, "GlobalUsings.g.cs");
448-
using (var writer = new StreamWriter(path))
449-
{
450-
writer.WriteLine("// <auto-generated/>");
451-
writer.WriteLine("");
452-
453-
foreach (var u in usings.OrderBy(u => u))
454-
{
455-
writer.WriteLine($"global using global::{u};");
456-
}
457-
}
458-
459-
this.generatedSources.Add(path);
460-
}
461-
}
462-
463-
private void GenerateSourceFilesFromWebViews()
464-
{
465-
var views = fileProvider.RazorViews;
466-
if (views.Count == 0)
467-
{
468-
return;
469-
}
470-
471-
logger.LogInfo($"Found {views.Count} cshtml and razor files.");
472-
473-
if (!IsAspNetCoreDetected())
474-
{
475-
logger.LogInfo("Generating source files from cshtml files is only supported for new (SDK-style) project files");
476-
return;
477-
}
478-
479-
logger.LogInfo("Generating source files from cshtml and razor files...");
480-
481-
var sdk = new Sdk(dotnet).GetNewestSdk();
482-
if (sdk != null)
483-
{
484-
try
485-
{
486-
var razor = new Razor(sdk, dotnet, logger);
487-
var targetDir = GetTemporaryWorkingDirectory("razor");
488-
var generatedFiles = razor.GenerateFiles(views, usedReferences.Keys, targetDir);
489-
this.generatedSources.AddRange(generatedFiles);
490-
}
491-
catch (Exception ex)
492-
{
493-
// It's okay, we tried our best to generate source files from cshtml files.
494-
logger.LogInfo($"Failed to generate source files from cshtml files: {ex.Message}");
495-
}
496-
}
497-
}
498-
499-
/// <summary>
500-
/// Creates a temporary directory with the given subfolder name.
501-
/// The created directory might be inside the repo folder, and it is deleted when the object is disposed.
502-
/// </summary>
503-
/// <param name="subfolder"></param>
504-
/// <returns></returns>
505-
private string GetTemporaryWorkingDirectory(string subfolder)
506-
{
507-
var temp = Path.Combine(tempWorkingDirectory.ToString(), subfolder);
508-
Directory.CreateDirectory(temp);
509-
510-
return temp;
511-
}
512-
513407
/// <summary>
514408
/// Resolves conflicts between all of the resolved references.
515409
/// If the same assembly name is duplicated with different versions,

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ public bool UseAspNetCoreDlls
5050
}
5151
}
5252

53+
public bool IsAspNetCoreDetected
54+
{
55+
get
56+
{
57+
return IsNewProjectStructureUsed && UseAspNetCoreDlls;
58+
}
59+
}
60+
5361
private bool useImplicitUsings = false;
5462

5563
public bool UseImplicitUsings
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Collections.Generic;
2+
3+
namespace Semmle.Extraction.CSharp.DependencyFetching
4+
{
5+
public interface ISourceGenerator
6+
{
7+
/// <summary>
8+
/// Returns the paths to the generated source files.
9+
/// </summary>
10+
IEnumerable<string> Generate();
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using Semmle.Util;
5+
using Semmle.Util.Logging;
6+
7+
namespace Semmle.Extraction.CSharp.DependencyFetching
8+
{
9+
internal class ImplicitUsingsGenerator : SourceGeneratorBase
10+
{
11+
private readonly FileContent fileContent;
12+
13+
public ImplicitUsingsGenerator(FileContent fileContent, ILogger logger, TemporaryDirectory tempWorkingDirectory)
14+
: base(logger, tempWorkingDirectory)
15+
{
16+
this.fileContent = fileContent;
17+
}
18+
19+
public override IEnumerable<string> Generate()
20+
{
21+
var usings = new HashSet<string>();
22+
if (!fileContent.UseImplicitUsings)
23+
{
24+
logger.LogDebug("Skipping implicit usings generation");
25+
return [];
26+
}
27+
28+
// Hardcoded values from https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview#implicit-using-directives
29+
usings.UnionWith([ "System", "System.Collections.Generic", "System.IO", "System.Linq", "System.Net.Http", "System.Threading",
30+
"System.Threading.Tasks" ]);
31+
32+
if (fileContent.UseAspNetCoreDlls)
33+
{
34+
usings.UnionWith([ "System.Net.Http.Json", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Hosting",
35+
"Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Routing", "Microsoft.Extensions.Configuration",
36+
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.Hosting", "Microsoft.Extensions.Logging" ]);
37+
}
38+
39+
if (fileContent.UseWindowsForms)
40+
{
41+
usings.UnionWith(["System.Drawing", "System.Windows.Forms"]);
42+
}
43+
44+
usings.UnionWith(fileContent.CustomImplicitUsings);
45+
46+
logger.LogInfo($"Generating source file for implicit usings. Namespaces: {string.Join(", ", usings.OrderBy(u => u))}");
47+
48+
if (usings.Count > 0)
49+
{
50+
var tempDir = GetTemporaryWorkingDirectory("implicitUsings");
51+
var path = Path.Combine(tempDir, "GlobalUsings.g.cs");
52+
using (var writer = new StreamWriter(path))
53+
{
54+
writer.WriteLine("// <auto-generated/>");
55+
writer.WriteLine("");
56+
57+
foreach (var u in usings.OrderBy(u => u))
58+
{
59+
writer.WriteLine($"global using global::{u};");
60+
}
61+
}
62+
63+
return [path];
64+
}
65+
66+
return [];
67+
}
68+
}
69+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using Semmle.Util;
4+
using Semmle.Util.Logging;
5+
6+
namespace Semmle.Extraction.CSharp.DependencyFetching
7+
{
8+
internal abstract class SourceGeneratorBase : ISourceGenerator
9+
{
10+
protected readonly ILogger logger;
11+
protected readonly TemporaryDirectory tempWorkingDirectory;
12+
13+
public SourceGeneratorBase(ILogger logger, TemporaryDirectory tempWorkingDirectory)
14+
{
15+
this.logger = logger;
16+
this.tempWorkingDirectory = tempWorkingDirectory;
17+
}
18+
19+
public abstract IEnumerable<string> Generate();
20+
21+
/// <summary>
22+
/// Creates a temporary directory with the given subfolder name.
23+
/// The created directory might be inside the repo folder, and it is deleted when the temporary working directory is disposed.
24+
/// </summary>
25+
protected string GetTemporaryWorkingDirectory(string subfolder)
26+
{
27+
var temp = Path.Combine(tempWorkingDirectory.ToString(), subfolder);
28+
Directory.CreateDirectory(temp);
29+
30+
return temp;
31+
}
32+
}
33+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Semmle.Util;
4+
using Semmle.Util.Logging;
5+
6+
namespace Semmle.Extraction.CSharp.DependencyFetching
7+
{
8+
internal class WebViewGenerator : SourceGeneratorBase
9+
{
10+
private readonly FileProvider fileProvider;
11+
private readonly FileContent fileContent;
12+
private readonly IDotNet dotnet;
13+
private readonly ICompilationInfoContainer compilationInfoContainer;
14+
private readonly IEnumerable<string> references;
15+
16+
public WebViewGenerator(
17+
FileProvider fileProvider,
18+
FileContent fileContent,
19+
IDotNet dotnet,
20+
ICompilationInfoContainer compilationInfoContainer,
21+
ILogger logger,
22+
TemporaryDirectory tempWorkingDirectory,
23+
IEnumerable<string> references) : base(logger, tempWorkingDirectory)
24+
{
25+
this.fileProvider = fileProvider;
26+
this.fileContent = fileContent;
27+
this.dotnet = dotnet;
28+
this.compilationInfoContainer = compilationInfoContainer;
29+
this.references = references;
30+
}
31+
32+
public override IEnumerable<string> Generate()
33+
{
34+
var webViewExtractionOption = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WebViewGeneration);
35+
if (webViewExtractionOption == null ||
36+
bool.TryParse(webViewExtractionOption, out var shouldExtractWebViews) &&
37+
shouldExtractWebViews)
38+
{
39+
compilationInfoContainer.CompilationInfos.Add(("WebView extraction enabled", "1"));
40+
return GenerateSourceFilesFromWebViews();
41+
}
42+
43+
compilationInfoContainer.CompilationInfos.Add(("WebView extraction enabled", "0"));
44+
return [];
45+
}
46+
47+
private IEnumerable<string> GenerateSourceFilesFromWebViews()
48+
{
49+
var views = fileProvider.RazorViews;
50+
if (views.Count == 0)
51+
{
52+
logger.LogDebug("No cshtml or razor files found.");
53+
return [];
54+
}
55+
56+
logger.LogInfo($"Found {views.Count} cshtml and razor files.");
57+
58+
if (!fileContent.IsAspNetCoreDetected)
59+
{
60+
logger.LogInfo("Generating source files from cshtml files is only supported for new (SDK-style) project files");
61+
return [];
62+
}
63+
64+
logger.LogInfo("Generating source files from cshtml and razor files...");
65+
66+
var sdk = new Sdk(dotnet).GetNewestSdk();
67+
if (sdk != null)
68+
{
69+
try
70+
{
71+
var razor = new Razor(sdk, dotnet, logger);
72+
var targetDir = GetTemporaryWorkingDirectory("razor");
73+
var generatedFiles = razor.GenerateFiles(views, references, targetDir);
74+
return generatedFiles ?? [];
75+
}
76+
catch (Exception ex)
77+
{
78+
// It's okay, we tried our best to generate source files from cshtml files.
79+
logger.LogInfo($"Failed to generate source files from cshtml files: {ex.Message}");
80+
}
81+
}
82+
83+
return [];
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)