Skip to content

Commit

Permalink
Make compilation outputs available via a workspace service (#34809)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat authored Apr 29, 2019
1 parent b252adb commit 3bdbd56
Show file tree
Hide file tree
Showing 46 changed files with 1,113 additions and 138 deletions.
23 changes: 21 additions & 2 deletions src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1792,30 +1792,35 @@ public void Debug()
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.False(parsedArgs.EmitPdb);
Assert.False(parsedArgs.EmitPdbFile);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

parsedArgs = DefaultParse(new[] { "/debug-", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.False(parsedArgs.EmitPdb);
Assert.False(parsedArgs.EmitPdbFile);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

parsedArgs = DefaultParse(new[] { "/debug", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.True(parsedArgs.EmitPdb);
Assert.True(parsedArgs.EmitPdbFile);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

parsedArgs = DefaultParse(new[] { "/debug+", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.True(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.True(parsedArgs.EmitPdb);
Assert.True(parsedArgs.EmitPdbFile);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

parsedArgs = DefaultParse(new[] { "/debug+", "/debug-", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.False(parsedArgs.EmitPdb);
Assert.False(parsedArgs.EmitPdbFile);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);

parsedArgs = DefaultParse(new[] { "/debug:full", "a.cs" }, WorkingDirectory);
Expand All @@ -1829,6 +1834,7 @@ public void Debug()
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, platformPdbKind);
Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Expand All @@ -1841,12 +1847,14 @@ public void Debug()
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, DebugInformationFormat.PortablePdb);
Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

parsedArgs = DefaultParse(new[] { "/debug:embedded", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.CompilationOptions.DebugPlusMode);
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(parsedArgs.EmitOptions.DebugInformationFormat, DebugInformationFormat.Embedded);
Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

parsedArgs = DefaultParse(new[] { "/debug:PDBONLY", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Expand Down Expand Up @@ -1914,14 +1922,23 @@ public void Pdb()
{
var parsedArgs = DefaultParse(new[] { "/pdb:something", "a.cs" }, WorkingDirectory);
Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.PdbPath);
Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.GetPdbFilePath("a.dll"));
Assert.False(parsedArgs.EmitPdbFile);

// No pdb
parsedArgs = DefaultParse(new[] { @"/debug", "a.cs" }, WorkingDirectory);
parsedArgs = DefaultParse(new[] { "/pdb:something", "/debug:embedded", "a.cs" }, WorkingDirectory);
Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.PdbPath);
Assert.Equal(Path.Combine(WorkingDirectory, "something.pdb"), parsedArgs.GetPdbFilePath("a.dll"));
Assert.False(parsedArgs.EmitPdbFile);

parsedArgs = DefaultParse(new[] { "/debug", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Null(parsedArgs.PdbPath);
Assert.True(parsedArgs.EmitPdbFile);
Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

parsedArgs = DefaultParse(new[] { "/pdb", "/debug", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/pdb"));
Assert.Equal(Path.Combine(WorkingDirectory, "a.pdb"), parsedArgs.GetPdbFilePath("a.dll"));

parsedArgs = DefaultParse(new[] { "/pdb:", "/debug", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/pdb:"));
Expand Down Expand Up @@ -3312,6 +3329,7 @@ public void ParseOut()
Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
Assert.Equal(@"C:\MyFolder", parsedArgs.OutputDirectory);
Assert.Equal(@"C:\MyFolder\MyBinary.dll", parsedArgs.GetOutputFilePath(parsedArgs.OutputFileName));

// Should handle quotes
parsedArgs = DefaultParse(new[] { @"/out:""C:\My Folder\MyBinary.dll""", "a.cs" }, baseDirectory);
Expand All @@ -3328,6 +3346,7 @@ public void ParseOut()
Assert.Equal("MyBinary.dll", parsedArgs.OutputFileName);
Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName);
Assert.Equal(baseDirectory, parsedArgs.OutputDirectory);
Assert.Equal(Path.Combine(baseDirectory, "MyBinary.dll"), parsedArgs.GetOutputFilePath(parsedArgs.OutputFileName));

// Should expand partially qualified paths
parsedArgs = DefaultParse(new[] { @"/out:..\MyBinary.dll", "a.cs" }, baseDirectory);
Expand Down
45 changes: 45 additions & 0 deletions src/Compilers/Core/Portable/CommandLine/CommandLineArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,51 @@ internal CommandLineArguments()
{
}

/// <summary>
/// Returns a full path of the file that the compiler will generate the assembly to if compilation succeeds.
/// </summary>
/// <remarks>
/// The method takes <paramref name="outputFileName"/> rather than using the value of <see cref="OutputFileName"/>
/// since the latter might be unspecified, in which case actual output path can't be determined for C# command line
/// without creating a compilation and finding an entry point. VB does not allow <see cref="OutputFileName"/> to
/// be unspecified.
/// </remarks>
public string GetOutputFilePath(string outputFileName)
{
if (outputFileName == null)
{
throw new ArgumentNullException(nameof(outputFileName));
}

return Path.Combine(OutputDirectory, outputFileName);
}

/// <summary>
/// Returns a full path of the PDB file that the compiler will generate the debug symbols to
/// if <see cref="EmitPdbFile"/> is true and the compilation succeeds.
/// </summary>
/// <remarks>
/// The method takes <paramref name="outputFileName"/> rather than using the value of <see cref="OutputFileName"/>
/// since the latter might be unspecified, in which case actual output path can't be determined for C# command line
/// without creating a compilation and finding an entry point. VB does not allow <see cref="OutputFileName"/> to
/// be unspecified.
/// </remarks>
public string GetPdbFilePath(string outputFileName)
{
if (outputFileName == null)
{
throw new ArgumentNullException(nameof(outputFileName));
}

return PdbPath ?? Path.Combine(OutputDirectory, Path.ChangeExtension(outputFileName, ".pdb"));
}

/// <summary>
/// Returns true if the PDB is generated to a PDB file, as opposed to embedded to the output binary and not generated at all.
/// </summary>
public bool EmitPdbFile
=> EmitPdb && EmitOptions.DebugInformationFormat != DebugInformationFormat.Embedded;

#region Metadata References

/// <summary>
Expand Down
8 changes: 3 additions & 5 deletions src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -870,8 +870,8 @@ private void CompileAndEmit(
cancellationToken.ThrowIfCancellationRequested();

string outputName = GetOutputFileName(compilation, cancellationToken);
var finalPeFilePath = Path.Combine(Arguments.OutputDirectory, outputName);
var finalPdbFilePath = Arguments.PdbPath ?? Path.ChangeExtension(finalPeFilePath, ".pdb");
var finalPeFilePath = Arguments.GetOutputFilePath(outputName);
var finalPdbFilePath = Arguments.GetPdbFilePath(outputName);
var finalXmlFilePath = Arguments.DocumentationPath;

NoThrowStreamDisposer sourceLinkStreamDisposerOpt = null;
Expand Down Expand Up @@ -1023,10 +1023,8 @@ private void CompileAndEmit(

if (success)
{
bool emitPdbFile = Arguments.EmitPdb && emitOptions.DebugInformationFormat != Emit.DebugInformationFormat.Embedded;

var peStreamProvider = new CompilerEmitStreamProvider(this, finalPeFilePath);
var pdbStreamProviderOpt = emitPdbFile ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null;
var pdbStreamProviderOpt = Arguments.EmitPdbFile ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null;

string finalRefPeFilePath = Arguments.OutputRefFilePath;
var refPeStreamProviderOpt = finalRefPeFilePath != null ? new CompilerEmitStreamProvider(this, finalRefPeFilePath) : null;
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
*REMOVED*Microsoft.CodeAnalysis.Operations.IEventAssignmentOperation.EventReference.get -> Microsoft.CodeAnalysis.Operations.IEventReferenceOperation
*REMOVED*Microsoft.CodeAnalysis.SpecialType.Count = 43 -> Microsoft.CodeAnalysis.SpecialType
Microsoft.CodeAnalysis.CommandLineArguments.EmitPdbFile.get -> bool
Microsoft.CodeAnalysis.CommandLineArguments.GetOutputFilePath(string outputFileName) -> string
Microsoft.CodeAnalysis.CommandLineArguments.GetPdbFilePath(string outputFileName) -> string
Microsoft.CodeAnalysis.IArrayTypeSymbol.ElementNullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
Microsoft.CodeAnalysis.IDiscardSymbol.NullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
Microsoft.CodeAnalysis.IEventSymbol.NullableAnnotation.get -> Microsoft.CodeAnalysis.NullableAnnotation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Debugging.UnitTests
{
public class DebugInformationReaderProviderTests
{
[Fact]
public void CreateFrom_Errors()
{
Assert.Throws<ArgumentException>(() => DebugInformationReaderProvider.CreateFromStream(new TestStream(canRead: false, canSeek: true, canWrite: true)));
Assert.Throws<ArgumentException>(() => DebugInformationReaderProvider.CreateFromStream(new TestStream(canRead: true, canSeek: false, canWrite: true)));
Assert.Throws<ArgumentNullException>(() => DebugInformationReaderProvider.CreateFromStream(null));
Assert.Throws<ArgumentNullException>(() => DebugInformationReaderProvider.CreateFromMetadataReader(null));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UnitTests;
using Microsoft.DiaSymReader;
using Microsoft.DiaSymReader.PortablePdb;
using Moq;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue
namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests
{
public class EditAndContinueMethodDebugInfoReaderTests
{
Expand All @@ -22,26 +23,24 @@ private class DummyMetadataImportProvider : IMetadataImportProvider
public object GetMetadataImport() => throw new NotImplementedException();
}

private class DummySymReaderMetadataProvider : ISymReaderMetadataProvider
[Fact]
public void Create_Errors()
{
public unsafe bool TryGetStandaloneSignature(int standaloneSignatureToken, out byte* signature, out int length)
=> throw new NotImplementedException();
Assert.Throws<ArgumentNullException>(() => EditAndContinueMethodDebugInfoReader.Create((ISymUnmanagedReader5)null));
Assert.Throws<ArgumentNullException>(() => EditAndContinueMethodDebugInfoReader.Create((MetadataReader)null));
Assert.Throws<ArgumentNullException>(() => EditAndContinueMethodDebugInfoReader.Create(null, 1));

public bool TryGetTypeDefinitionInfo(int typeDefinitionToken, out string namespaceName, out string typeName, out TypeAttributes attributes)
=> throw new NotImplementedException();

public bool TryGetTypeReferenceInfo(int typeReferenceToken, out string namespaceName, out string typeName)
=> throw new NotImplementedException();
var mockSymReader = new Mock<ISymUnmanagedReader5>().Object;
Assert.Throws<ArgumentOutOfRangeException>(() => EditAndContinueMethodDebugInfoReader.Create(mockSymReader, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => EditAndContinueMethodDebugInfoReader.Create(mockSymReader, -1));
}

[Theory]
[InlineData(DebugInformationFormat.PortablePdb)]
[InlineData(DebugInformationFormat.Pdb)]
public void DebugInfo(DebugInformationFormat format)
[InlineData(DebugInformationFormat.PortablePdb, true)]
[InlineData(DebugInformationFormat.PortablePdb, false)]
[InlineData(DebugInformationFormat.Pdb, true)]
public void DebugInfo(DebugInformationFormat format, bool useSymReader)
{
var symBinder = new SymBinder();
var metadataImportProvider = new DummyMetadataImportProvider();

var source = @"
using System;
delegate void D();
Expand All @@ -61,22 +60,22 @@ public static void Main()
compilation.EmitToArray(new EmitOptions(debugInformationFormat: format), pdbStream: pdbStream);
pdbStream.Position = 0;

var pdbStreamCom = SymUnmanagedStreamFactory.CreateStream(pdbStream);
DebugInformationReaderProvider provider;
EditAndContinueMethodDebugInfoReader reader;

ISymUnmanagedReader5 symReader5;
if (format == DebugInformationFormat.PortablePdb)
if (format == DebugInformationFormat.PortablePdb && useSymReader)
{
int hr = symBinder.GetReaderFromPdbStream(metadataImportProvider, pdbStreamCom, out var symReader);
Assert.Equal(0, hr);
symReader5 = (ISymUnmanagedReader5)symReader;
var pdbStreamCom = SymUnmanagedStreamFactory.CreateStream(pdbStream);
var metadataImportProvider = new DummyMetadataImportProvider();
Assert.Equal(0, new SymBinder().GetReaderFromPdbStream(metadataImportProvider, pdbStreamCom, out var symReader));
reader = EditAndContinueMethodDebugInfoReader.Create((ISymUnmanagedReader5)symReader, version: 1);
}
else
{
symReader5 = SymUnmanagedReaderFactory.CreateReader<ISymUnmanagedReader5>(pdbStream, new DummySymReaderMetadataProvider());
provider = DebugInformationReaderProvider.CreateFromStream(pdbStream);
reader = provider.CreateEditAndContinueMethodDebugInfoReader();
}

var reader = EditAndContinueMethodDebugInfoReader.Create(symReader5, version: 1);

// Main method
var debugInfo = reader.GetDebugInfo(MetadataTokens.MethodDefinitionHandle(5));
Assert.Equal(0, debugInfo.GetMethodOrdinal());
Expand Down
55 changes: 55 additions & 0 deletions src/EditorFeatures/Test/Emit/CompilationOutputFilesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Emit.UnitTests
{
public class CompilationOutputFilesTests : TestBase
{
[Fact]
public void OpenStream_Errors()
{
Assert.Throws<ArgumentException>(() => new CompilationOutputFiles(@"a.dll"));
Assert.Throws<ArgumentException>(() => new CompilationOutputFiles(@"\a.dll", @"a.dll"));
}

[Fact]
public void AssemblyAndPdb()
{
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 peImage = compilation.EmitToArray(new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb), pdbStream: pdbStream);
pdbStream.Position = 0;

var dir = Temp.CreateDirectory();
var dllFile = dir.CreateFile("a.dll").WriteAllBytes(peImage);
var pdbFile = dir.CreateFile("a.pdb").WriteAllBytes(pdbStream.ToArray());

var outputs = new CompilationOutputFiles(dllFile.Path, pdbFile.Path);

using (var pdb = outputs.OpenPdb())
{
var encReader = pdb.CreateEditAndContinueMethodDebugInfoReader();
Assert.True(encReader.IsPortable);
var localSig = encReader.GetLocalSignature(MetadataTokens.MethodDefinitionHandle(1));
Assert.Equal(MetadataTokens.StandaloneSignatureHandle(1), localSig);
}

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);
}
}
}
Loading

0 comments on commit 3bdbd56

Please sign in to comment.