未验证 提交 f6a92c04 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Fix EnC for multi-targeted projects that emit embedded PDBs (#39178)

上级 9f0d3fcf
...@@ -785,6 +785,8 @@ void Emit() ...@@ -785,6 +785,8 @@ void Emit()
} }
else else
{ {
// Report diagnosics even when the module is never going to be loaded (e.g. in multi-targeting scenario, where only one framework being debugged).
// This is consistent with reporting compilation errors - the IDE reports them for all TFMs regardless of what framework the app is running on.
diagnostics.Add((project.Id, createBaselineDiagnostics)); diagnostics.Add((project.Id, createBaselineDiagnostics));
Telemetry.LogProjectAnalysisSummary(projectSummary, createBaselineDiagnostics); Telemetry.LogProjectAnalysisSummary(projectSummary, createBaselineDiagnostics);
isBlocked = true; isBlocked = true;
......
...@@ -20,24 +20,28 @@ public void OpenStream_Errors() ...@@ -20,24 +20,28 @@ public void OpenStream_Errors()
} }
[Theory] [Theory]
[InlineData(true)] [InlineData(DebugInformationFormat.PortablePdb, true)]
[InlineData(false)] [InlineData(DebugInformationFormat.PortablePdb, false)]
public void AssemblyAndPdb(bool exactPdbPath) [InlineData(DebugInformationFormat.Embedded, false)]
public void AssemblyAndPdb(DebugInformationFormat pdbFormat, bool exactPdbPath)
{ {
var dir = Temp.CreateDirectory(); var dir = Temp.CreateDirectory();
var dllFile = dir.CreateFile("lib.dll"); var dllFile = dir.CreateFile("lib.dll");
var pdbFile = dir.CreateFile("lib.pdb"); var pdbFile = (pdbFormat == DebugInformationFormat.Embedded) ? null : dir.CreateFile("lib.pdb");
var source = @"class C { public static void Main() { int x = 1; } }"; var source = @"class C { public static void Main() { int x = 1; } }";
var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, assemblyName: "lib"); var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, assemblyName: "lib");
var pdbStream = new MemoryStream(); var pdbStream = (pdbFile != null) ? new MemoryStream() : null;
var debugDirPdbPath = exactPdbPath ? pdbFile.Path : "a/y/z/lib.pdb"; var debugDirPdbPath = exactPdbPath ? pdbFile.Path : "a/y/z/lib.pdb";
var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream); var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: pdbFormat, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream);
pdbStream.Position = 0;
dllFile.WriteAllBytes(peImage); dllFile.WriteAllBytes(peImage);
pdbFile.WriteAllBytes(pdbStream.ToArray());
if (pdbFile != null)
{
pdbStream.Position = 0;
pdbFile.WriteAllBytes(pdbStream.ToArray());
}
var outputs = new CompilationOutputFilesWithImplicitPdbPath(dllFile.Path); var outputs = new CompilationOutputFilesWithImplicitPdbPath(dllFile.Path);
...@@ -59,5 +63,43 @@ public void AssemblyAndPdb(bool exactPdbPath) ...@@ -59,5 +63,43 @@ public void AssemblyAndPdb(bool exactPdbPath)
Directory.Delete(dir.Path, recursive: true); Directory.Delete(dir.Path, recursive: true);
} }
[Fact]
public void AssemblyFileNotFound()
{
var dir = Temp.CreateDirectory();
var outputs = new CompilationOutputFilesWithImplicitPdbPath(Path.Combine(dir.Path, "nonexistent.dll"));
Assert.Null(outputs.OpenPdb());
Assert.Null(outputs.OpenAssemblyMetadata(prefetch: false));
}
[Fact]
public void PdbFileNotFound()
{
var dir = Temp.CreateDirectory();
var dllFile = dir.CreateFile("lib.dll");
var source = @"class C { public static void Main() { int x = 1; } }";
var compilation = CSharpTestBase.CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.DebugDll, assemblyName: "lib");
var pdbStream = new MemoryStream();
var debugDirPdbPath = Path.Combine(dir.Path, "nonexistent.pdb");
var peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb, pdbFilePath: debugDirPdbPath), pdbStream: pdbStream);
pdbStream.Position = 0;
dllFile.WriteAllBytes(peImage);
var outputs = new CompilationOutputFilesWithImplicitPdbPath(dllFile.Path);
Assert.Null(outputs.OpenPdb());
using (var metadata = outputs.OpenAssemblyMetadata(prefetch: false))
{
var mdReader = metadata.GetMetadataReader();
Assert.Equal("lib", mdReader.GetString(mdReader.GetAssemblyDefinition().Name));
}
// make sure all files are closed and can be deleted
Directory.Delete(dir.Path, recursive: true);
}
} }
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Emit;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -27,12 +28,18 @@ public CompilationOutputFilesWithImplicitPdbPath(string assemblyFilePath = null) ...@@ -27,12 +28,18 @@ public CompilationOutputFilesWithImplicitPdbPath(string assemblyFilePath = null)
} }
public override string AssemblyDisplayPath => 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"; public override string PdbDisplayPath => Path.GetFileNameWithoutExtension(AssemblyFilePath) + ".pdb";
protected override Stream OpenAssemblyStream() protected override Stream OpenAssemblyStream()
=> AssemblyFilePath != null ? FileUtilities.OpenRead(AssemblyFilePath) : null; => TryOpenFileStream(AssemblyFilePath);
// Not gonna be called since we override OpenPdb.
protected override Stream OpenPdbStream() protected override Stream OpenPdbStream()
=> throw ExceptionUtilities.Unreachable;
public override DebugInformationReaderProvider OpenPdb()
{ {
var assemblyStream = OpenAssemblyStream(); var assemblyStream = OpenAssemblyStream();
if (assemblyStream == null) if (assemblyStream == null)
...@@ -44,30 +51,49 @@ protected override Stream OpenPdbStream() ...@@ -44,30 +51,49 @@ protected override Stream OpenPdbStream()
string pdbPath; string pdbPath;
using (var peReader = new PEReader(assemblyStream)) using (var peReader = new PEReader(assemblyStream))
{ {
var pdbEntry = peReader.ReadDebugDirectory().FirstOrDefault( var debugDirectory = peReader.ReadDebugDirectory();
e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb || e.Type == DebugDirectoryEntryType.CodeView); var embeddedPdbEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
if (embeddedPdbEntry.DataSize != 0)
{
return DebugInformationReaderProvider.CreateFromMetadataReader(peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry));
}
if (pdbEntry.Type != DebugDirectoryEntryType.CodeView) var codeViewEntry = debugDirectory.FirstOrDefault(e => e.Type == DebugDirectoryEntryType.CodeView);
if (codeViewEntry.DataSize == 0)
{ {
return null; return null;
} }
pdbPath = peReader.ReadCodeViewDebugDirectoryData(pdbEntry).Path; pdbPath = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry).Path;
} }
// First try to use the full path as specified in the PDB, then look next to the assembly. // First try to use the full path as specified in the PDB, then look next to the assembly.
Stream result; 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 try
{ {
result = new FileStream(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read); return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
} }
catch (Exception e) when (e is FileNotFoundException || e is DirectoryNotFoundException) catch (Exception e) when (e is FileNotFoundException || e is DirectoryNotFoundException)
{ {
pdbPath = Path.Combine(Path.GetDirectoryName(AssemblyFilePath), PathUtilities.GetFileName(pdbPath)); return null;
result = FileUtilities.OpenRead(pdbPath); }
catch (Exception e) when (!(e is IOException))
{
throw new IOException(e.Message, e);
} }
return result;
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册