| File: CommandLine\CommonCompiler.CompilerEmitStreamProvider.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics; using System.IO; using System.Linq; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { internal abstract partial class CommonCompiler { /// <summary> /// This implementation of <see cref="Compilation.EmitStreamProvider"/> will delay the creation /// of the PE / PDB file until the compiler determines the compilation has succeeded. This prevents /// the compiler from deleting output from the previous compilation when a new compilation /// fails. The <see cref="Close"/> method must be called to retrieve all diagnostics. /// </summary> private sealed class CompilerEmitStreamProvider : Compilation.EmitStreamProvider { private readonly CommonCompiler _compiler; private readonly string _filePath; private Stream? _streamToDispose; internal CompilerEmitStreamProvider( CommonCompiler compiler, string filePath) { _compiler = compiler; _filePath = filePath; } public void Close(DiagnosticBag diagnostics) { try { _streamToDispose?.Dispose(); } catch (Exception e) { var messageProvider = _compiler.MessageProvider; var diagnosticInfo = new DiagnosticInfo(messageProvider, messageProvider.ERR_OutputWriteFailed, _filePath, e.Message); diagnostics.Add(messageProvider.CreateDiagnostic(diagnosticInfo)); } } protected override Stream? CreateStream(DiagnosticBag diagnostics) { Debug.Assert(_streamToDispose == null); try { try { return OpenFileStream(); } catch (IOException e) { // Other process is reading the file preventing us to write to it. // We attempt to rename and delete the file in case the reader opened it with FileShare.Delete flag that // allows the file to be deleted by other processes. // // Note that if the file is marked "readonly" or the current user doesn't have sufficient privileges // the exception thrown is UnauthorizedAccessException, not IOException, so we won't attempt to delete the file. try { const int eWin32SharingViolation = unchecked((int)0x80070020); if (PathUtilities.IsUnixLikePlatform) { // Unix & Mac are simple: just delete the file in the directory. // The memory mapped content remains available for the reader. File.Delete(_filePath); } else if (e.HResult == eWin32SharingViolation) { // On Windows File.Delete only marks the file for deletion, but doesn't remove it from the directory. var newFilePath = Path.Combine(Path.GetDirectoryName(_filePath)!, Guid.NewGuid().ToString() + "_" + Path.GetFileName(_filePath)); // Try to rename the existing file. This fails unless the file is open with FileShare.Delete. File.Move(_filePath, newFilePath); // hide the renamed file: File.SetAttributes(newFilePath, FileAttributes.Hidden); // Mark the renamed file for deletion, so that it's deleted as soon as the current reader is finished reading it File.Delete(newFilePath); } } catch { // report the original exception ReportOpenFileDiagnostic(diagnostics, e); return null; } return OpenFileStream(); } } catch (Exception e) { ReportOpenFileDiagnostic(diagnostics, e); return null; } } private Stream OpenFileStream() { return _streamToDispose = _compiler.FileSystem.OpenFile(_filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None); } private void ReportOpenFileDiagnostic(DiagnosticBag diagnostics, Exception e) { var messageProvider = _compiler.MessageProvider; var lockedBy = FileLockCheck.TryGetLockingProcessInfos(_filePath); var message = lockedBy.IsEmpty ? (object)e.Message : new LocalizableResourceString( nameof(CodeAnalysisResources.ExceptionMessage_FileMayBeLockedBy), CodeAnalysisResources.ResourceManager, typeof(CodeAnalysisResources), e.Message, string.Join(", ", lockedBy.Select(info => $"'{info.applicationName}' ({info.processId})"))); diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_CantOpenFileWrite, Location.None, _filePath, message)); } } } }