Skip to content

Commit 709bf3a

Browse files
committed
Reuse prebuilt results if possible
1 parent 083d551 commit 709bf3a

File tree

2 files changed

+123
-20
lines changed

2 files changed

+123
-20
lines changed

core/IncrementalCompiler/Compiler.cs

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.IO;
45
using Microsoft.CodeAnalysis;
56
using Microsoft.CodeAnalysis.CSharp;
67
using System.Linq;
8+
using System.Reflection;
79
using System.Text;
810
using Mono.CompilerServices.SymbolWriter;
911
using NLog;
@@ -19,6 +21,8 @@ public class Compiler
1921
private FileTimeList _sourceFileList;
2022
private Dictionary<string, MetadataReference> _referenceMap;
2123
private Dictionary<string, SyntaxTree> _sourceMap;
24+
private MemoryStream _outputDllStream;
25+
private MemoryStream _outputDebugSymbolStream;
2226

2327
public CompileResult Build(CompileOptions options)
2428
{
@@ -127,7 +131,59 @@ private CompileResult BuildIncremental(CompileOptions options)
127131
_sourceMap.Remove(file);
128132
}
129133

130-
Emit(result);
134+
// emit or reuse prebuilt output
135+
136+
var reusePrebuilt = _outputDllStream != null && (
137+
(_options.PrebuiltOutputReuse == PrebuiltOutputReuseType.WhenNoChange &&
138+
sourceChanges.Empty && referenceChanges.Empty) ||
139+
(_options.PrebuiltOutputReuse == PrebuiltOutputReuseType.WhenNoSourceChange &&
140+
sourceChanges.Empty && referenceChanges.Added.Count == 0 && referenceChanges.Removed.Count == 0));
141+
142+
if (reusePrebuilt)
143+
{
144+
_logger.Info("Reuse prebuilt output");
145+
146+
// write dll
147+
148+
var dllFile = Path.Combine(_options.WorkDirectory, _options.Output);
149+
using (var dllStream = new FileStream(dllFile, FileMode.Create))
150+
{
151+
_outputDllStream.Seek(0L, SeekOrigin.Begin);
152+
_outputDllStream.CopyTo(dllStream);
153+
}
154+
155+
// write pdb or mdb
156+
157+
switch (_options.DebugSymbolFile)
158+
{
159+
case DebugSymbolFileType.Pdb:
160+
var pdbFile = Path.Combine(_options.WorkDirectory, Path.ChangeExtension(_options.Output, ".pdb"));
161+
using (var debugSymbolStream = new FileStream(pdbFile, FileMode.Create))
162+
{
163+
_outputDebugSymbolStream.Seek(0L, SeekOrigin.Begin);
164+
_outputDebugSymbolStream.CopyTo(debugSymbolStream);
165+
}
166+
break;
167+
168+
case DebugSymbolFileType.PdbToMdb:
169+
case DebugSymbolFileType.Mdb:
170+
var mdbFile = Path.Combine(_options.WorkDirectory, _options.Output + ".mdb");
171+
using (var debugSymbolStream = new FileStream(mdbFile, FileMode.Create))
172+
{
173+
_outputDebugSymbolStream.Seek(0L, SeekOrigin.Begin);
174+
_outputDebugSymbolStream.CopyTo(debugSymbolStream);
175+
}
176+
break;
177+
}
178+
179+
result.Succeeded = true;
180+
}
181+
else
182+
{
183+
_logger.Info("Emit");
184+
185+
Emit(result);
186+
}
131187

132188
return result;
133189
}
@@ -145,30 +201,75 @@ private SyntaxTree ParseSource(string file, CSharpParseOptions parseOption)
145201

146202
private void Emit(CompileResult result)
147203
{
148-
var outputFile = Path.Combine(_options.WorkDirectory, _options.Output);
149-
var debugFile = Path.Combine(_options.WorkDirectory, _options.Output + ".mdb");
150-
using (var peStream = new FileStream(outputFile, FileMode.Create))
151-
using (var pdbStream = new FileStream(debugFile, FileMode.Create))
204+
_outputDllStream = new MemoryStream();
205+
_outputDebugSymbolStream = _options.DebugSymbolFile != DebugSymbolFileType.None ? new MemoryStream() : null;
206+
207+
// emit to memory
208+
209+
var r = _options.DebugSymbolFile == DebugSymbolFileType.Mdb ? _compilation.EmitWithMdb(_outputDllStream, _outputDebugSymbolStream) : _compilation.Emit(_outputDllStream, _outputDebugSymbolStream);
210+
211+
// memory to file
212+
213+
var dllFile = Path.Combine(_options.WorkDirectory, _options.Output);
214+
var mdbFile = Path.Combine(_options.WorkDirectory, _options.Output + ".mdb");
215+
var pdbFile = Path.Combine(_options.WorkDirectory, Path.ChangeExtension(_options.Output, ".pdb"));
216+
217+
var emitDebugSymbolFile = _options.DebugSymbolFile == DebugSymbolFileType.Mdb ? mdbFile : pdbFile;
218+
219+
using (var dllStream = new FileStream(dllFile, FileMode.Create))
152220
{
153-
var r = _compilation.EmitWithMdb(peStream, pdbStream);
221+
_outputDllStream.Seek(0L, SeekOrigin.Begin);
222+
_outputDllStream.CopyTo(dllStream);
223+
}
154224

155-
foreach (var d in r.Diagnostics)
225+
if (_outputDebugSymbolStream != null)
226+
{
227+
using (var debugSymbolStream = new FileStream(emitDebugSymbolFile, FileMode.Create))
156228
{
157-
if (d.Severity == DiagnosticSeverity.Warning && d.IsWarningAsError == false)
158-
result.Warnings.Add(GetDiagnosticString(d, "warning"));
159-
else if (d.Severity == DiagnosticSeverity.Error || d.IsWarningAsError)
160-
result.Errors.Add(GetDiagnosticString(d, "error"));
229+
_outputDebugSymbolStream.Seek(0L, SeekOrigin.Begin);
230+
_outputDebugSymbolStream.CopyTo(debugSymbolStream);
161231
}
232+
}
162233

163-
result.Succeeded = r.Success;
234+
// gather result
235+
236+
foreach (var d in r.Diagnostics)
237+
{
238+
if (d.Severity == DiagnosticSeverity.Warning && d.IsWarningAsError == false)
239+
result.Warnings.Add(GetDiagnosticString(d, "warning"));
240+
else if (d.Severity == DiagnosticSeverity.Error || d.IsWarningAsError)
241+
result.Errors.Add(GetDiagnosticString(d, "error"));
242+
}
243+
244+
result.Succeeded = r.Success;
245+
246+
// pdb to mdb when required
247+
248+
if (_options.DebugSymbolFile == DebugSymbolFileType.PdbToMdb)
249+
{
250+
var code = ConvertPdb2Mdb(dllFile);
251+
_logger.Info("pdb2mdb exited with {0}", code);
252+
File.Delete(pdbFile);
253+
254+
// read converted mdb file to cache contents
255+
_outputDebugSymbolStream = new MemoryStream(File.ReadAllBytes(mdbFile));
164256
}
165257
}
166258

167259
private static string GetDiagnosticString(Diagnostic diagnostic, string type)
168260
{
169261
var line = diagnostic.Location.GetLineSpan();
170-
return $"{line.Path}({line.StartLinePosition.Line + 1},{line.StartLinePosition.Character + 1}): " +
171-
$"{type} {diagnostic.Id}: {diagnostic.GetMessage()}";
262+
return $"{line.Path}({line.StartLinePosition.Line + 1},{line.StartLinePosition.Character + 1}): " + $"{type} {diagnostic.Id}: {diagnostic.GetMessage()}";
263+
}
264+
265+
public static int ConvertPdb2Mdb(string dllFile)
266+
{
267+
var toolPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "pdb2mdb.exe");
268+
var process = new Process();
269+
process.StartInfo = new ProcessStartInfo(toolPath, '"' + dllFile + '"');
270+
process.Start();
271+
process.WaitForExit();
272+
return process.ExitCode;
172273
}
173274
}
174275
}

core/IncrementalCompiler/FileTimeList.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ public class FileTimeList
1111

1212
public class Result
1313
{
14-
public IEnumerable<string> Added;
15-
public IEnumerable<string> Changed;
16-
public IEnumerable<string> Removed;
14+
public List<string> Added;
15+
public List<string> Changed;
16+
public List<string> Removed;
17+
18+
public bool Empty => Added.Count == 0 && Changed.Count == 0 && Removed.Count == 0;
1719
}
1820

1921
public Result Update(IEnumerable<string> files)
@@ -30,9 +32,9 @@ public Result Update(IEnumerable<Tuple<string, DateTime>> files)
3032
{
3133
return new Result
3234
{
33-
Added = Enumerable.Empty<string>(),
34-
Changed = Enumerable.Empty<string>(),
35-
Removed = Enumerable.Empty<string>(),
35+
Added = new List<string>(),
36+
Changed = new List<string>(),
37+
Removed = new List<string>(),
3638
};
3739
}
3840

0 commit comments

Comments
 (0)