-
Notifications
You must be signed in to change notification settings - Fork 2
/
PipeLoggerServer.cs
128 lines (112 loc) · 3.98 KB
/
PipeLoggerServer.cs
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
namespace MsBuildPipeLogger
{
/// <summary>
/// Receives MSBuild logging events over a pipe. This is the base class for <see cref="AnonymousPipeLoggerServer"/>
/// and <see cref="NamedPipeLoggerServer"/>.
/// </summary>
public abstract class PipeLoggerServer<TPipeStream> : EventArgsDispatcher, IPipeLoggerServer
where TPipeStream : PipeStream
{
private readonly BinaryReader _binaryReader;
private readonly BuildEventArgsReaderProxy _buildEventArgsReader;
internal PipeBuffer Buffer { get; } = new PipeBuffer();
protected TPipeStream PipeStream { get; }
protected CancellationToken CancellationToken { get; }
/// <summary>
/// Creates a server that receives MSBuild events over a specified pipe.
/// </summary>
/// <param name="pipeStream">The pipe to receive events from.</param>
protected PipeLoggerServer(TPipeStream pipeStream)
: this(pipeStream, CancellationToken.None)
{
}
/// <summary>
/// Creates a server that receives MSBuild events over a specified pipe.
/// </summary>
/// <param name="pipeStream">The pipe to receive events from.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that will cancel read operations if triggered.</param>
protected PipeLoggerServer(TPipeStream pipeStream, CancellationToken cancellationToken)
{
PipeStream = pipeStream;
_binaryReader = new BinaryReader(Buffer);
_buildEventArgsReader = new BuildEventArgsReaderProxy(_binaryReader);
CancellationToken = cancellationToken;
Thread readerThread = new Thread(() =>
{
try
{
Connect();
while (Buffer.FillFromStream(PipeStream, CancellationToken))
{
}
}
catch (IOException)
{
// The client broke the stream so we're done
}
catch (ObjectDisposedException)
{
// The pipe was disposed
}
// Add a final 0 (BinaryLogRecordKind.EndOfFile) into the stream in case the BuildEventArgsReader is waiting for a read
Buffer.Write(new byte[1] { 0 }, 0, 1);
Buffer.CompleteAdding();
})
{
IsBackground = true
};
readerThread.Start();
}
protected abstract void Connect();
/// <inheritdoc/>
public BuildEventArgs Read()
{
if (Buffer.IsCompleted)
{
return null;
}
try
{
BuildEventArgs args = _buildEventArgsReader.Read();
if (args != null)
{
Dispatch(args);
return args;
}
}
catch (EndOfStreamException)
{
// The stream may have been closed or otherwise stopped
}
return null;
}
/// <inheritdoc/>
public void ReadAll()
{
BuildEventArgs args = Read();
while (args != null)
{
if (args is BuildFinishedEventArgs)
{
return;
}
args = Read();
}
}
/// <inheritdoc/>
public void Dispose()
{
_binaryReader.Dispose();
Buffer.Dispose();
PipeStream.Dispose();
}
}
}