| File: Emit\CompilationOutputFilesWithImplicitPdbPath.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// 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.IO; using System.Linq; using System.Reflection.PortableExecutable; using Microsoft.CodeAnalysis.Debugging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Emit; /// <summary> /// Provides access to compilation outputs based only on the path of the output asssembly. /// If PDB path is known upfront use <see cref="CompilationOutputFiles"/> instead. /// </summary> internal sealed class CompilationOutputFilesWithImplicitPdbPath : CompilationOutputs { public string? AssemblyFilePath { get; } public CompilationOutputFilesWithImplicitPdbPath(string? assemblyFilePath = null) { if (assemblyFilePath != null) { CompilerPathUtilities.RequireAbsolutePath(assemblyFilePath, nameof(assemblyFilePath)); } AssemblyFilePath = assemblyFilePath; } public override string? AssemblyDisplayPath => AssemblyFilePath; // heuristic for error messages (determining the actual path requires opening the assembly): public override string PdbDisplayPath => Path.GetFileNameWithoutExtension(AssemblyFilePath) + ".pdb"; protected override Stream? OpenAssemblyStream() => TryOpenFileStream(AssemblyFilePath); // Not gonna be called since we override OpenPdb. protected override Stream OpenPdbStream() => throw ExceptionUtilities.Unreachable(); public override DebugInformationReaderProvider? OpenPdb() { var assemblyStream = OpenAssemblyStream(); if (assemblyStream == null) { return null; } // find associated PDB string pdbPath; using (var peReader = new PEReader(assemblyStream)) { var debugDirectory = peReader.ReadDebugDirectory(); var embeddedPdbEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb); if (embeddedPdbEntry.DataSize != 0) { return DebugInformationReaderProvider.CreateFromMetadataReader(peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry)); } var codeViewEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.CodeView); if (codeViewEntry.DataSize == 0) { return null; } pdbPath = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry).Path; } // First try to use the full path as specified in the PDB, then look next to the assembly. var pdbStream = TryOpenFileStream(pdbPath) ?? TryOpenFileStream(Path.Combine(Path.GetDirectoryName(AssemblyFilePath)!, PathUtilities.GetFileName(pdbPath))); return (pdbStream != null) ? DebugInformationReaderProvider.CreateFromStream(pdbStream) : null; } private static Stream? TryOpenFileStream(string? path) { if (path == null) { return null; } try { return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); } catch (Exception e) when (e is FileNotFoundException or DirectoryNotFoundException) { return null; } catch (Exception e) when (e is not IOException) { throw new IOException(e.Message, e); } } }