diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design/Internal/PrecompileRunCommand.cs b/src/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design/Internal/PrecompileRunCommand.cs index 9062ca3317..b68ef54157 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design/Internal/PrecompileRunCommand.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design/Internal/PrecompileRunCommand.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.CodeAnalysis; @@ -104,7 +103,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Internal var resources = GetResources(results); var assemblyPath = Path.Combine(OutputPathOption.Value(), precompileAssemblyName + ".dll"); - var emitResult = EmitAssembly(compilation, assemblyPath, resources); + var emitResult = EmitAssembly( + compilation, + MvcServiceProvider.Compiler.EmitOptions, + assemblyPath, + resources); if (!emitResult.Success) { @@ -143,23 +146,41 @@ namespace Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Internal return resources; } - private EmitResult EmitAssembly( + public EmitResult EmitAssembly( CSharpCompilation compilation, + EmitOptions emitOptions, string assemblyPath, ResourceDescription[] resources) { - Directory.CreateDirectory(Path.GetDirectoryName(assemblyPath)); - EmitResult emitResult; - using (var assemblyStream = File.OpenWrite(assemblyPath)) + using (var assemblyStream = new MemoryStream()) { - using (var pdbStream = File.OpenWrite(Path.ChangeExtension(assemblyPath, ".pdb"))) + using (var pdbStream = new MemoryStream()) { emitResult = compilation.Emit( assemblyStream, pdbStream, manifestResources: resources, - options: MvcServiceProvider.Compiler.EmitOptions); + options: emitOptions); + + if (emitResult.Success) + { + Directory.CreateDirectory(Path.GetDirectoryName(assemblyPath)); + var pdbPath = Path.ChangeExtension(assemblyPath, ".pdb"); + assemblyStream.Position = 0; + pdbStream.Position = 0; + + // Avoid writing to disk unless the compilation is successful. + using (var assemblyFileStream = File.OpenWrite(assemblyPath)) + { + assemblyStream.CopyTo(assemblyFileStream); + } + + using (var pdbFileStream = File.OpenWrite(pdbPath)) + { + pdbStream.CopyTo(pdbFileStream); + } + } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Test/PrecompileRunCommandTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Test/PrecompileRunCommandTest.cs index 5a4408e6cd..8493d81934 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Test/PrecompileRunCommandTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Test/PrecompileRunCommandTest.cs @@ -6,6 +6,9 @@ using System.IO; using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design.Internal; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; using Xunit; namespace Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Tools @@ -150,6 +153,29 @@ Options: Assert.Equal(expectedError, result.Error.Trim()); } + [Fact] + public void EmitAssembly_DoesNotWriteAssembliesToDisk_IfCompilationFails() + { + // Arrange + var assemblyDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var assemblyPath = Path.Combine(assemblyDirectory, "out.dll"); + var precompileRunCommand = new PrecompileRunCommand(); + var syntaxTree = CSharpSyntaxTree.ParseText("using Microsoft.DoestNotExist"); + var compilation = CSharpCompilation.Create("Test.dll", new[] { syntaxTree }); + + // Act + var emitResult = precompileRunCommand.EmitAssembly( + compilation, + new EmitOptions(), + assemblyPath, + new ResourceDescription[0]); + + // Assert + Assert.False(emitResult.Success); + Assert.False(Directory.Exists(assemblyDirectory)); + Assert.False(File.Exists(assemblyPath)); + } + private static string GetToolVersion() { return typeof(Program)