11using System ;
22using System . Collections . Generic ;
3+ using System . Diagnostics ;
34using System . IO ;
45using Microsoft . CodeAnalysis ;
56using Microsoft . CodeAnalysis . CSharp ;
67using System . Linq ;
8+ using System . Reflection ;
79using System . Text ;
810using Mono . CompilerServices . SymbolWriter ;
911using 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}
0 commit comments