forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDotnetSourceGeneratorWrapper.cs
More file actions
97 lines (79 loc) · 4.05 KB
/
DotnetSourceGeneratorWrapper.cs
File metadata and controls
97 lines (79 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal abstract class DotnetSourceGeneratorWrapper
{
protected readonly ILogger logger;
protected readonly IDotNet dotnet;
private readonly string cscPath;
protected abstract string SourceGeneratorFolder { get; init; }
protected abstract string FileType { get; }
public DotnetSourceGeneratorWrapper(
Sdk sdk,
IDotNet dotnet,
ILogger logger)
{
this.logger = logger;
this.dotnet = dotnet;
if (sdk.CscPath is null)
{
throw new Exception($"Not running {FileType} source generator because CSC path is not available.");
}
this.cscPath = sdk.CscPath;
}
protected abstract void GenerateAnalyzerConfig(IEnumerable<string> additionalFiles, string csprojFile, string analyzerConfigPath);
public IEnumerable<string> RunSourceGenerator(IEnumerable<string> additionalFiles, string csprojFile, IEnumerable<string> references, string targetDir, string sourceDir)
{
try
{
var relativePathToCsProj = Path.GetRelativePath(sourceDir, csprojFile)
.Replace('\\', '/'); // Ensure we're generating the same hash regardless of the OS
var name = FileUtils.ComputeHash($"{relativePathToCsProj}\n{this.GetType().Name}");
using var tempDir = new TemporaryDirectory(Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), "source-generator"), "source generator temporary", logger);
var analyzerConfigPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.txt");
var dllPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.dll");
var cscArgsPath = Path.Combine(tempDir.DirInfo.FullName, $"{name}.rsp");
var outputFolder = Path.Combine(targetDir, name);
Directory.CreateDirectory(outputFolder);
logger.LogInfo("Producing analyzer config content.");
GenerateAnalyzerConfig(additionalFiles, csprojFile, analyzerConfigPath);
logger.LogDebug($"Analyzer config content: {File.ReadAllText(analyzerConfigPath)}");
var args = new StringBuilder();
args.Append($"/target:exe /generatedfilesout:\"{outputFolder}\" /out:\"{dllPath}\" /analyzerconfig:\"{analyzerConfigPath}\" ");
foreach (var f in Directory.GetFiles(SourceGeneratorFolder, "*.dll", new EnumerationOptions { RecurseSubdirectories = false, MatchCasing = MatchCasing.CaseInsensitive }))
{
args.Append($"/analyzer:\"{f}\" ");
}
foreach (var f in additionalFiles)
{
args.Append($"/additionalfile:\"{f}\" ");
}
foreach (var f in references)
{
args.Append($"/reference:\"{f}\" ");
}
var argsString = args.ToString();
logger.LogInfo($"Running CSC to generate source files from {FileType} files.");
logger.LogDebug($"Running CSC to generate source files from {FileType} files with arguments: {argsString}.");
using (var sw = new StreamWriter(cscArgsPath))
{
sw.Write(argsString);
}
dotnet.Exec($"\"{cscPath}\" /noconfig @\"{cscArgsPath}\"");
var files = Directory.GetFiles(outputFolder, "*.*", new EnumerationOptions { RecurseSubdirectories = true });
logger.LogInfo($"Generated {files.Length} source files from {FileType} files.");
return files;
}
catch (Exception ex)
{
logger.LogInfo($"Failed to generate source files from {FileType} files: {ex.Message}");
return [];
}
}
}
}