diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/DefaultRoslynCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/DefaultRoslynCompilationService.cs new file mode 100644 index 0000000000..8d1b5fca76 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/DefaultRoslynCompilationService.cs @@ -0,0 +1,118 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.Dnx.Compilation.CSharp; +using Microsoft.Extensions.CompilationAbstractions; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.PlatformAbstractions; + +namespace Microsoft.AspNet.Mvc.Razor.Compilation +{ + /// + /// A type that uses Roslyn to compile C# content and to find out references. + /// + public class DefaultRoslynCompilationService : RoslynCompilationService + { + private readonly IApplicationEnvironment _environment; + private readonly ILibraryExporter _libraryExporter; + + /// + /// Initalizes a new instance of the class. + /// + /// The environment for the executing application. + /// The library manager that provides export and reference information. + /// The that was used to generate the code. + /// Accessor to . + /// The . + public DefaultRoslynCompilationService(IApplicationEnvironment environment, + ILibraryExporter libraryExporter, + IMvcRazorHost host, + IOptions optionsAccessor, + IRazorViewEngineFileProviderAccessor fileProviderAccessor) + : base(environment, host, optionsAccessor, fileProviderAccessor) + { + _environment = environment; + _libraryExporter = libraryExporter; + } + + protected override List GetApplicationReferences() + { + var references = new List(); + + // Get the MetadataReference for the executing application. If it's a Roslyn reference, + // we can copy the references created when compiling the application to the Razor page being compiled. + // This avoids performing expensive calls to MetadataReference.CreateFromImage. + var libraryExport = _libraryExporter.GetExport(_environment.ApplicationName); + if (libraryExport?.MetadataReferences != null && libraryExport.MetadataReferences.Count > 0) + { + Debug.Assert(libraryExport.MetadataReferences.Count == 1, + "Expected 1 MetadataReferences, found " + libraryExport.MetadataReferences.Count); + var roslynReference = libraryExport.MetadataReferences[0] as IRoslynMetadataReference; + var compilationReference = roslynReference?.MetadataReference as CompilationReference; + if (compilationReference != null) + { + references.AddRange(compilationReference.Compilation.References); + references.Add(roslynReference.MetadataReference); + return references; + } + } + + var export = _libraryExporter.GetAllExports(_environment.ApplicationName); + if (export != null) + { + foreach (var metadataReference in export.MetadataReferences) + { + // Taken from https://github.com/aspnet/KRuntime/blob/757ba9bfdf80bd6277e715d6375969a7f44370ee/src/... + // Microsoft.Extensions.Runtime.Roslyn/RoslynCompiler.cs#L164 + // We don't want to take a dependency on the Roslyn bit directly since it pulls in more dependencies + // than the view engine needs (Microsoft.Extensions.Runtime) for example + references.Add(ConvertMetadataReference(metadataReference)); + } + } + + return references; + } + + private MetadataReference ConvertMetadataReference(IMetadataReference metadataReference) + { + var roslynReference = metadataReference as IRoslynMetadataReference; + + if (roslynReference != null) + { + return roslynReference.MetadataReference; + } + + var embeddedReference = metadataReference as IMetadataEmbeddedReference; + + if (embeddedReference != null) + { + return MetadataReference.CreateFromImage(embeddedReference.Contents); + } + + var fileMetadataReference = metadataReference as IMetadataFileReference; + + if (fileMetadataReference != null) + { + return CreateMetadataFileReference(fileMetadataReference.Path); + } + + var projectReference = metadataReference as IMetadataProjectReference; + if (projectReference != null) + { + using (var ms = new MemoryStream()) + { + projectReference.EmitReferenceAssembly(ms); + + return MetadataReference.CreateFromImage(ms.ToArray()); + } + } + + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/DependencyContextCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/DependencyContextCompilationService.cs new file mode 100644 index 0000000000..7522fe0852 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/DependencyContextCompilationService.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.CompilationAbstractions; +using Microsoft.Extensions.DependencyModel; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.PlatformAbstractions; + +namespace Microsoft.AspNet.Mvc.Razor.Compilation +{ + /// + /// A type that uses Roslyn to compile C# content and to find out references. + /// + public class DependencyContextCompilationService : RoslynCompilationService + { + private DependencyContext _dependencyContext; + + /// + /// Initalizes a new instance of the class. + /// + /// The environment for the executing application. + /// The that was used to generate the code. + /// Accessor to . + /// The . + public DependencyContextCompilationService(IApplicationEnvironment environment, + IMvcRazorHost host, + IOptions optionsAccessor, + IRazorViewEngineFileProviderAccessor fileProviderAccessor) + : this(DependencyContext.Default, environment, host, optionsAccessor, fileProviderAccessor) + { + } + + /// + /// Initalizes a new instance of the class. + /// + /// to use for reference resolution. + /// The environment for the executing application. + /// The that was used to generate the code. + /// Accessor to . + /// The . + public DependencyContextCompilationService(DependencyContext dependencyContext, + IApplicationEnvironment environment, + IMvcRazorHost host, + IOptions optionsAccessor, + IRazorViewEngineFileProviderAccessor fileProviderAccessor) + : base(environment, host, optionsAccessor, fileProviderAccessor) + { + _dependencyContext = dependencyContext; + } + + protected override List GetApplicationReferences() + { + return _dependencyContext.CompileLibraries + .SelectMany(library => library.ResolveReferencePaths()) + .Select(CreateMetadataFileReference) + .ToList(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/DependencyContextRazorViewEngineOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Razor/DependencyContextRazorViewEngineOptionsSetup.cs new file mode 100644 index 0000000000..d87b34d817 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/DependencyContextRazorViewEngineOptionsSetup.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Mvc.Razor; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyModel; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// Sets up compilation and parse option default options for using + /// + public class DependencyContextRazorViewEngineOptionsSetup : ConfigureOptions + { + /// + /// Initializes a new instance of . + /// + public DependencyContextRazorViewEngineOptionsSetup() : this(DependencyContext.Default) + { + } + + /// + /// Initializes a new instance of . + /// + /// to use as compilation and parse option source. + public DependencyContextRazorViewEngineOptionsSetup(DependencyContext dependencyContext) : base(options => ConfigureRazor(options, dependencyContext)) + { + } + + private static void ConfigureRazor(RazorViewEngineOptions options, DependencyContext dependencyContext) + { + var compilationOptions = dependencyContext.CompilationOptions; + + SetParseOptions(options, compilationOptions); + SetCompilationOptions(options, compilationOptions); + } + + private static void SetCompilationOptions(RazorViewEngineOptions options, Microsoft.Extensions.DependencyModel.CompilationOptions compilationOptions) + { + var roslynOptions = options.CompilationOptions; + + // Disable 1702 until roslyn turns this off by default + roslynOptions = roslynOptions.WithSpecificDiagnosticOptions( + new Dictionary + { + {"CS1701", ReportDiagnostic.Suppress}, // Binding redirects + {"CS1702", ReportDiagnostic.Suppress}, + {"CS1705", ReportDiagnostic.Suppress} + }); + + if (compilationOptions.AllowUnsafe.HasValue) + { + roslynOptions = roslynOptions.WithAllowUnsafe(compilationOptions.AllowUnsafe.Value); + } + + if (compilationOptions.Optimize.HasValue) + { + var optimizationLevel = compilationOptions.Optimize.Value ? OptimizationLevel.Debug : OptimizationLevel.Release; + roslynOptions = roslynOptions.WithOptimizationLevel(optimizationLevel); + } + + if (compilationOptions.WarningsAsErrors.HasValue) + { + var reportDiagnostic = compilationOptions.WarningsAsErrors.Value ? ReportDiagnostic.Error : ReportDiagnostic.Default; + roslynOptions = roslynOptions.WithGeneralDiagnosticOption(reportDiagnostic); + } + + options.CompilationOptions = roslynOptions; + } + + private static void SetParseOptions(RazorViewEngineOptions options, Microsoft.Extensions.DependencyModel.CompilationOptions compilationOptions) + { + var roslynParseOptions = options.ParseOptions; + roslynParseOptions = roslynParseOptions.WithPreprocessorSymbols(compilationOptions.Defines); + + var languageVersion = roslynParseOptions.LanguageVersion; + if (Enum.TryParse(compilationOptions.LanguageVersion, ignoreCase: true, result: out languageVersion)) + { + roslynParseOptions = roslynParseOptions.WithLanguageVersion(languageVersion); + } + + options.ParseOptions = roslynParseOptions; + } + } +} \ No newline at end of file