From 99f501152b4ce01c99818b670b89089e9f6cfbd4 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 6 Dec 2015 03:34:10 -0800 Subject: [PATCH] Remove dependency on ICompilationOptionsProvider - Removed Configuration from RazorViewEngineOptions - Added ParseOptions and CompilationOptions to RazorViewEngineOptions --- .../Compilation/RoslynCompilationService.cs | 37 +++++----- .../Compilation/SyntaxTreeGenerator.cs | 19 ++++- .../MvcRazorMvcCoreBuilderExtensions.cs | 1 - .../RazorViewEngineOptions.cs | 59 +++++++++------ .../RazorViewEngineOptionsSetup.cs | 16 +++-- .../RoslynCompilationServiceTest.cs | 71 ++----------------- .../RazorViewEngineOptionsSetupTest.cs | 34 +++++++-- test/WebSites/RazorWebSite/Startup.cs | 5 ++ 8 files changed, 120 insertions(+), 122 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs index 7af077d285..76fb3eb28b 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs @@ -35,12 +35,12 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation private readonly ILibraryExporter _libraryExporter; private readonly IApplicationEnvironment _environment; - private readonly ICompilerOptionsProvider _compilerOptionsProvider; private readonly IFileProvider _fileProvider; private readonly Lazy> _applicationReferences; private readonly string _classPrefix; - private readonly string _configuration; - private Action _compilationCallback; + private readonly Action _compilationCallback; + private readonly CSharpParseOptions _parseOptions; + private readonly CSharpCompilationOptions _compilationOptions; #if DOTNET5_5 private readonly RazorLoadContext _razorLoadContext; @@ -61,18 +61,18 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation public RoslynCompilationService( IApplicationEnvironment environment, ILibraryExporter libraryExporter, - ICompilerOptionsProvider compilerOptionsProvider, IMvcRazorHost host, IOptions optionsAccessor) { _environment = environment; _libraryExporter = libraryExporter; _applicationReferences = new Lazy>(GetApplicationReferences); - _compilerOptionsProvider = compilerOptionsProvider; _fileProvider = optionsAccessor.Value.FileProvider; _classPrefix = host.MainClassNamePrefix; - _configuration = optionsAccessor.Value.Configuration; _compilationCallback = optionsAccessor.Value.CompilationCallback; + _parseOptions = optionsAccessor.Value.ParseOptions; + _compilationOptions = optionsAccessor.Value.CompilationOptions; + #if DOTNET5_5 _razorLoadContext = new RazorLoadContext(); @@ -93,21 +93,17 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation } var assemblyName = Path.GetRandomFileName(); - var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment, _configuration); + var syntaxTree = SyntaxTreeGenerator.Generate( compilationContent, assemblyName, - compilationSettings); + _parseOptions); var references = _applicationReferences.Value; - var compilationOptions = compilationSettings - .CompilationOptions - .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); - var compilation = CSharpCompilation.Create( assemblyName, - options: compilationOptions, + options: _compilationOptions, syntaxTrees: new[] { syntaxTree }, references: references); @@ -258,13 +254,16 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation } var export = _libraryExporter.GetAllExports(_environment.ApplicationName); - foreach (var metadataReference in export.MetadataReferences) + if (export != null) { - // 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)); + 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; diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs index 6859ec86f3..9e3d5987ec 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs @@ -16,6 +16,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation string text, string path, CompilationSettings compilationSettings) + { + if (compilationSettings == null) + { + throw new ArgumentNullException(nameof(compilationSettings)); + } + + return Generate(text, path, GetParseOptions(compilationSettings)); + } + + public static SyntaxTree Generate( + string text, + string path, + CSharpParseOptions parseOptions) { if (text == null) { @@ -27,15 +40,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation throw new ArgumentNullException(nameof(path)); } - if (compilationSettings == null) + if (parseOptions == null) { - throw new ArgumentNullException(nameof(compilationSettings)); + throw new ArgumentNullException(nameof(parseOptions)); } var sourceText = SourceText.From(text, Encoding.UTF8); var syntaxTree = CSharpSyntaxTree.ParseText(sourceText, path: path, - options: GetParseOptions(compilationSettings)); + options: parseOptions); return syntaxTree; } diff --git a/src/Microsoft.AspNet.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNet.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs index fbefa3faa5..ba63660b68 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs @@ -123,7 +123,6 @@ namespace Microsoft.Extensions.DependencyInjection if (CompilationServices.Default != null) { services.TryAdd(ServiceDescriptor.Instance(CompilationServices.Default.LibraryExporter)); - services.TryAdd(ServiceDescriptor.Instance(CompilationServices.Default.CompilerOptionsProvider)); } services.TryAddEnumerable( diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs index 479224c4b5..cb8c63ed54 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.Mvc.Razor.Compilation; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNet.Mvc.Razor { @@ -15,7 +16,9 @@ namespace Microsoft.AspNet.Mvc.Razor { private IFileProvider _fileProvider; - private string _configuration; + private CSharpParseOptions _parseOptions = new CSharpParseOptions(LanguageVersion.CSharp6); + + private CSharpCompilationOptions _compilationOptions = new CSharpCompilationOptions(CodeAnalysis.OutputKind.DynamicallyLinkedLibrary); private Action _compilationCallback = c => { }; @@ -48,27 +51,6 @@ namespace Microsoft.AspNet.Mvc.Razor } } - /// - /// Gets or sets the configuration name used by to compile razor views. - /// - /// - /// At startup, this is initialized to "Debug" if service is - /// registred and environment is development () else it is set to - /// "Release". - /// - public string Configuration - { - get { return _configuration; } - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(value)); - } - _configuration = value; - } - } - /// /// Gets or sets the callback that is used to customize Razor compilation /// to change compilation settings you can update property. @@ -86,5 +68,38 @@ namespace Microsoft.AspNet.Mvc.Razor _compilationCallback = value; } } + + /// + /// Gets or sets the options used by Razor view compilation. + /// + public CSharpParseOptions ParseOptions + { + get { return _parseOptions; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + _parseOptions = value; + } + } + + /// + /// Gets or sets the used by Razor view compilation. + /// + public CSharpCompilationOptions CompilationOptions + { + get { return _compilationOptions; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + _compilationOptions = value; + } + } + } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs index 7276b64126..72751fe07f 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs @@ -4,8 +4,8 @@ using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Mvc.Razor; -using Microsoft.AspNet.Mvc.Razor.Compilation; -using Microsoft.Extensions.CompilationAbstractions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.OptionsModel; using Microsoft.Extensions.PlatformAbstractions; @@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc /// for the application. /// for the application. public RazorViewEngineOptionsSetup(IApplicationEnvironment applicationEnvironment, - IHostingEnvironment hostingEnvironment) + IHostingEnvironment hostingEnvironment) : base(options => ConfigureRazor(options, applicationEnvironment, hostingEnvironment)) { } @@ -32,13 +32,19 @@ namespace Microsoft.AspNet.Mvc IHostingEnvironment hostingEnvironment) { razorOptions.FileProvider = new PhysicalFileProvider(applicationEnvironment.ApplicationBasePath); + + var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp6); + var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + if (hostingEnvironment.IsDevelopment()) { - razorOptions.Configuration = "Debug"; + razorOptions.ParseOptions = parseOptions.WithPreprocessorSymbols("DEBUG"); + razorOptions.CompilationOptions = compilationOptions.WithOptimizationLevel(OptimizationLevel.Debug); } else { - razorOptions.Configuration = "Release"; + razorOptions.ParseOptions = parseOptions.WithPreprocessorSymbols("RELEASE"); + razorOptions.CompilationOptions = compilationOptions.WithOptimizationLevel(OptimizationLevel.Release); } } } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs index 21611b7cc8..114a700b14 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs @@ -27,14 +27,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation public class MyTestType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); var mvcRazorHost = new Mock(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns(string.Empty); @@ -42,7 +34,6 @@ public class MyTestType {}"; var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); var relativeFileInfo = new RelativeFileInfo( @@ -67,14 +58,6 @@ public class MyTestType {}"; this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of(); var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); @@ -82,7 +65,6 @@ this should fail"; var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); @@ -106,20 +88,11 @@ this should fail"; var content = @"this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of(); var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost, GetOptions()); var relativeFileInfo = new RelativeFileInfo( @@ -148,14 +121,6 @@ this should fail"; this should fail"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); var mvcRazorHost = Mock.Of(); var mockFileInfo = new Mock(); @@ -167,7 +132,6 @@ this should fail"; var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost, GetOptions(fileProvider)); @@ -197,24 +161,18 @@ public class MyNonCustomDefinedClass {} "; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } }); var mvcRazorHost = new Mock(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("My"); + var options = GetOptions(); + options.Value.ParseOptions = options.Value.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); + var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost.Object, - GetOptions()); + options); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); @@ -236,14 +194,6 @@ public class RazorPrefixType {} public class NotRazorPrefixType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); var mvcRazorHost = new Mock(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) .Returns("RazorPrefix"); @@ -251,7 +201,6 @@ public class NotRazorPrefixType {}"; var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions()); @@ -284,7 +233,6 @@ public class NotRazorPrefixType {}"; var compilationService = new RoslynCompilationService( PlatformServices.Default.Application, CompilationServices.Default.LibraryExporter, - Mock.Of(), Mock.Of(), options.Object); @@ -368,15 +316,6 @@ public class NotRazorPrefixType {}"; var content = "public class MyTestType {}"; var applicationEnvironment = PlatformServices.Default.Application; var libraryExporter = CompilationServices.Default.LibraryExporter; - - var compilerOptionsProvider = new Mock(); - compilerOptionsProvider - .Setup(p => p.GetCompilerOptions( - applicationEnvironment.ApplicationName, - applicationEnvironment.RuntimeFramework, - ConfigurationName)) - .Returns(new CompilerOptions()); - RoslynCompilationContext usedCompilation = null; var mvcRazorHost = new Mock(); mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) @@ -385,7 +324,6 @@ public class NotRazorPrefixType {}"; var compilationService = new RoslynCompilationService( applicationEnvironment, libraryExporter, - compilerOptionsProvider.Object, mvcRazorHost.Object, GetOptions(callback: c => usedCompilation = c)); @@ -418,7 +356,6 @@ public class NotRazorPrefixType {}"; var razorViewEngineOptions = new RazorViewEngineOptions { FileProvider = fileProvider ?? new TestFileProvider(), - Configuration = ConfigurationName, CompilationCallback = callback ?? (c => { }) }; var options = new Mock>(); diff --git a/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs index 033e0e895d..b0921c51ec 100644 --- a/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs @@ -5,6 +5,7 @@ using System.IO; using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Mvc.Razor; +using Microsoft.CodeAnalysis; using Microsoft.Extensions.PlatformAbstractions; using Moq; using Xunit; @@ -35,10 +36,10 @@ namespace Microsoft.AspNet.Mvc } [Theory] - [InlineData("Development", "Debug")] - [InlineData("Staging", "Release")] - [InlineData("Production", "Release")] - public void RazorViewEngineOptionsSetup_SetsCorrectConfiguration(string environment, string expectedConfiguration) + [InlineData("Development", "DEBUG")] + [InlineData("Staging", "RELEASE")] + [InlineData("Production", "RELEASE")] + public void RazorViewEngineOptionsSetup_SetsPreprocessorSymbols(string environment, string expectedConfiguration) { // Arrange var options = new RazorViewEngineOptions(); @@ -54,7 +55,30 @@ namespace Microsoft.AspNet.Mvc optionsSetup.Configure(options); // Assert - Assert.Equal(expectedConfiguration, options.Configuration); + Assert.Equal(new[] { expectedConfiguration }, options.ParseOptions.PreprocessorSymbolNames); + } + + [Theory] + [InlineData("Development", OptimizationLevel.Debug)] + [InlineData("Staging", OptimizationLevel.Release)] + [InlineData("Production", OptimizationLevel.Release)] + public void RazorViewEngineOptionsSetup_SetsOptimizationLevel(string environment, OptimizationLevel expectedOptimizationLevel) + { + // Arrange + var options = new RazorViewEngineOptions(); + var appEnv = new Mock(); + appEnv.SetupGet(e => e.ApplicationBasePath) + .Returns(Directory.GetCurrentDirectory()); + var hostingEnv = new Mock(); + hostingEnv.SetupGet(e => e.EnvironmentName) + .Returns(environment); + var optionsSetup = new RazorViewEngineOptionsSetup(appEnv.Object, hostingEnv.Object); + + // Act + optionsSetup.Configure(options); + + // Assert + Assert.Equal(expectedOptimizationLevel, options.CompilationOptions.OptimizationLevel); } } } \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Startup.cs b/test/WebSites/RazorWebSite/Startup.cs index a144968783..beb4c59efa 100644 --- a/test/WebSites/RazorWebSite/Startup.cs +++ b/test/WebSites/RazorWebSite/Startup.cs @@ -19,6 +19,11 @@ namespace RazorWebSite .AddRazorOptions(options => { options.ViewLocationExpanders.Add(new NonMainPageViewLocationExpander()); +#if DNX451 + options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("DNX451", "DNX451_CUSTOM_DEFINE"); +#else + options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("DNXCORE50", "DNXCORE50_CUSTOM_DEFINE"); +#endif }) .AddViewOptions(options => {