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 bool Run()
        {
            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);
                if (Diagnostics.Any())
                {
                    return(false);
                }
                Encoding = CscArgs.Encoding ?? new UTF8Encoding(false); // utf8 without bom

                var compilationOptions = CscArgs.CompilationOptions.WithAssemblyIdentityComparer(GetAssemblyIdentityComparer());
                if (!string.IsNullOrEmpty(CscArgs.CompilationOptions.CryptoKeyFile))
                {
                    var cryptoKeyFilePath = Path.Combine(CscArgs.BaseDirectory, CscArgs.CompilationOptions.CryptoKeyFile);
                    compilationOptions = compilationOptions.WithStrongNameProvider(new DesktopStrongNameProvider(ImmutableArray.Create(cryptoKeyFilePath)));
                }

                var references = SetupReferences();
                var sources    = LoadSources(CscArgs.SourceFiles);

                var compilationModules = LoadModules().ToList();

                var compilation = CSharpCompilation.Create(
                    options: compilationOptions,
                    references: references,
                    syntaxTrees: sources,
                    assemblyName: CscArgs.CompilationName);

                var context = new CompileContext(compilationModules);

                context.Before(new BeforeCompileContext
                {
                    Arguments   = CscArgs,
                    Compilation = compilation,
                    Diagnostics = Diagnostics,
                    Resources   = CscArgs.ManifestResources.ToList()
                });

                var emitResult = Emit(context);
                return(emitResult.Success);
            }
            finally
            {
                Diagnostics.ForEach(x => Console.WriteLine(x.ToString())); // strings only, since the Console.Out textwriter is another app domain...
            }
        }
Example #4
0
        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"));
                    }
                }
            }
        }
Example #5
0
        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...
            }
        }