Skip to content
This repository was archived by the owner on Dec 12, 2020. It is now read-only.

Commit 4bd01ce

Browse files
authored
Merge pull request #187 from manne/feature/cancellation
Make the generator cancellable
2 parents 338b32e + fea5d44 commit 4bd01ce

4 files changed

Lines changed: 38 additions & 13 deletions

File tree

src/CodeGeneration.Roslyn.Engine/CompilationGenerator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ public async Task GenerateAsync(IProgress<Diagnostic> progress = null, Cancellat
123123
inputSyntaxTree,
124124
this.ProjectDirectory,
125125
this.LoadPlugin,
126-
progress);
127-
128-
var outputText = generatedSyntaxTree.GetText(cancellationToken);
126+
progress,
127+
cancellationToken);
128+
var outputText = await generatedSyntaxTree.GetTextAsync(cancellationToken);
129129
using (var outputFileStream = File.OpenWrite(outputFilePath))
130130
using (var outputWriter = new StreamWriter(outputFileStream))
131131
{
132-
outputText.Write(outputWriter);
132+
outputText.Write(outputWriter, cancellationToken);
133133

134134
// Truncate any data that may be beyond this point if the file existed previously.
135135
outputWriter.Flush();
@@ -150,9 +150,9 @@ public async Task GenerateAsync(IProgress<Diagnostic> progress = null, Cancellat
150150
catch (IOException ex) when (ex.HResult == ProcessCannotAccessFileHR && retriesLeft > 0)
151151
{
152152
retriesLeft--;
153-
Task.Delay(200).Wait();
153+
await Task.Delay(200, cancellationToken);
154154
}
155-
catch (Exception ex)
155+
catch (Exception ex) when (!(ex is OperationCanceledException))
156156
{
157157
ReportError(progress, "CGR001", inputSyntaxTree, ex);
158158
fileFailures.Add(ex);

src/CodeGeneration.Roslyn.Engine/DocumentTransform.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ public static class DocumentTransform
4242
/// <param name="projectDirectory">The path of the <c>.csproj</c> project file.</param>
4343
/// <param name="assemblyLoader">A function that can load an assembly with the given name.</param>
4444
/// <param name="progress">Reports warnings and errors in code generation.</param>
45+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
4546
/// <returns>A task whose result is the generated document.</returns>
4647
public static async Task<SyntaxTree> TransformAsync(
4748
CSharpCompilation compilation,
4849
SyntaxTree inputDocument,
4950
string projectDirectory,
5051
Func<AssemblyName, Assembly> assemblyLoader,
51-
IProgress<Diagnostic> progress)
52+
IProgress<Diagnostic> progress,
53+
CancellationToken cancellationToken)
5254
{
5355
Requires.NotNull(compilation, nameof(compilation));
5456
Requires.NotNull(inputDocument, nameof(inputDocument));
@@ -70,17 +72,19 @@ public static async Task<SyntaxTree> TransformAsync(
7072
var emittedAttributeLists = ImmutableArray<AttributeListSyntax>.Empty;
7173
var emittedMembers = ImmutableArray<MemberDeclarationSyntax>.Empty;
7274

73-
var root = await inputDocument.GetRootAsync();
75+
var root = await inputDocument.GetRootAsync(cancellationToken);
7476
var memberNodes = root
7577
.DescendantNodesAndSelf(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax)
7678
.OfType<CSharpSyntaxNode>();
7779

7880
foreach (var memberNode in memberNodes)
7981
{
82+
cancellationToken.ThrowIfCancellationRequested();
8083
var attributeData = GetAttributeData(compilation, inputSemanticModel, memberNode);
8184
var generators = FindCodeGenerators(attributeData, assemblyLoader);
8285
foreach (var generator in generators)
8386
{
87+
cancellationToken.ThrowIfCancellationRequested();
8488
var context = new TransformationContext(
8589
memberNode,
8690
inputSemanticModel,
@@ -91,7 +95,7 @@ public static async Task<SyntaxTree> TransformAsync(
9195

9296
var richGenerator = generator as IRichCodeGenerator ?? new EnrichingCodeGeneratorProxy(generator);
9397

94-
var emitted = await richGenerator.GenerateRichAsync(context, progress, CancellationToken.None);
98+
var emitted = await richGenerator.GenerateRichAsync(context, progress, cancellationToken);
9599

96100
emittedExterns = emittedExterns.AddRange(emitted.Externs);
97101
emittedUsings = emittedUsings.AddRange(emitted.Usings);
@@ -244,7 +248,7 @@ public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(
244248

245249
public async Task<RichGenerationResult> GenerateRichAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
246250
{
247-
var generatedMembers = await CodeGenerator.GenerateAsync(context, progress, CancellationToken.None);
251+
var generatedMembers = await CodeGenerator.GenerateAsync(context, progress, cancellationToken);
248252

249253
// Figure out ancestry for the generated type, including nesting types and namespaces.
250254
var wrappedMembers = context.ProcessingNode.Ancestors().Aggregate(generatedMembers, WrapInAncestor);

src/CodeGeneration.Roslyn.Tests/Helpers/CompilationTestsBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Linq;
88
using System.Reflection;
9+
using System.Threading;
910
using System.Threading.Tasks;
1011
using CodeGeneration.Roslyn;
1112
using CodeGeneration.Roslyn.Engine;
@@ -78,7 +79,7 @@ protected static async Task<SyntaxTree> GenerateAsync(string source)
7879
var diagnostics = compilation.GetDiagnostics();
7980
Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning));
8081
var progress = new Progress<Diagnostic>();
81-
var result = await DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, progress);
82+
var result = await DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, progress, CancellationToken.None);
8283
return result;
8384
}
8485

src/CodeGeneration.Roslyn.Tool/Program.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Andrew Arnott. All rights reserved.
22
// Licensed under the MS-PL license. See LICENSE.txt file in the project root for full license information.
33

4+
using System.Threading;
5+
46
namespace CodeGeneration.Roslyn.Generate
57
{
68
using System;
@@ -14,6 +16,24 @@ namespace CodeGeneration.Roslyn.Generate
1416
internal static class Program
1517
{
1618
private static async Task<int> Main(string[] args)
19+
{
20+
using (var cancellationTokenSource = new CancellationTokenSource())
21+
{
22+
var cancellationToken = cancellationTokenSource.Token;
23+
Console.CancelKeyPress += ConsoleOnCancelKeyPress;
24+
25+
void ConsoleOnCancelKeyPress(object sender, ConsoleCancelEventArgs e)
26+
{
27+
Console.CancelKeyPress -= ConsoleOnCancelKeyPress;
28+
cancellationTokenSource.Cancel();
29+
e.Cancel = true;
30+
}
31+
32+
return await Core(args, cancellationToken);
33+
}
34+
}
35+
36+
private static async Task<int> Core(string[] args, CancellationToken cancellationToken)
1737
{
1838
IReadOnlyList<string> compile = Array.Empty<string>();
1939
IReadOnlyList<string> refs = Array.Empty<string>();
@@ -66,7 +86,7 @@ private static async Task<int> Main(string[] args)
6686

6787
try
6888
{
69-
await generator.GenerateAsync(progress);
89+
await generator.GenerateAsync(progress, cancellationToken);
7090
}
7191
catch (Exception e)
7292
{
@@ -77,7 +97,7 @@ private static async Task<int> Main(string[] args)
7797

7898
if (generatedCompileItemFile != null)
7999
{
80-
File.WriteAllLines(generatedCompileItemFile, generator.GeneratedFiles);
100+
await File.WriteAllLinesAsync(generatedCompileItemFile, generator.GeneratedFiles, cancellationToken);
81101
}
82102

83103
foreach (var file in generator.GeneratedFiles)

0 commit comments

Comments
 (0)