private static Assembly CompileToAssembly(WebPageRazorHost host, SyntaxTree syntaxTree, ICollection <ICompileModule> compilationModules) { var strArgs = new List <string>(); strArgs.Add("/target:library"); strArgs.Add(host.DefaultDebugCompilation ? "/o-" : "/o+"); strArgs.Add(host.DefaultDebugCompilation ? "/debug+" : "/debug-"); var cscArgs = CSharpCommandLineParser.Default.Parse(strArgs, null, null); var compilation = CSharpCompilation.Create( "RoslynRazor", // Note: using a fixed assembly name, which doesn't matter as long as we don't expect cross references of generated assemblies new[] { syntaxTree }, BuildManager.GetReferencedAssemblies().OfType <Assembly>().Select(ResolveReference), cscArgs.CompilationOptions.WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default)); compilation = Hacks.MakeValueTuplesWorkWhenRunningOn47RuntimeAndTargetingNet45Plus(compilation); var diagnostics = new List <Diagnostic>(); var context = new CompileContext(compilationModules); context.Before(new BeforeCompileContext { Arguments = cscArgs, Compilation = compilation, Diagnostics = diagnostics, }); compilation = context.BeforeCompileContext.Compilation; using (var dllStream = new MemoryStream()) using (var pdbStream = new MemoryStream()) { EmitResult emitResult = compilation.Emit(dllStream, pdbStream); diagnostics.AddRange(emitResult.Diagnostics); if (!emitResult.Success) { Diagnostic diagnostic = diagnostics.First(x => x.Severity == DiagnosticSeverity.Error); string message = diagnostic.ToString(); LinePosition linePosition = diagnostic.Location.GetMappedLineSpan().StartLinePosition; throw new HttpParseException(message, null, host.VirtualPath, null, linePosition.Line + 1); } context.After(new AfterCompileContext { Arguments = context.BeforeCompileContext.Arguments, AssemblyStream = dllStream, Compilation = compilation, Diagnostics = diagnostics, SymbolStream = pdbStream, XmlDocStream = null, }); return(Assembly.Load(dllStream.GetBuffer(), pdbStream.GetBuffer())); } }
private EmitResult Emit(CompileContext context) { var compilation = context.BeforeCompileContext.Compilation; var pdbPath = CscArgs.PdbPath; var outputPath = Path.Combine(CscArgs.OutputDirectory, CscArgs.OutputFileName); if (!CscArgs.EmitPdb) { pdbPath = null; } else if (string.IsNullOrWhiteSpace(pdbPath)) { pdbPath = Path.ChangeExtension(outputPath, ".pdb"); } using (var peStream = new MemoryStream()) using (var pdbStream = !string.IsNullOrWhiteSpace(pdbPath) ? new MemoryStream() : null) using (var xmlDocumentationStream = !string.IsNullOrWhiteSpace(CscArgs.DocumentationPath) ? new MemoryStream(): null) using (var win32Resources = CreateWin32Resource(compilation)) { // https://github.com/dotnet/roslyn/blob/41950e21da3ac2c307fb46c2ca8c8509b5059909/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs#L437 var emitResult = compilation.Emit( peStream: peStream, pdbStream: pdbStream, xmlDocumentationStream: xmlDocumentationStream, win32Resources: win32Resources, manifestResources: CscArgs.ManifestResources, options: CscArgs.EmitOptions, debugEntryPoint: null); Diagnostics.AddRange(emitResult.Diagnostics); context.After(new AfterCompileContext { Arguments = CscArgs, AssemblyStream = peStream, Compilation = compilation, Diagnostics = Diagnostics, SymbolStream = pdbStream, XmlDocStream = xmlDocumentationStream, }); // do not create the output files if emit fails // if the output files are there, msbuild incremental build thinks the previous build succeeded if (emitResult.Success) { Task.WaitAll( DumpToFileAsync(outputPath, peStream), DumpToFileAsync(pdbPath, pdbStream), DumpToFileAsync(CscArgs.DocumentationPath, xmlDocumentationStream)); } return(emitResult); } }
public async Task <bool> RunAsync(CancellationToken cancellationToken = default(CancellationToken)) { try { // this parameter was introduced in rc3, all call to it seem to be using RuntimeEnvironment.GetRuntimeDirectory() // https://github.com/dotnet/roslyn/blob/0382e3e3fc543fc483090bff3ab1eaae39dfb4d9/src/Compilers/CSharp/csc/Program.cs#L18 var sdkDirectory = RuntimeEnvironment.GetRuntimeDirectory(); CscArgs = CSharpCommandLineParser.Default.Parse(_precompilationCommandLineArgs.Arguments, _precompilationCommandLineArgs.BaseDirectory, sdkDirectory); Diagnostics = new List <Diagnostic>(CscArgs.Errors); // load those before anything else hooks into our AssemlbyResolve. var loader = new PrecompilationModuleLoader(PrecompilerSection.Current); loader.ModuleInitializationFailed += (module, ex) => { Diagnostics.Add(Diagnostic.Create( FailedToCreateModule, Location.Create(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, new TextSpan(), new LinePositionSpan()), module.Type, ex.Message)); }; var compilationModules = loader.LoadedModules; if (Diagnostics.Any()) { return(false); } Encoding = CscArgs.Encoding ?? new UTF8Encoding(false); // utf8 without bom var outputPath = Path.Combine(CscArgs.OutputDirectory, CscArgs.OutputFileName); var pdbPath = CscArgs.PdbPath ?? Path.ChangeExtension(outputPath, ".pdb"); using (var container = CreateCompositionHost()) using (var workspace = CreateWokspace(container)) using (var peStream = new MemoryStream()) using (var pdbStream = CscArgs.EmitPdb && CscArgs.EmitOptions.DebugInformationFormat != DebugInformationFormat.Embedded ? new MemoryStream() : null) using (var xmlDocumentationStream = !string.IsNullOrWhiteSpace(CscArgs.DocumentationPath) ? new MemoryStream() : null) { EmitResult emitResult = null; var documentExtenders = workspace.Services.FindLanguageServices <IDocumentExtender>(_ => true).ToList(); var project = CreateProject(workspace, documentExtenders); CSharpCompilation compilation = null; CompilationWithAnalyzers compilationWithAnalyzers = null; try { Diagnostics.AddRange((await Task.WhenAll(documentExtenders.Select(x => x.Complete()))).SelectMany(x => x)); compilation = await project.GetCompilationAsync(cancellationToken) as CSharpCompilation; } catch (Exception ex) { Diagnostics.Add(Diagnostic.Create(FailedToCreateCompilation, Location.None, ex)); return(false); } var analyzers = project.AnalyzerReferences.SelectMany(x => x.GetAnalyzers(project.Language)).ToImmutableArray(); if (!analyzers.IsEmpty) { compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, cancellationToken); compilation = compilationWithAnalyzers.Compilation as CSharpCompilation; } var context = new CompileContext(compilationModules); context.Before(new BeforeCompileContext { Arguments = CscArgs, Compilation = compilation.AddSyntaxTrees(GeneratedSyntaxTrees()), Diagnostics = Diagnostics, }); CscArgs = context.BeforeCompileContext.Arguments; compilation = context.BeforeCompileContext.Compilation; var analysisTask = compilationWithAnalyzers?.GetAnalysisResultAsync(cancellationToken); using (var win32Resources = CreateWin32Resource(compilation)) { // PathMapping is also required here, to actually get the symbols to line up: // https://github.com/dotnet/roslyn/blob/9d081e899b35294b8f1793d31abe5e2c43698844/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs#L616 // PathUtilities.NormalizePathPrefix is internal, but callable via the SourceFileResolver, that we set in CreateProject var emitOptions = CscArgs.EmitOptions .WithPdbFilePath(compilation.Options.SourceReferenceResolver.NormalizePath(pdbPath, CscArgs.BaseDirectory)); // https://github.com/dotnet/roslyn/blob/41950e21da3ac2c307fb46c2ca8c8509b5059909/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs#L437 emitResult = compilation.Emit( peStream: peStream, pdbStream: pdbStream, xmlDocumentationStream: xmlDocumentationStream, win32Resources: win32Resources, manifestResources: CscArgs.ManifestResources, options: emitOptions, sourceLinkStream: TryOpenFile(CscArgs.SourceLink, out var sourceLinkStream) ? sourceLinkStream : null, embeddedTexts: CscArgs.EmbeddedFiles.AsEnumerable() .Select(x => TryOpenFile(x.Path, out var embeddedText) ? EmbeddedText.FromStream(x.Path, embeddedText) : null) .Where(x => x != null), debugEntryPoint: null, cancellationToken: cancellationToken); } Diagnostics.AddRange(emitResult.Diagnostics); try { var analysisResult = analysisTask == null ? null : await analysisTask; if (analysisResult != null) { Diagnostics.AddRange(analysisResult.GetAllDiagnostics()); foreach (var info in analysisResult.AnalyzerTelemetryInfo) { Console.WriteLine($"hidden: {info.Key} {info.Value.ExecutionTime.TotalMilliseconds:#}ms"); } } } catch (OperationCanceledException) { Console.WriteLine("warning: analysis canceled"); } catch (Exception ex) { Diagnostics.Add(Diagnostic.Create(AnalysisFailed, Location.None, ex)); return(false); } if (!emitResult.Success || HasErrors) { return(false); } context.After(new AfterCompileContext { Arguments = CscArgs, AssemblyStream = peStream, Compilation = compilation, Diagnostics = Diagnostics, SymbolStream = pdbStream, XmlDocStream = xmlDocumentationStream, }); if (!HasErrors) { // do not create the output files if emit fails // if the output files are there, msbuild incremental build thinks the previous build succeeded await Task.WhenAll( DumpToFileAsync(outputPath, peStream, cancellationToken), DumpToFileAsync(pdbPath, pdbStream, cancellationToken), DumpToFileAsync(CscArgs.DocumentationPath, xmlDocumentationStream, cancellationToken)); return(true); } return(false); } } catch (PrecompilationModuleException pmex) { Diagnostics.Add(Diagnostic.Create(PrecompilationModuleFailed, Location.None, pmex.Message, pmex.InnerException)); return(false); } catch (Exception ex) { Diagnostics.Add(Diagnostic.Create(UnhandledException, Location.None, ex)); return(false); } finally { // strings only, since the Console.Out textwriter is another app domain... // https://stackoverflow.com/questions/2459994/is-there-a-way-to-print-a-new-line-when-using-message for (var i = 0; i < Diagnostics.Count; i++) { var d = Diagnostics[i]; if (!d.IsSuppressed && d.Severity != DiagnosticSeverity.Hidden) { Console.WriteLine(d.ToString().Replace("\r", "").Replace("\n", "\\n")); } } } }
public async Task <bool> RunAsync() { try { // this parameter was introduced in rc3, all call to it seem to be using RuntimeEnvironment.GetRuntimeDirectory() // https://github.com/dotnet/roslyn/blob/0382e3e3fc543fc483090bff3ab1eaae39dfb4d9/src/Compilers/CSharp/csc/Program.cs#L18 var sdkDirectory = RuntimeEnvironment.GetRuntimeDirectory(); CscArgs = CSharpCommandLineParser.Default.Parse(_precompilationCommandLineArgs.Arguments, _precompilationCommandLineArgs.BaseDirectory, sdkDirectory); Diagnostics = new List <Diagnostic>(CscArgs.Errors); // load those before anything else hooks into our AssemlbyResolve... var compilationModules = LoadModules().ToList(); if (Diagnostics.Any()) { return(false); } Encoding = CscArgs.Encoding ?? new UTF8Encoding(false); // utf8 without bom var pdbPath = CscArgs.PdbPath; var outputPath = Path.Combine(CscArgs.OutputDirectory, CscArgs.OutputFileName); if (!CscArgs.EmitPdb) { pdbPath = null; } else if (string.IsNullOrWhiteSpace(pdbPath)) { pdbPath = Path.ChangeExtension(outputPath, ".pdb"); } using (var workspace = CreateWokspace()) using (var analysisCts = new CancellationTokenSource()) using (var peStream = new MemoryStream()) using (var pdbStream = !string.IsNullOrWhiteSpace(pdbPath) ? new MemoryStream() : null) using (var xmlDocumentationStream = !string.IsNullOrWhiteSpace(CscArgs.DocumentationPath) ? new MemoryStream() : null) { EmitResult emitResult = null; var project = CreateProject(workspace); CSharpCompilation compilation = null; CompilationWithAnalyzers compilationWithAnalyzers = null; try { compilation = await project.GetCompilationAsync() as CSharpCompilation; } catch (Exception ex) { Console.WriteLine("error: failed to create compilation: " + ex.Message); return(false); } var context = new CompileContext(compilationModules); context.Before(new BeforeCompileContext { Arguments = CscArgs, Compilation = compilation.AddSyntaxTrees(GeneratedSyntaxTrees()), Diagnostics = Diagnostics, }); CscArgs = context.BeforeCompileContext.Arguments; compilation = context.BeforeCompileContext.Compilation; var analyzers = project.AnalyzerReferences.SelectMany(x => x.GetAnalyzers(project.Language)).ToImmutableArray(); if (!analyzers.IsEmpty) { compilationWithAnalyzers = compilation.WithAnalyzers(analyzers, project.AnalyzerOptions, analysisCts.Token); compilation = compilationWithAnalyzers.Compilation as CSharpCompilation; } var analysisTask = compilationWithAnalyzers?.GetAnalysisResultAsync(analysisCts.Token); using (var win32Resources = CreateWin32Resource(compilation)) { // https://github.com/dotnet/roslyn/blob/41950e21da3ac2c307fb46c2ca8c8509b5059909/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs#L437 emitResult = compilation.Emit( peStream: peStream, pdbStream: pdbStream, xmlDocumentationStream: xmlDocumentationStream, win32Resources: win32Resources, manifestResources: CscArgs.ManifestResources, options: CscArgs.EmitOptions, sourceLinkStream: TryOpenFile(CscArgs.SourceLink, out var sourceLinkStream) ? sourceLinkStream : null, embeddedTexts: CscArgs.EmbeddedFiles.AsEnumerable() .Select(x => TryOpenFile(x.Path, out var embeddedText) ? EmbeddedText.FromStream(x.Path, embeddedText) : null) .Where(x => x != null), debugEntryPoint: null); } Diagnostics.AddRange(emitResult.Diagnostics); try { var analysisResult = analysisTask == null ? null : await analysisTask; if (analysisResult != null) { Diagnostics.AddRange(analysisResult.GetAllDiagnostics()); foreach (var info in analysisResult.AnalyzerTelemetryInfo) { Console.WriteLine($"hidden: {info.Key} {info.Value.ExecutionTime.TotalMilliseconds:#}ms"); } } } catch (OperationCanceledException) { Console.WriteLine("warning: analysis canceled"); } catch (Exception ex) { Console.WriteLine("error: analysis failed: " + ex.Message); return(false); } if (!emitResult.Success || HasErrors) { return(false); } context.After(new AfterCompileContext { Arguments = CscArgs, AssemblyStream = peStream, Compilation = compilation, Diagnostics = Diagnostics, SymbolStream = pdbStream, XmlDocStream = xmlDocumentationStream, }); if (!HasErrors) { // do not create the output files if emit fails // if the output files are there, msbuild incremental build thinks the previous build succeeded await Task.WhenAll( DumpToFileAsync(outputPath, peStream), DumpToFileAsync(pdbPath, pdbStream), DumpToFileAsync(CscArgs.DocumentationPath, xmlDocumentationStream)); return(true); } return(false); } } catch (Exception ex) { Console.WriteLine(string.Join("\n", ex.ToString().Split('\n').Select((x, i) => x + $"ERROR: {i:D4} {x}"))); return(false); } finally { Diagnostics.ForEach(x => Console.WriteLine(x.ToString())); // strings only, since the Console.Out textwriter is another app domain... } }