diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationOptionsProviderExtension.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationOptionsProviderExtension.cs new file mode 100644 index 0000000000..4e0ccc6179 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationOptionsProviderExtension.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Runtime; +using Microsoft.Framework.Runtime.Roslyn; + +namespace Microsoft.AspNet.Mvc.Razor +{ + /// + /// Extension methods for . + /// + public static class CompilationOptionsProviderExtension + { + /// + /// Parses the for the current executing application and returns a + /// used for Roslyn compilation. + /// + /// A that reads compiler options. + /// The for the executing application. + /// The for the current application. + public static CompilationSettings GetCompilationSettings( + [NotNull] this ICompilerOptionsProvider compilerOptionsProvider, + [NotNull] IApplicationEnvironment applicationEnvironment) + { + return compilerOptionsProvider.GetCompilerOptions(applicationEnvironment.ApplicationBasePath, + applicationEnvironment.RuntimeFramework, + applicationEnvironment.Configuration) + .ToCompilationSettings(applicationEnvironment.RuntimeFramework); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs index a0a977e037..047ce5acb3 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs @@ -15,7 +15,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; using Microsoft.Framework.Runtime; -namespace Microsoft.AspNet.Mvc.Razor.Compilation +namespace Microsoft.AspNet.Mvc.Razor { /// /// A type that uses Roslyn to compile C# content. @@ -29,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation private readonly ILibraryManager _libraryManager; private readonly IApplicationEnvironment _environment; private readonly IAssemblyLoadContext _loader; + private readonly ICompilerOptionsProvider _compilerOptionsProvider; private readonly Lazy> _applicationReferences; @@ -44,12 +45,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation public RoslynCompilationService(IApplicationEnvironment environment, IAssemblyLoadContextAccessor loaderAccessor, ILibraryManager libraryManager, + ICompilerOptionsProvider compilerOptionsProvider, IMvcRazorHost host) { _environment = environment; _loader = loaderAccessor.GetLoadContext(typeof(RoslynCompilationService).GetTypeInfo().Assembly); _libraryManager = libraryManager; _applicationReferences = new Lazy>(GetApplicationReferences); + _compilerOptionsProvider = compilerOptionsProvider; _classPrefix = host.MainClassNamePrefix; } @@ -60,15 +63,19 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation // map to the source file. If a file does not exist on a physical file system, PhysicalPath will be null. // This prevents files that exist in a non-physical file system from being debugged. var path = fileInfo.PhysicalPath ?? fileInfo.Name; - var syntaxTrees = new[] { SyntaxTreeGenerator.Generate(compilationContent, path) }; - + var compilationSettings = _compilerOptionsProvider.GetCompilationSettings(_environment); + var syntaxTree = SyntaxTreeGenerator.Generate(compilationContent, + path, + compilationSettings); var references = _applicationReferences.Value; var assemblyName = Path.GetRandomFileName(); + var compilationOptions = compilationSettings.CompilationOptions + .WithOutputKind(OutputKind.DynamicallyLinkedLibrary); var compilation = CSharpCompilation.Create(assemblyName, - options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary), - syntaxTrees: syntaxTrees, + options: compilationOptions, + syntaxTrees: new[] { syntaxTree }, references: references); using (var ms = new MemoryStream()) @@ -115,7 +122,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation .First(t => t.Name. StartsWith(_classPrefix, StringComparison.Ordinal)); - return UncachedCompilationResult.Successful(type); + return UncachedCompilationResult.Successful(type, compilationContent); } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs index 0598a1a90e..0453ba051d 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/SyntaxTreeGenerator.cs @@ -5,48 +5,29 @@ using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; +using Microsoft.Framework.Runtime.Roslyn; namespace Microsoft.AspNet.Mvc.Razor { public static class SyntaxTreeGenerator { - private static CSharpParseOptions DefaultOptions - { - get - { - return CSharpParseOptions.Default - .WithLanguageVersion(LanguageVersion.CSharp6); - } - } - - public static SyntaxTree Generate([NotNull] string text, [NotNull] string path) - { - return GenerateCore(text, path, DefaultOptions); - } - public static SyntaxTree Generate([NotNull] string text, [NotNull] string path, - [NotNull] CSharpParseOptions options) - { - return GenerateCore(text, path, options); - } - - public static SyntaxTree GenerateCore([NotNull] string text, - [NotNull] string path, - [NotNull] CSharpParseOptions options) + [NotNull] CompilationSettings compilationSettings) { var sourceText = SourceText.From(text, Encoding.UTF8); var syntaxTree = CSharpSyntaxTree.ParseText(sourceText, path: path, - options: options); + options: GetParseOptions(compilationSettings)); return syntaxTree; } - public static CSharpParseOptions GetParseOptions(CSharpCompilation compilation) + public static CSharpParseOptions GetParseOptions(CompilationSettings compilationSettings) { - return CSharpParseOptions.Default - .WithLanguageVersion(compilation.LanguageVersion); + return new CSharpParseOptions( + languageVersion: compilationSettings.LanguageVersion, + preprocessorSymbols: compilationSettings.Defines.AsImmutable()); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorFileInfoCollectionGenerator.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorFileInfoCollectionGenerator.cs index e7da94f301..956aa62d63 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorFileInfoCollectionGenerator.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorFileInfoCollectionGenerator.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Framework.Runtime.Roslyn; namespace Microsoft.AspNet.Mvc.Razor { @@ -13,13 +13,13 @@ namespace Microsoft.AspNet.Mvc.Razor private string _fileFormat; protected IReadOnlyList FileInfos { get; private set; } - protected CSharpParseOptions Options { get; private set; } + protected CompilationSettings CompilationSettings { get; } public RazorFileInfoCollectionGenerator([NotNull] IReadOnlyList fileInfos, - [NotNull] CSharpParseOptions options) + [NotNull] CompilationSettings compilationSettings) { FileInfos = fileInfos; - Options = options; + CompilationSettings = compilationSettings; } public virtual SyntaxTree GenerateCollection() @@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.Razor var sourceCode = builder.ToString(); var syntaxTree = SyntaxTreeGenerator.Generate(sourceCode, "__AUTO__GeneratedViewsCollection.cs", - Options); + CompilationSettings); return syntaxTree; } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs index 2d69ef1ff6..b41c3bf017 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Microsoft.Framework.Runtime; +using Microsoft.Framework.Runtime.Roslyn; namespace Microsoft.AspNet.Mvc.Razor { @@ -19,24 +20,28 @@ namespace Microsoft.AspNet.Mvc.Razor private readonly IFileSystem _fileSystem; private readonly IMvcRazorHost _host; - public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider) : + public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider, + [NotNull] CompilationSettings compilationSettings) : this(designTimeServiceProvider, designTimeServiceProvider.GetRequiredService(), - designTimeServiceProvider.GetRequiredService>()) + designTimeServiceProvider.GetRequiredService>(), + compilationSettings) { } public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider, [NotNull] IMvcRazorHost host, - [NotNull] IOptions optionsAccessor) + [NotNull] IOptions optionsAccessor, + [NotNull] CompilationSettings compilationSettings) { _serviceProvider = designTimeServiceProvider; _host = host; - - var appEnv = _serviceProvider.GetRequiredService(); _fileSystem = optionsAccessor.Options.FileSystem; + CompilationSettings = compilationSettings; } + protected CompilationSettings CompilationSettings { get; } + protected virtual string FileExtension { get; } = ".cshtml"; public virtual void CompileViews([NotNull] IBeforeCompileContext context) @@ -47,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.Razor { var collectionGenerator = new RazorFileInfoCollectionGenerator( descriptors, - SyntaxTreeGenerator.GetParseOptions(context.CSharpCompilation)); + CompilationSettings); var tree = collectionGenerator.GenerateCollection(); context.CSharpCompilation = context.CSharpCompilation.AddSyntaxTrees(tree); @@ -57,14 +62,11 @@ namespace Microsoft.AspNet.Mvc.Razor protected virtual IReadOnlyList CreateCompilationDescriptors( [NotNull] IBeforeCompileContext context) { - var options = SyntaxTreeGenerator.GetParseOptions(context.CSharpCompilation); var list = new List(); foreach (var info in GetFileInfosRecursive(string.Empty)) { - var descriptor = ParseView(info, - context, - options); + var descriptor = ParseView(info, context); if (descriptor != null) { @@ -107,8 +109,7 @@ namespace Microsoft.AspNet.Mvc.Razor } protected virtual RazorFileInfo ParseView([NotNull] RelativeFileInfo fileInfo, - [NotNull] IBeforeCompileContext context, - [NotNull] CSharpParseOptions options) + [NotNull] IBeforeCompileContext context) { using (var stream = fileInfo.FileInfo.CreateReadStream()) { @@ -124,7 +125,9 @@ namespace Microsoft.AspNet.Mvc.Razor if (generatedCode != null) { - var syntaxTree = SyntaxTreeGenerator.Generate(generatedCode, fileInfo.FileInfo.PhysicalPath, options); + var syntaxTree = SyntaxTreeGenerator.Generate(generatedCode, + fileInfo.FileInfo.PhysicalPath, + CompilationSettings); var fullTypeName = results.GetMainClassName(_host, syntaxTree); if (fullTypeName != null) diff --git a/src/Microsoft.AspNet.Mvc.Razor/project.json b/src/Microsoft.AspNet.Mvc.Razor/project.json index f6db9c2334..239715bbc6 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/project.json +++ b/src/Microsoft.AspNet.Mvc.Razor/project.json @@ -8,7 +8,7 @@ "Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" }, "Microsoft.AspNet.Mvc.Core": "6.0.0-*", "Microsoft.AspNet.Mvc.Razor.Host": "6.0.0-*", - "Microsoft.CodeAnalysis.CSharp": "1.0.0-beta2-*" + "Microsoft.Framework.Runtime.Roslyn.Common": "1.0.0-*" }, "frameworks": { "aspnet50": { diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index d02390f793..6c7b832146 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -9,7 +9,6 @@ using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.OptionDescriptors; using Microsoft.AspNet.Mvc.Razor; -using Microsoft.AspNet.Mvc.Razor.Compilation; using Microsoft.AspNet.Mvc.Razor.OptionDescriptors; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Routing; diff --git a/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs b/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs index fa676ee056..e4f5298c1a 100644 --- a/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs +++ b/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs @@ -29,14 +29,17 @@ namespace Microsoft.AspNet.Mvc public virtual void BeforeCompile(IBeforeCompileContext context) { - var appEnv = _appServices.GetRequiredService(); + var applicationEnvironment = _appServices.GetRequiredService(); + var compilerOptionsProvider = _appServices.GetRequiredService(); + var compilationSettings = compilerOptionsProvider.GetCompilationSettings(applicationEnvironment); - var setup = new RazorViewEngineOptionsSetup(appEnv); + var setup = new RazorViewEngineOptionsSetup(applicationEnvironment); var sc = new ServiceCollection(); sc.ConfigureOptions(setup); sc.AddMvc(); - var viewCompiler = new RazorPreCompiler(BuildFallbackServiceProvider(sc, _appServices)); + var serviceProvider = BuildFallbackServiceProvider(sc, _appServices); + var viewCompiler = new RazorPreCompiler(serviceProvider, compilationSettings); viewCompiler.CompileViews(context); } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/CompilationOptionsTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/CompilationOptionsTests.cs new file mode 100644 index 0000000000..ed5f3f1a32 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/CompilationOptionsTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using RazorWebSite; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + // Test to verify compilation options from the application are used to compile + // precompiled and dynamically compiled views. + public class CompilationOptionsTests + { + private readonly IServiceProvider _provider = TestHelper.CreateServices(nameof(RazorWebSite)); + private readonly Action _app = new Startup().Configure; + + [Fact] + public async Task CompilationOptions_AreUsedByViewsAndPartials() + { + // Arrange +#if ASPNET50 + var expected = +@"This method is running from ASPNET50 + +This method is only defined in ASPNET50"; +#elif ASPNETCORE50 + var expected = +@"This method is running from ASPNETCORE50 + +This method is only defined in ASPNETCORE50"; +#endif + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var body = await client.GetStringAsync("http://localhost/ViewsConsumingCompilationOptions/"); + + // Assert + Assert.Equal(expected, body.Trim()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs index 2e4945701a..307fb7751e 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/PrecompilationTest.cs @@ -11,6 +11,7 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.TestHost; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Runtime; +using PrecompilationWebSite; using Xunit; namespace Microsoft.AspNet.Mvc.FunctionalTests @@ -18,8 +19,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests public class PrecompilationTest { private static readonly TimeSpan _cacheDelayInterval = TimeSpan.FromSeconds(2); - private readonly IServiceProvider _services = TestHelper.CreateServices("PrecompilationWebSite"); - private readonly Action _app = new PrecompilationWebSite.Startup().Configure; + private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(PrecompilationWebSite)); + private readonly Action _app = new Startup().Configure; [Fact] public async Task PrecompiledView_RendersCorrectly() @@ -37,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // We will render a view that writes the fully qualified name of the Assembly containing the type of // the view. If the view is precompiled, this assembly will be PrecompilationWebsite. - var assemblyName = typeof(PrecompilationWebSite.Startup).GetTypeInfo().Assembly.GetName().ToString(); + var assemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().ToString(); try { @@ -142,6 +143,30 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests } } + [Fact] + public async Task PrecompiledView_UsesCompilationOptionsFromApplication() + { + // Arrange + var assemblyName = typeof(Startup).GetTypeInfo().Assembly.GetName().ToString(); +#if ASPNET50 + var expected = +@"Value set inside ASPNET50 " + assemblyName; +#elif ASPNETCORE50 + var expected = +@"Value set inside ASPNETCORE50 " + assemblyName; +#endif + + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/Home/PrecompiledViewsCanConsumeCompilationOptions"); + var responseContent = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(expected, responseContent.Trim()); + } + private static Task TouchFile(string viewsDir, string file) { File.AppendAllText(Path.Combine(viewsDir, file), " "); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs similarity index 100% rename from test/Microsoft.AspNet.Mvc.Razor.Test/RazorCompilationServiceTest.cs rename to test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RazorCompilationServiceTest.cs diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs new file mode 100644 index 0000000000..ebc6bfb26f --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.IO; +using System.Reflection; +using System.Runtime.Versioning; +using Microsoft.Framework.Runtime; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Razor +{ + public class RoslynCompilationServiceTest + { + [Fact] + public void Compile_ReturnsUncachedCompilationResultWithCompiledContent() + { + // Arrange + var content = @" +public class MyTestType {}"; + var applicationEnvironment = GetApplicationEnvironment(); + var accessor = GetLoadContextAccessor(); + var libraryManager = GetLibraryManager(); + + var compilerOptionsProvider = new Mock(); + compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath, + applicationEnvironment.RuntimeFramework, + applicationEnvironment.Configuration)) + .Returns(new CompilerOptions()); + var mvcRazorHost = new Mock(); + mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) + .Returns(string.Empty); + + var compilationService = new RoslynCompilationService(applicationEnvironment, + accessor, + libraryManager, + compilerOptionsProvider.Object, + mvcRazorHost.Object); + + // Act + var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content); + + // Assert + var uncachedResult = Assert.IsType(result); + Assert.Equal("MyTestType", result.CompiledType.Name); + Assert.Equal(content, result.CompiledContent); + } + + [Fact] + public void Compile_UsesApplicationsCompilationSettings_ForParsingAndCompilation() + { + // Arrange + var content = @" +#if MY_CUSTOM_DEFINE +public class MyCustomDefinedClass {} +#else +public class MyNonCustomDefinedClass {} +#endif +"; + var applicationEnvironment = GetApplicationEnvironment(); + var accessor = GetLoadContextAccessor(); + var libraryManager = GetLibraryManager(); + + var compilerOptionsProvider = new Mock(); + compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath, + applicationEnvironment.RuntimeFramework, + applicationEnvironment.Configuration)) + .Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } }); + var mvcRazorHost = new Mock(); + mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) + .Returns("My"); + + var compilationService = new RoslynCompilationService(applicationEnvironment, + accessor, + libraryManager, + compilerOptionsProvider.Object, + mvcRazorHost.Object); + + // Act + var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content); + + // Assert + Assert.NotNull(result.CompiledType); + Assert.Equal("MyCustomDefinedClass", result.CompiledType.Name); + } + + [Fact] + public void Compile_ReturnsSingleTypeThatStartsWithMainClassNamePrefix() + { + // Arrange + var content = @" +public class RazorPrefixType {} +public class NotRazorPrefixType {}"; + var applicationEnvironment = GetApplicationEnvironment(); + var accessor = GetLoadContextAccessor(); + var libraryManager = GetLibraryManager(); + + var compilerOptionsProvider = new Mock(); + compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationBasePath, + applicationEnvironment.RuntimeFramework, + applicationEnvironment.Configuration)) + .Returns(new CompilerOptions()); + var mvcRazorHost = new Mock(); + mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) + .Returns("RazorPrefix"); + + var compilationService = new RoslynCompilationService(applicationEnvironment, + accessor, + libraryManager, + compilerOptionsProvider.Object, + mvcRazorHost.Object); + + // Act + var result = compilationService.Compile(new TestFileInfo { PhysicalPath = "SomePath" }, content); + + // Assert + Assert.NotNull(result.CompiledType); + Assert.Equal("RazorPrefixType", result.CompiledType.Name); + } + + private static ILibraryManager GetLibraryManager() + { + var fileReference = new Mock(); + fileReference.SetupGet(f => f.Path) + .Returns(typeof(string).Assembly.Location); + var libraryExport = new Mock(); + libraryExport.SetupGet(e => e.MetadataReferences) + .Returns(new[] { fileReference.Object }); + libraryExport.SetupGet(e => e.SourceReferences) + .Returns(new ISourceReference[0]); + + var libraryManager = new Mock(); + libraryManager.Setup(l => l.GetAllExports(It.IsAny())) + .Returns(libraryExport.Object); + return libraryManager.Object; + } + + private static IAssemblyLoadContextAccessor GetLoadContextAccessor() + { + var loadContext = new Mock(); + loadContext.Setup(s => s.LoadStream(It.IsAny(), It.IsAny())) + .Returns((Stream stream, Stream pdb) => + { + var memoryStream = (MemoryStream)stream; + return Assembly.Load(memoryStream.ToArray()); + }); + + var accessor = new Mock(); + accessor.Setup(a => a.GetLoadContext(typeof(RoslynCompilationService).Assembly)) + .Returns(loadContext.Object); + return accessor.Object; + } + + private IApplicationEnvironment GetApplicationEnvironment() + { + var applicationEnvironment = new Mock(); + applicationEnvironment.SetupGet(a => a.ApplicationName) + .Returns("MyApp"); + applicationEnvironment.SetupGet(a => a.RuntimeFramework) + .Returns(new FrameworkName("ASPNET", new Version(5, 0))); + applicationEnvironment.SetupGet(a => a.Configuration) + .Returns("Debug"); + applicationEnvironment.SetupGet(a => a.ApplicationBasePath) + .Returns("MyBasePath"); + + return applicationEnvironment.Object; + } + } +} \ No newline at end of file diff --git a/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs b/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs index bdff9e4876..7fca420d2e 100644 --- a/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs +++ b/test/WebSites/PrecompilationWebSite/Controllers/HomeController.cs @@ -11,5 +11,10 @@ namespace PrecompilationWebSite.Controllers { return View(); } + + public IActionResult PrecompiledViewsCanConsumeCompilationOptions() + { + return View("~/Views/ViewsConsumingCompilationOptions/Index"); + } } } diff --git a/test/WebSites/PrecompilationWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml b/test/WebSites/PrecompilationWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml new file mode 100644 index 0000000000..58b2a8d961 --- /dev/null +++ b/test/WebSites/PrecompilationWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml @@ -0,0 +1,9 @@ +@{ +string message = +#if ASPNET50 + "Value set inside ASPNET50 " + GetType().Assembly.FullName; +#elif ASPNETCORE50 + "Value set inside ASPNETCORE50 " + System.Reflection.IntrospectionExtensions.GetTypeInfo(GetType()).Assembly.FullName; +#endif +} +@message diff --git a/test/WebSites/PrecompilationWebSite/project.json b/test/WebSites/PrecompilationWebSite/project.json index 01696e38f0..3f6e124d78 100644 --- a/test/WebSites/PrecompilationWebSite/project.json +++ b/test/WebSites/PrecompilationWebSite/project.json @@ -12,8 +12,16 @@ "Microsoft.AspNet.StaticFiles": "1.0.0-*" }, "frameworks": { - "aspnet50": { }, - "aspnetcore50": { } + "aspnet50": { + "compilationOptions": { + "define": [ "CUSTOM_ASPNET50_DEFINE" ] + } + }, + "aspnetcore50": { + "compilationOptions": { + "define": [ "CUSTOM_ASPNETCORE50_DEFINE" ] + } + } }, "webroot": "wwwroot" } diff --git a/test/WebSites/RazorWebSite/Controllers/ViewsConsumingCompilationOptionsController.cs b/test/WebSites/RazorWebSite/Controllers/ViewsConsumingCompilationOptionsController.cs new file mode 100644 index 0000000000..9144cae9a9 --- /dev/null +++ b/test/WebSites/RazorWebSite/Controllers/ViewsConsumingCompilationOptionsController.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc; + +namespace RazorWebSite.Controllers +{ + // Views returned by this controller use #ifdefs for defines specified in project.json + // The intent of this controller is to verify that view compilation uses the app's compilation settings. + public class ViewsConsumingCompilationOptionsController : Controller + { + public ViewResult Index() + { + return View(); + } + } +} \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Services/FrameworkSpecificHelper.cs b/test/WebSites/RazorWebSite/Services/FrameworkSpecificHelper.cs new file mode 100644 index 0000000000..5c8cbf01f8 --- /dev/null +++ b/test/WebSites/RazorWebSite/Services/FrameworkSpecificHelper.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace RazorWebSite +{ + public class FrameworkSpecificHelper + { + public string ExecuteOperation() + { +#if ASPNET50 + return "This method is running from ASPNET50"; +#elif ASPNETCORE50 + return "This method is running from ASPNETCORE50"; +#endif + } + +#if ASPNET50_CUSTOM_DEFINE + public string ExecuteAspNet50Operation() + { + return "This method is only defined in ASPNET50"; + } +#endif + +#if ASPNETCORE50_CUSTOM_DEFINE + public string ExecuteAspNetCore50Operation() + { + return "This method is only defined in ASPNETCORE50"; + } +#endif + } +} \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Startup.cs b/test/WebSites/RazorWebSite/Startup.cs index ae70ef8d0f..91a7547720 100644 --- a/test/WebSites/RazorWebSite/Startup.cs +++ b/test/WebSites/RazorWebSite/Startup.cs @@ -21,6 +21,7 @@ namespace RazorWebSite services.AddMvc(configuration); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.Configure(options => { var expander = new LanguageViewLocationExpander( diff --git a/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml b/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml new file mode 100644 index 0000000000..71684f7ea6 --- /dev/null +++ b/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/Index.cshtml @@ -0,0 +1,3 @@ +@inject FrameworkSpecificHelper MyHelper +@MyHelper.ExecuteOperation() +@Html.Partial("_Partial") \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/_Partial.cshtml b/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/_Partial.cshtml new file mode 100644 index 0000000000..9234f21545 --- /dev/null +++ b/test/WebSites/RazorWebSite/Views/ViewsConsumingCompilationOptions/_Partial.cshtml @@ -0,0 +1,11 @@ +@inject FrameworkSpecificHelper MyHelper +@{ + string value = +#if ASPNET50_CUSTOM_DEFINE + MyHelper.ExecuteAspNet50Operation(); +#endif +#if ASPNETCORE50_CUSTOM_DEFINE + MyHelper.ExecuteAspNetCore50Operation(); +#endif +} +@value \ No newline at end of file diff --git a/test/WebSites/RazorWebSite/project.json b/test/WebSites/RazorWebSite/project.json index c582a1382f..25816ad6c7 100644 --- a/test/WebSites/RazorWebSite/project.json +++ b/test/WebSites/RazorWebSite/project.json @@ -12,8 +12,16 @@ "Microsoft.AspNet.StaticFiles": "1.0.0-*" }, "frameworks": { - "aspnet50": { }, - "aspnetcore50": { } + "aspnet50": { + "compilationOptions": { + "define": [ "ASPNET50_CUSTOM_DEFINE" ] + } + }, + "aspnetcore50": { + "compilationOptions": { + "define": [ "ASPNETCORE50_CUSTOM_DEFINE" ] + } + } }, "webroot": "wwwroot" }