diff --git a/samples/MvcSample.Web/Startup.cs b/samples/MvcSample.Web/Startup.cs index b6cedc98b8..7005ce2aa7 100644 --- a/samples/MvcSample.Web/Startup.cs +++ b/samples/MvcSample.Web/Startup.cs @@ -10,17 +10,12 @@ namespace MvcSample.Web { public class Startup { - private readonly IApplicationEnvironment _env; - - public Startup(IApplicationEnvironment env) - { - _env = env; - } - public void Configuration(IBuilder app) { var configuration = new Configuration(); - var serviceProvider = new ServiceProvider().Add(MvcServices.GetDefaultServices(configuration, _env)); + var services = MvcServices.GetDefaultServices(configuration); + var serviceProvider = + DefaultServiceProvider.Create(app.ServiceProvider, services); var routes = new RouteCollection() { diff --git a/samples/MvcSample.Web/project.json b/samples/MvcSample.Web/project.json index 2ba16c2367..c0ab33e38e 100644 --- a/samples/MvcSample.Web/project.json +++ b/samples/MvcSample.Web/project.json @@ -12,6 +12,12 @@ }, "configurations": { "net45": { }, - "k10" : { } + "k10" : { + "dependencies": { + "System.Collections" : "4.0.0.0", + "System.Runtime" : "4.0.20.0", + "System.ComponentModel": "4.0.0.0" + } + } } } \ No newline at end of file diff --git a/samples/MvcSample.Web/web.config b/samples/MvcSample.Web/web.config index 7bab44e5cd..04274d15b2 100644 --- a/samples/MvcSample.Web/web.config +++ b/samples/MvcSample.Web/web.config @@ -1,7 +1,7 @@  - + diff --git a/samples/MvcSample/Program.cs b/samples/MvcSample/Program.cs index 18b39ea4be..dfd898791c 100644 --- a/samples/MvcSample/Program.cs +++ b/samples/MvcSample/Program.cs @@ -13,19 +13,16 @@ namespace MvcSample { const string baseUrl = "http://localhost:9001/"; private readonly IServiceProvider _serviceProvider; - private readonly IApplicationEnvironment _env; - public Program(IServiceProvider serviceProvider, - IApplicationEnvironment env) + public Program(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; - _env = env; } public void Main() { #if NET45 - using (WebApp.Start(baseUrl, app => new Startup(_serviceProvider, _env).Configuration(app))) + using (WebApp.Start(baseUrl, app => new Startup(_serviceProvider).Configuration(app))) { Console.WriteLine("Listening at {0}", baseUrl); Process.Start(baseUrl); diff --git a/samples/MvcSample/Startup.cs b/samples/MvcSample/Startup.cs index 92d2b37fc2..780d0e89ed 100644 --- a/samples/MvcSample/Startup.cs +++ b/samples/MvcSample/Startup.cs @@ -12,14 +12,11 @@ namespace MvcSample { public class Startup { - private readonly IServiceProvider _serviceProvider; - private readonly IApplicationEnvironment _env; + private IServiceProvider _serviceProvider; - public Startup(IServiceProvider serviceProvider, - IApplicationEnvironment env) + public Startup(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; - _env = env; } public void Configuration(IAppBuilder app) @@ -33,11 +30,11 @@ namespace MvcSample private void ConfigureMvc(IBuilder builder) { var configuration = new Configuration(); - var services = MvcServices.GetDefaultServices(configuration, _env); - var serviceProvider = new ServiceProvider().Add(services); + var services = MvcServices.GetDefaultServices(configuration); + var serviceProvider = new ServiceProvider(_serviceProvider).Add(services); serviceProvider.AddInstance(new PassThroughAttribute()); - + var routes = new RouteCollection() { DefaultHandler = new MvcApplication(serviceProvider), diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CscBasedCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/CscBasedCompilationService.cs deleted file mode 100644 index 151d8e0237..0000000000 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/CscBasedCompilationService.cs +++ /dev/null @@ -1,126 +0,0 @@ -#if NET45 -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.AspNet.Mvc.Razor -{ - public class CscBasedCompilationService : ICompilationService - { - private static readonly string _tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - private readonly string _path; - - public CscBasedCompilationService() - { - _path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), - @"Microsoft.NET\Framework\v4.0.30319\csc.exe"); - } - - public async Task Compile(string contents) - { - Directory.CreateDirectory(_tempDir); - string inFile = Path.Combine(_tempDir, Path.GetRandomFileName() + ".cs"); - string outFile = Path.Combine(_tempDir, Path.GetRandomFileName() + ".dll"); - StringBuilder args = new StringBuilder("/target:library "); - args.AppendFormat("/out:\"{0}\" ", outFile); - - string binDir = Path.Combine(Directory.GetCurrentDirectory(), "bin"); - // In the k-world, CurrentDir happens to be the bin dir - binDir = Directory.Exists(binDir) ? binDir : Directory.GetCurrentDirectory(); - foreach (var file in Directory.EnumerateFiles(binDir, "*.dll")) - { - args.AppendFormat("/R:\"{0}\" ", file); - } - args.AppendFormat("\"{0}\"", inFile); - var outputStream = new MemoryStream(); - - // common execute - Process process = CreateProcess(args.ToString()); - int exitCode; - try - { - File.WriteAllText(inFile, contents); - exitCode = await Start(process, outputStream); - } - finally - { - File.Delete(inFile); - } - - - string output = GetString(outputStream); - if (exitCode != 0) - { - IEnumerable messages = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) - .Skip(3) - .Select(e => new CompilationMessage(e)); - return CompilationResult.Failed(String.Empty, messages); - } - - var type = Assembly.LoadFrom(outFile) - .GetExportedTypes() - .First(); - return CompilationResult.Successful(String.Empty, type); - } - - - private string GetString(MemoryStream stream) - { - if (stream.Length > 0) - { - return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); - } - - return String.Empty; - } - - private static async Task Start(Process process, Stream output) - { - var tcs = new TaskCompletionSource(); - process.EnableRaisingEvents = true; - process.Exited += (sender, eventArgs) => - { - tcs.SetResult(process.ExitCode); - }; - - process.Start(); - - var copyTask = process.StandardOutput.BaseStream.CopyToAsync(output); - await Task.WhenAll(tcs.Task, copyTask); - - return process.ExitCode; - } - - internal Process CreateProcess(string arguments) - { - var psi = new ProcessStartInfo - { - FileName = _path, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = false, - ErrorDialog = false, - Arguments = arguments - }; - - psi.StandardOutputEncoding = Encoding.UTF8; - psi.StandardErrorEncoding = Encoding.UTF8; - - var process = new Process() - { - StartInfo = psi - }; - - return process; - } - } -} -#endif \ 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 977bf5501e..15ffb72aba 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -6,23 +7,22 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Net.Runtime; -using Microsoft.Net.Runtime.Services; namespace Microsoft.AspNet.Mvc.Razor.Compilation { public class RoslynCompilationService : ICompilationService { - private readonly IMetadataReferenceProvider _provider; + private readonly IDependencyExporter _exporter; private readonly IApplicationEnvironment _environment; private readonly IAssemblyLoaderEngine _loader; - public RoslynCompilationService(IServiceProvider serviceProvider) + public RoslynCompilationService(IApplicationEnvironment environment, + IAssemblyLoaderEngine loaderEngine, + IDependencyExporter exporter) { - // TODO: Get these services via ctor injection when we get container chaining implemented - _provider = (IMetadataReferenceProvider)serviceProvider.GetService(typeof(IMetadataReferenceProvider)); - _environment = (IApplicationEnvironment)serviceProvider.GetService(typeof(IApplicationEnvironment)); - _loader = (IAssemblyLoaderEngine)serviceProvider.GetService(typeof(IAssemblyLoaderEngine)); - + _environment = environment; + _loader = loaderEngine; + _exporter = exporter; } public Task Compile(string content) @@ -30,15 +30,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(content) }; var targetFramework = _environment.TargetFramework; - // Get references from the application itself and from - // this assembly for the base types etc - var referenceNames = new[] { - _environment.ApplicationName, - typeof(RoslynCompilationService).GetTypeInfo().Assembly.GetName().Name - }; - - var references = referenceNames.SelectMany(name => _provider.GetReferences(name, targetFramework)) - .Cast(); + var references = GetApplicationReferences().ToList(); var assemblyName = Path.GetRandomFileName(); @@ -47,24 +39,75 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation syntaxTrees: syntaxTrees, references: references); - var ms = new MemoryStream(); - var result = compilation.Emit(ms); - - if (!result.Success) + using (var ms = new MemoryStream()) { - var formatter = new DiagnosticFormatter(); + using (var pdb = new MemoryStream()) + { + var result = compilation.Emit(ms, pdbStream: pdb); - var messages = result.Diagnostics.Where(IsError).Select(d => GetCompilationMessage(formatter, d)); + if (!result.Success) + { + var formatter = new DiagnosticFormatter(); - return Task.FromResult(CompilationResult.Failed(content, messages)); + var messages = result.Diagnostics.Where(IsError).Select(d => GetCompilationMessage(formatter, d)); + + return Task.FromResult(CompilationResult.Failed(content, messages)); + } + + var type = _loader.LoadBytes(ms.ToArray(), pdb.ToArray()) + .GetExportedTypes() + .First(); + + return Task.FromResult(CompilationResult.Successful(String.Empty, type)); + } + } + } + + private IEnumerable GetApplicationReferences() + { + var assemblyNames = new[] { + _environment.ApplicationName, + typeof(RoslynCompilationService).GetTypeInfo().Assembly.GetName().Name + }; + + return assemblyNames.Select(a => _exporter.GetDependencyExport(a, _environment.TargetFramework)) + .SelectMany(e => e.MetadataReferences.SelectMany(ConvertMetadataReference)); + } + + private IEnumerable ConvertMetadataReference(IMetadataReference metadataReference) + { + var fileMetadataReference = metadataReference as IMetadataFileReference; + + if (fileMetadataReference != null) + { + string path = fileMetadataReference.Path; +#if NET45 + return new[] { new MetadataFileReference(path) }; +#else + // TODO: What about access to the file system? We need to be able to + // read files from anywhere on disk, not just under the web root + using (var stream = File.OpenRead(path)) + { + return new[] { new MetadataImageReference(stream) }; + } +#endif } - // TODO: Flow loader to this code so we're not using Load() directly - var type = _loader.LoadBytes(ms.ToArray(), null) - .GetExportedTypes() - .First(); + var roslynReference = metadataReference as IRoslynMetadataReference; - return Task.FromResult(CompilationResult.Successful(String.Empty, type)); + if (roslynReference != null) + { + // REVIEW: We should really only compile against the app's closure + var compilatonReference = roslynReference.MetadataReference as CompilationReference; + if (compilatonReference != null) + { + return new[] { compilatonReference }.Concat(compilatonReference.Compilation.References); + } + + return new[] { roslynReference.MetadataReference }; + } + + throw new NotSupportedException(); } private CompilationMessage GetCompilationMessage(DiagnosticFormatter formatter, Diagnostic diagnostic) diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IApplicationEnvironment.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IApplicationEnvironment.cs index 09a1a39f1e..c7514da6ef 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Services/IApplicationEnvironment.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IApplicationEnvironment.cs @@ -1,5 +1,4 @@ using System.Runtime.Versioning; -using Microsoft.Net.Runtime.Services; namespace Microsoft.Net.Runtime { diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IAssemblyLoaderEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IAssemblyLoaderEngine.cs index 2432bff62a..0a47b08aff 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Services/IAssemblyLoaderEngine.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IAssemblyLoaderEngine.cs @@ -1,5 +1,4 @@ using System.Reflection; -using Microsoft.Net.Runtime.Services; namespace Microsoft.Net.Runtime { diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExport.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExport.cs new file mode 100644 index 0000000000..1f5aca13b4 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExport.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IDependencyExport + { + IList MetadataReferences { get; } + IList SourceReferences { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExporter.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExporter.cs new file mode 100644 index 0000000000..bfe702cd81 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IDependencyExporter.cs @@ -0,0 +1,10 @@ +using System.Runtime.Versioning; + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IDependencyExporter + { + IDependencyExport GetDependencyExport(string name, FrameworkName targetFramework); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataFileReference.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataFileReference.cs new file mode 100644 index 0000000000..ca63379413 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataFileReference.cs @@ -0,0 +1,8 @@ +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IMetadataFileReference : IMetadataReference + { + string Path { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReference.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReference.cs new file mode 100644 index 0000000000..6f011eb3b0 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReference.cs @@ -0,0 +1,8 @@ + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IMetadataReference + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReferenceProvider.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReferenceProvider.cs index b78dd62d7a..d61b5743f7 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReferenceProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IMetadataReferenceProvider.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Runtime.Versioning; -namespace Microsoft.Net.Runtime.Services +namespace Microsoft.Net.Runtime { [AssemblyNeutral] public interface IMetadataReferenceProvider diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/IRoslynMetadataReference.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/IRoslynMetadataReference.cs new file mode 100644 index 0000000000..66b797ed7a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/IRoslynMetadataReference.cs @@ -0,0 +1,10 @@ +using Microsoft.CodeAnalysis; + +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface IRoslynMetadataReference : IMetadataReference + { + MetadataReference MetadataReference { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Services/ISourceReference.cs b/src/Microsoft.AspNet.Mvc.Razor/Services/ISourceReference.cs new file mode 100644 index 0000000000..cfb404e908 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Services/ISourceReference.cs @@ -0,0 +1,7 @@ +namespace Microsoft.Net.Runtime +{ + [AssemblyNeutral] + public interface ISourceReference + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs index 69c90fd628..4301988418 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/PathBasedViewFactory.cs @@ -1,24 +1,27 @@ using System; using System.Threading.Tasks; using Microsoft.AspNet.FileSystems; +using Microsoft.Net.Runtime; namespace Microsoft.AspNet.Mvc.Razor { public class VirtualPathViewFactory : IVirtualPathViewFactory { - private readonly IFileSystem _fileSystem; + private readonly PhysicalFileSystem _fileSystem; private readonly IRazorCompilationService _compilationService; - public VirtualPathViewFactory(IFileSystem fileSystem, IRazorCompilationService compilationService) + public VirtualPathViewFactory(IApplicationEnvironment env, IRazorCompilationService compilationService) { - _fileSystem = fileSystem; + // TODO: Continue to inject the IFileSystem but only when we get it from the host + _fileSystem = new PhysicalFileSystem(env.ApplicationBasePath); _compilationService = compilationService; } public async Task CreateInstance(string virtualPath) { // TODO: We need to glean the approot from HttpContext - var appRoot = ((PhysicalFileSystem)_fileSystem).Root; + var appRoot = _fileSystem.Root; + IFileInfo fileInfo; if (_fileSystem.TryGetFileInfo(virtualPath, out fileInfo)) { diff --git a/src/Microsoft.AspNet.Mvc.Razor/project.json b/src/Microsoft.AspNet.Mvc.Razor/project.json index 481924a39b..6ba81eff35 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/project.json +++ b/src/Microsoft.AspNet.Mvc.Razor/project.json @@ -35,6 +35,7 @@ "System.Dynamic.Runtime": "4.0.0.0", "System.Globalization": "4.0.10.0", "System.IO": "4.0.0.0", + "System.IO.FileSystem": "4.0.0.0", "System.Linq": "4.0.0.0", "System.Reflection": "4.0.10.0", "System.Reflection.Compatibility": "4.0.0.0", diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 4f83d0e17f..26bb98a685 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -1,20 +1,17 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Microsoft.AspNet.ConfigurationModel; using Microsoft.AspNet.DependencyInjection; using Microsoft.AspNet.DependencyInjection.NestedProviders; -using Microsoft.AspNet.FileSystems; using Microsoft.AspNet.Mvc.Filters; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Razor; -using Microsoft.Net.Runtime; +using Microsoft.AspNet.Mvc.Razor.Compilation; namespace Microsoft.AspNet.Mvc { public class MvcServices { - public static IEnumerable GetDefaultServices(IConfiguration configuration, - IApplicationEnvironment env) + public static IEnumerable GetDefaultServices(IConfiguration configuration) { var describe = new ServiceDescriber(configuration); @@ -27,19 +24,11 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); yield return describe.Transient(); yield return describe.Transient(); - yield return describe.Instance(new PhysicalFileSystem(env.ApplicationBasePath)); yield return describe.Instance(new MvcRazorHost(typeof(RazorView).FullName)); -#if NET45 - // TODO: Container chaining to flow services from the host to this container + yield return describe.Transient(); - yield return describe.Transient(); - - // TODO: Make this work like normal when we get container chaining - // TODO: Update this when we have the new host services - // yield return describe.Instance(new RoslynCompilationService(hostServiceProvider)); -#endif yield return describe.Transient(); yield return describe.Transient(); yield return describe.Transient();