diff --git a/.vscode/launch.json b/.vscode/launch.json
index fe9303d2e88a6..ae371f1da31ef 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -4,6 +4,22 @@
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
+ {
+ "name": "Launch BuildValidator.dll",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/artifacts/bin/BuildValidator/Debug/netcoreapp3.1/BuildValidator.dll",
+ "args": [
+ "--assembliesPath", "./artifacts/obj/RunTests",
+ "--debugPath", "./artifacts/BuildValidator",
+ "--sourcePath", "."
+ ],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
{
"name": "Launch RunTests.dll",
"type": "coreclr",
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 819f1ffcb44fd..bb28e7fecfffc 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -213,3 +213,38 @@ jobs:
continueOnError: true
condition: not(succeeded())
+- job: Correctness_Rebuild
+ pool:
+ vmImage: windows-2019
+ timeoutInMinutes: 90
+ steps:
+ - template: eng/pipelines/checkout-windows-task.yml
+
+ - task: PowerShell@2
+ displayName: Restore
+ inputs:
+ filePath: eng/build.ps1
+ arguments: -configuration Debug -prepareMachine -ci -restore -binaryLog
+
+ - task: PowerShell@2
+ displayName: Build
+ inputs:
+ filePath: eng/build.ps1
+ arguments: -configuration Debug -prepareMachine -ci -build -bootstrap -publish -binaryLog -skipDocumentation
+
+ - script: .\artifacts\bin\BuildValidator\Debug\net472\BuildValidator.exe --assembliesPath .\artifacts\obj\Microsoft.CodeAnalysis --debugPath .\artifacts\BuildValidator --sourcePath .
+ displayName: Run BuildValidator
+
+ - task: PublishBuildArtifacts@1
+ displayName: Publish BuildValidator debug outputs
+ inputs:
+ PathtoPublish: '$(Build.SourcesDirectory)/artifacts/BuildValidator'
+ ArtifactName: 'BuildValidator_DebugOut'
+ publishLocation: Container
+ continueOnError: true
+ condition: failed()
+
+ - template: eng/pipelines/publish-logs.yml
+ parameters:
+ jobName: Correctness_Rebuild
+ configuration: Debug
diff --git a/eng/Versions.props b/eng/Versions.props
index 7725af2bc2590..0de8b1b7f5bc6 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -96,7 +96,7 @@
3.13.8
15.8.27812-alpha
14.3.25407-alpha
- 1.0.0-beta3.20174.1
+ 1.0.0-beta3.21075.2
8.0.0
2.2.101
2.1.2
@@ -205,6 +205,7 @@
4.5.1
1.0.31
4.7.0
+ 2.0.0-beta1.20574.7
0.3.0-alpha.19577.1
4.5.0
4.5.0
diff --git a/eng/build-utils.ps1 b/eng/build-utils.ps1
index 964b074bc614c..6e32af8866751 100644
--- a/eng/build-utils.ps1
+++ b/eng/build-utils.ps1
@@ -333,7 +333,7 @@ function Make-BootstrapBuild([switch]$force32 = $false) {
Run-MSBuild $projectPath "/restore /t:Pack /p:RoslynEnforceCodeStyle=false /p:RunAnalyzersDuringBuild=false /p:DotNetUseShippingVersions=true /p:InitialDefineConstants=BOOTSTRAP /p:PackageOutputPath=`"$dir`" /p:EnableNgenOptimization=false /p:PublishWindowsPdb=false $force32Flag" -logFileName "Bootstrap" -configuration $bootstrapConfiguration -runAnalyzers
$packageFile = Get-ChildItem -Path $dir -Filter "$packageName.*.nupkg"
- Unzip "$dir\$packageFile" $dir
+ Unzip (Join-Path $dir $packageFile) $dir
Write-Host "Cleaning Bootstrap compiler artifacts"
Run-MSBuild $projectPath "/t:Clean" -logFileName "BootstrapClean"
diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1
new file mode 100644
index 0000000000000..10b83f7ce4a73
--- /dev/null
+++ b/eng/test-rebuild.ps1
@@ -0,0 +1,43 @@
+<#
+ This script tests that Roslyn artifacts are rebuildable--i.e. that the source code and resources can be identified
+#>
+
+[CmdletBinding(PositionalBinding=$false)]
+param(
+ [string]$configuration = "Debug",
+ [switch]$ci = $false,
+ [switch]$help)
+
+Set-StrictMode -version 2.0
+$ErrorActionPreference="Stop"
+
+function Print-Usage() {
+ Write-Host "Usage: test-rebuild.ps1"
+ Write-Host " -configuration Build configuration ('Debug' or 'Release')"
+ Write-Host " -ci Set when running on CI server"
+ Write-Host " -help Print help and exit"
+}
+
+try {
+ if ($help) {
+ Print-Usage
+ exit 0
+ }
+
+ . (Join-Path $PSScriptRoot "build-utils.ps1")
+ Push-Location $RepoRoot
+
+ Write-Host "Building Roslyn"
+ Exec-Console (Join-Path $PSScriptRoot "build.ps1") "-restore -build -ci:$ci -configuration:$configuration -pack -binaryLog"
+ Exec-Console "artifacts\bin\BuildValidator\$configuration\net472\BuildValidator.exe" "--assembliesPath '$ArtifactsDir/obj/Microsoft.CodeAnalysis'"
+
+ exit 0
+}
+catch [exception] {
+ Write-Host $_
+ Write-Host $_.Exception
+ exit 1
+}
+finally {
+ Pop-Location
+}
diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBAsyncTests.cs
index fe8b3e055d521..c00ac605dbb53 100644
--- a/src/Compilers/CSharp/Test/Emit/PDB/PDBAsyncTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBAsyncTests.cs
@@ -2016,11 +2016,13 @@ async partial void M() {}
AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
MethodDebugInformation (index: 0x31, size: 20):
-==================================================
-1: nil
-2: nil
-3: nil
-4:
+================================================
+ IL
+================================================
+1: nil
+2: nil
+3: nil
+4:
{
Kickoff Method: 0x06000001 (MethodDef)
Locals: 0x11000002 (StandAloneSig)
diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBIteratorTests.cs
index 735172d632663..07868e367a1db 100644
--- a/src/Compilers/CSharp/Test/Emit/PDB/PDBIteratorTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBIteratorTests.cs
@@ -1599,12 +1599,14 @@ public class C
AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
MethodDebugInformation (index: 0x31, size: 40):
-==================================================
-1: nil
-2: nil
-3: nil
-4: nil
-5:
+================================================
+ IL
+================================================
+1: nil
+2: nil
+3: nil
+4: nil
+5:
{
Kickoff Method: 0x06000001 (MethodDef)
Locals: 0x11000001 (StandAloneSig)
@@ -1615,12 +1617,11 @@ public class C
IL_0030:
IL_0037: (5, 44) - (5, 45)
}
-6: nil
-7: nil
-8: nil
-9: nil
-a: nil
-",
+6: nil
+7: nil
+8: nil
+9: nil
+a: nil",
writer.ToString());
}
}
diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs
index 07d511f3652a3..5027090b839b0 100644
--- a/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBSourceLinkTests.cs
@@ -154,6 +154,7 @@ public static void Main()
debugEntryPoint: null,
sourceLinkStream: new MemoryStream(new byte[] { 1, 2, 3 }),
embeddedTexts: null,
+ pdbOptionsBlobReader: null,
testData: new CompilationTestData()
{
SymWriterFactory = metadataProvider => new SymUnmanagedWriterWithoutSourceLinkSupport(metadataProvider)
diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
index 0b75e44db146d..2a55b4f6b50e8 100644
--- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs
@@ -188,6 +188,7 @@ public void SymWriterErrors()
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts: null,
+ pdbOptionsBlobReader: null,
testData: new CompilationTestData() { SymWriterFactory = _ => new MockSymUnmanagedWriter() });
result.Diagnostics.Verify(
@@ -219,6 +220,7 @@ public void SymWriterErrors2()
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts: null,
+ pdbOptionsBlobReader: null,
testData: new CompilationTestData() { SymWriterFactory = SymWriterTestUtilities.ThrowingFactory });
result.Diagnostics.Verify(
@@ -250,6 +252,7 @@ public void SymWriterErrors3()
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts: null,
+ pdbOptionsBlobReader: null,
testData: new CompilationTestData() { SymWriterFactory = SymWriterTestUtilities.ThrowingFactory });
result.Diagnostics.Verify(
@@ -281,6 +284,7 @@ public void SymWriterErrors4()
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts: null,
+ pdbOptionsBlobReader: null,
testData: new CompilationTestData() { SymWriterFactory = _ => throw new DllNotFoundException("xxx") });
result.Diagnostics.Verify(
diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
index 40c6121301440..364ec2aefc490 100644
--- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
+++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
@@ -1266,6 +1266,7 @@ private void CompileAndEmit(
peStreamProvider,
refPeStreamProviderOpt,
pdbStreamProviderOpt,
+ pdbOptionsBlobReader: null,
testSymWriterFactory: null,
diagnostics: diagnostics,
emitOptions: emitOptions,
diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs
index 18dbb8502671c..aacd2786350d4 100644
--- a/src/Compilers/Core/Portable/Compilation/Compilation.cs
+++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs
@@ -2440,6 +2440,35 @@ public EmitResult Emit(
IEnumerable? embeddedTexts = null,
Stream? metadataPEStream = null,
CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return Emit(
+ peStream,
+ pdbStream,
+ xmlDocumentationStream,
+ win32Resources,
+ manifestResources,
+ options,
+ debugEntryPoint,
+ sourceLinkStream,
+ embeddedTexts,
+ metadataPEStream,
+ pdbOptionsBlobReader: null,
+ cancellationToken);
+ }
+
+ internal EmitResult Emit(
+ Stream peStream,
+ Stream? pdbStream,
+ Stream? xmlDocumentationStream,
+ Stream? win32Resources,
+ IEnumerable? manifestResources,
+ EmitOptions? options,
+ IMethodSymbol? debugEntryPoint,
+ Stream? sourceLinkStream,
+ IEnumerable? embeddedTexts,
+ Stream? metadataPEStream,
+ BlobReader? pdbOptionsBlobReader,
+ CancellationToken cancellationToken)
{
if (peStream == null)
{
@@ -2535,6 +2564,7 @@ public EmitResult Emit(
debugEntryPoint,
sourceLinkStream,
embeddedTexts,
+ pdbOptionsBlobReader,
testData: null,
cancellationToken: cancellationToken);
}
@@ -2554,6 +2584,7 @@ internal EmitResult Emit(
IMethodSymbol? debugEntryPoint,
Stream? sourceLinkStream,
IEnumerable? embeddedTexts,
+ BlobReader? pdbOptionsBlobReader,
CompilationTestData? testData,
CancellationToken cancellationToken)
{
@@ -2634,6 +2665,7 @@ internal EmitResult Emit(
new SimpleEmitStreamProvider(peStream),
(metadataPEStream != null) ? new SimpleEmitStreamProvider(metadataPEStream) : null,
(pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null,
+ pdbOptionsBlobReader,
testData?.SymWriterFactory,
diagnostics,
emitOptions: options,
@@ -2795,6 +2827,7 @@ internal bool SerializeToPeStream(
EmitStreamProvider peStreamProvider,
EmitStreamProvider? metadataPEStreamProvider,
EmitStreamProvider? pdbStreamProvider,
+ BlobReader? pdbOptionsBlobReader,
Func? testSymWriterFactory,
DiagnosticBag diagnostics,
EmitOptions emitOptions,
@@ -2868,6 +2901,7 @@ internal bool SerializeToPeStream(
getPortablePdbStream,
nativePdbWriter,
pePdbFilePath,
+ pdbOptionsBlobReader,
emitOptions.EmitMetadataOnly,
emitOptions.IncludePrivateMembers,
deterministic,
@@ -2949,6 +2983,7 @@ internal static bool SerializePeToStream(
Func? getPortablePdbStreamOpt,
Cci.PdbWriter? nativePdbWriterOpt,
string? pdbPathOpt,
+ BlobReader? pdbOptionsBlobReader,
bool metadataOnly,
bool includePrivateMembers,
bool isDeterministic,
@@ -2966,6 +3001,7 @@ internal static bool SerializePeToStream(
getPeStream,
getPortablePdbStreamOpt,
nativePdbWriterOpt,
+ pdbOptionsBlobReader,
pdbPathOpt,
metadataOnly,
deterministicPrimaryOutput,
@@ -2988,6 +3024,7 @@ internal static bool SerializePeToStream(
getMetadataPeStreamOpt,
getPortablePdbStreamOpt: null,
nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
pdbPathOpt: null,
metadataOnly: true,
isDeterministic: true,
@@ -3043,6 +3080,7 @@ internal static bool SerializePeToStream(
metadataStream,
ilStream,
(nativePdbWriterOpt == null) ? pdbStream : null,
+ pdbOptionsBlobReader: null,
out MetadataSizes metadataSizes);
writer.GetMethodTokens(updatedMethods);
diff --git a/src/Compilers/Core/Portable/Compilation/CompilationOptions.cs b/src/Compilers/Core/Portable/Compilation/CompilationOptions.cs
index a59fc6ada5f98..81ce6f3109049 100644
--- a/src/Compilers/Core/Portable/Compilation/CompilationOptions.cs
+++ b/src/Compilers/Core/Portable/Compilation/CompilationOptions.cs
@@ -166,7 +166,7 @@ public abstract class CompilationOptions
///
/// Emit mode that favors debuggability.
///
- internal bool DebugPlusMode { get; private protected set; }
+ internal bool DebugPlusMode { get; set; }
///
/// Specifies whether to import members with accessibility other than public or protected by default.
diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj
index c6dd8ec9229ee..ca5d78b8ad38b 100644
--- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj
+++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj
@@ -11,7 +11,8 @@
true
partial
true
-
+ true
+
true
Microsoft.CodeAnalysis.Common
@@ -51,6 +52,7 @@
+
diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs
index 90690ec26c748..6b5de03992338 100644
--- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs
+++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs
@@ -842,50 +842,57 @@ private void EmbedSourceLink(Stream stream)
/// Capture the set of compilation options to allow a compilation
/// to be reconstructed from the pdb
///
- private void EmbedCompilationOptions(CommonPEModuleBuilder module)
+ private void EmbedCompilationOptions(BlobReader? pdbCompilationOptionsReader, CommonPEModuleBuilder module)
{
var builder = new BlobBuilder();
- var compilerVersion = typeof(Compilation).Assembly.GetCustomAttribute().InformationalVersion;
- WriteValue(CompilationOptionNames.CompilationOptionsVersion, CompilationOptionsSchemaVersion.ToString());
- WriteValue(CompilationOptionNames.CompilerVersion, compilerVersion);
-
- WriteValue(CompilationOptionNames.Language, module.CommonCompilation.Options.Language);
- WriteValue(CompilationOptionNames.SourceFileCount, module.CommonCompilation.SyntaxTrees.Count().ToString());
-
- if (module.EmitOptions.FallbackSourceFileEncoding != null)
+ if (pdbCompilationOptionsReader is { } reader)
{
- WriteValue(CompilationOptionNames.FallbackEncoding, module.EmitOptions.FallbackSourceFileEncoding.WebName);
+ builder.WriteBytes(reader.ReadBytes(reader.RemainingBytes));
}
-
- if (module.EmitOptions.DefaultSourceFileEncoding != null)
+ else
{
- WriteValue(CompilationOptionNames.DefaultEncoding, module.EmitOptions.DefaultSourceFileEncoding.WebName);
- }
+ var compilerVersion = typeof(Compilation).Assembly.GetCustomAttribute().InformationalVersion;
+ WriteValue(CompilationOptionNames.CompilationOptionsVersion, CompilationOptionsSchemaVersion.ToString());
+ WriteValue(CompilationOptionNames.CompilerVersion, compilerVersion);
- int portabilityPolicy = 0;
- if (module.CommonCompilation.Options.AssemblyIdentityComparer is DesktopAssemblyIdentityComparer identityComparer)
- {
- portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightLibraryAssembliesPortability ? 0b1 : 0;
- portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightPlatformAssembliesPortability ? 0b10 : 0;
- }
+ WriteValue(CompilationOptionNames.Language, module.CommonCompilation.Options.Language);
+ WriteValue(CompilationOptionNames.SourceFileCount, module.CommonCompilation.SyntaxTrees.Count().ToString());
- if (portabilityPolicy != 0)
- {
- WriteValue(CompilationOptionNames.PortabilityPolicy, portabilityPolicy.ToString());
- }
+ if (module.EmitOptions.FallbackSourceFileEncoding != null)
+ {
+ WriteValue(CompilationOptionNames.FallbackEncoding, module.EmitOptions.FallbackSourceFileEncoding.WebName);
+ }
- var optimizationLevel = module.CommonCompilation.Options.OptimizationLevel;
- var debugPlusMode = module.CommonCompilation.Options.DebugPlusMode;
- if (optimizationLevel != OptimizationLevel.Debug || debugPlusMode)
- {
- WriteValue(CompilationOptionNames.Optimization, optimizationLevel.ToPdbSerializedString(debugPlusMode));
- }
+ if (module.EmitOptions.DefaultSourceFileEncoding != null)
+ {
+ WriteValue(CompilationOptionNames.DefaultEncoding, module.EmitOptions.DefaultSourceFileEncoding.WebName);
+ }
+
+ int portabilityPolicy = 0;
+ if (module.CommonCompilation.Options.AssemblyIdentityComparer is DesktopAssemblyIdentityComparer identityComparer)
+ {
+ portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightLibraryAssembliesPortability ? 0b1 : 0;
+ portabilityPolicy |= identityComparer.PortabilityPolicy.SuppressSilverlightPlatformAssembliesPortability ? 0b10 : 0;
+ }
+
+ if (portabilityPolicy != 0)
+ {
+ WriteValue(CompilationOptionNames.PortabilityPolicy, portabilityPolicy.ToString());
+ }
+
+ var optimizationLevel = module.CommonCompilation.Options.OptimizationLevel;
+ var debugPlusMode = module.CommonCompilation.Options.DebugPlusMode;
+ if (optimizationLevel != OptimizationLevel.Debug || debugPlusMode)
+ {
+ WriteValue(CompilationOptionNames.Optimization, optimizationLevel.ToPdbSerializedString(debugPlusMode));
+ }
- var runtimeVersion = typeof(object).Assembly.GetCustomAttribute()?.InformationalVersion;
- WriteValue(CompilationOptionNames.RuntimeVersion, runtimeVersion);
+ var runtimeVersion = typeof(object).Assembly.GetCustomAttribute()?.InformationalVersion;
+ WriteValue(CompilationOptionNames.RuntimeVersion, runtimeVersion);
- module.CommonCompilation.SerializePdbEmbeddedCompilationOptions(builder);
+ module.CommonCompilation.SerializePdbEmbeddedCompilationOptions(builder);
+ }
_debugMetadataOpt.AddCustomDebugInformation(
parent: EntityHandle.ModuleDefinition,
diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs
index c92e87a9e16f5..f7f370be93134 100644
--- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs
+++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs
@@ -1697,7 +1697,7 @@ internal EntityHandle GetDefinitionHandle(IDefinition definition)
};
}
- public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStream, Stream ilStream, Stream portablePdbStreamOpt, out MetadataSizes metadataSizes)
+ public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStream, Stream ilStream, Stream portablePdbStreamOpt, BlobReader? pdbOptionsBlobReader, out MetadataSizes metadataSizes)
{
Debug.Assert(nativePdbWriterOpt == null ^ portablePdbStreamOpt == null);
@@ -1724,6 +1724,7 @@ public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStre
ilBuilder,
mappedFieldDataBuilder,
managedResourceDataBuilder,
+ pdbOptionsBlobReader,
out Blob mvidFixup,
out Blob mvidStringFixup);
@@ -1779,6 +1780,7 @@ public void BuildMetadataAndIL(
BlobBuilder ilBuilder,
BlobBuilder mappedFieldDataBuilder,
BlobBuilder managedResourceDataBuilder,
+ BlobReader? pdbOptionsBlobReader,
out Blob mvidFixup,
out Blob mvidStringFixup)
{
@@ -1805,7 +1807,7 @@ public void BuildMetadataAndIL(
EmbedSourceLink(module.SourceLinkStreamOpt);
}
- EmbedCompilationOptions(module);
+ EmbedCompilationOptions(pdbOptionsBlobReader, module);
EmbedMetadataReferenceInformation(module);
}
diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs
index 6ed8a3d1d6224..7a9d060dca3f8 100644
--- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs
+++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs
@@ -41,6 +41,7 @@ internal static bool WritePeToStream(
Func getPeStream,
Func getPortablePdbStreamOpt,
PdbWriter nativePdbWriterOpt,
+ BlobReader? pdbOptionsBlobReader,
string pdbPathOpt,
bool metadataOnly,
bool isDeterministic,
@@ -73,6 +74,7 @@ internal static bool WritePeToStream(
ilBuilder,
mappedFieldDataBuilder,
managedResourceBuilder,
+ pdbOptionsBlobReader,
out mvidFixup,
out mvidStringFixup);
diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs
index 139c5a289f207..fc9c2ddbccf3a 100644
--- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs
+++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs
@@ -68,6 +68,7 @@ internal static ImmutableArray EmitToArray(
debugEntryPoint: debugEntryPoint,
sourceLinkStream: sourceLinkStream,
embeddedTexts: embeddedTexts,
+ pdbOptionsBlobReader: null,
testData: testData,
cancellationToken: default(CancellationToken));
diff --git a/src/Compilers/Test/Core/Compilation/IRuntimeEnvironment.cs b/src/Compilers/Test/Core/Compilation/IRuntimeEnvironment.cs
index 3e8065083d402..38105d8bc78ff 100644
--- a/src/Compilers/Test/Core/Compilation/IRuntimeEnvironment.cs
+++ b/src/Compilers/Test/Core/Compilation/IRuntimeEnvironment.cs
@@ -262,6 +262,7 @@ EmitOptions emitOptions
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts,
+ pdbOptionsBlobReader: null,
testData: testData,
cancellationToken: default);
}
diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBAsyncTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBAsyncTests.vb
index bf0f3a18452d3..bcc2d27e251f6 100644
--- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBAsyncTests.vb
+++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBAsyncTests.vb
@@ -939,11 +939,13 @@ End Class
AssertEx.AssertEqualToleratingWhitespaceDifferences("
MethodDebugInformation (index: 0x31, size: 20):
-==================================================
-1: nil
-2: nil
-3: nil
-4:
+================================================
+ IL
+================================================
+1: nil
+2: nil
+3: nil
+4:
{
Kickoff Method: 0x06000002 (MethodDef)
Locals: 0x11000002 (StandAloneSig)
@@ -956,8 +958,7 @@ MethodDebugInformation (index: 0x31, size: 20):
IL_002D: (7, 5) - (7, 12)
IL_0037:
}
-5: nil
-", writer.ToString())
+5: nil", writer.ToString())
End Using
End Sub
diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBIteratorTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBIteratorTests.vb
index 517a930aaf470..4634819a844b4 100644
--- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBIteratorTests.vb
+++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBIteratorTests.vb
@@ -556,12 +556,14 @@ End Class"
AssertEx.AssertEqualToleratingWhitespaceDifferences("
MethodDebugInformation (index: 0x31, size: 40):
-==================================================
-1: nil
-2: nil
-3: nil
-4: nil
-5:
+================================================
+ IL
+================================================
+1: nil
+2: nil
+3: nil
+4: nil
+5:
{
Kickoff Method: 0x06000002 (MethodDef)
Locals: 0x11000002 (StandAloneSig)
@@ -571,10 +573,10 @@ MethodDebugInformation (index: 0x31, size: 40):
IL_0022: (5, 8) - (5, 15)
IL_003D: (6, 5) - (6, 17)
}
-6: nil
-7: nil
-8: nil
-9: nil
+6: nil
+7: nil
+8: nil
+9: nil
a: nil", writer.ToString())
End Using
End Sub
diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs
index b540143fe6de0..5867309217851 100644
--- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs
+++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs
@@ -229,6 +229,7 @@ internal CompilationContext CreateCompilationContext()
() => stream,
getPortablePdbStreamOpt: null,
nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
pdbPathOpt: null,
metadataOnly: false,
isDeterministic: false,
@@ -291,6 +292,7 @@ internal CompilationContext CreateCompilationContext()
() => stream,
getPortablePdbStreamOpt: null,
nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
pdbPathOpt: null,
metadataOnly: false,
isDeterministic: false,
@@ -375,6 +377,7 @@ internal CompilationContext CreateCompilationContext()
() => stream,
getPortablePdbStreamOpt: null,
nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
pdbPathOpt: null,
metadataOnly: false,
isDeterministic: false,
@@ -423,6 +426,7 @@ internal override ReadOnlyCollection CompileGetLocals(
() => stream,
getPortablePdbStreamOpt: null,
nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
pdbPathOpt: null,
metadataOnly: false,
isDeterministic: false,
diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs
index caf94b9d23aff..04dfc1523e410 100644
--- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs
+++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs
@@ -840,7 +840,9 @@ internal static void EmitCorLibWithAssemblyReferences(
comp.MessageProvider,
() => peStream,
() => pdbStream,
- null, null,
+ nativePdbWriterOpt: null,
+ pdbOptionsBlobReader: null,
+ pdbPathOpt: null,
metadataOnly: true,
isDeterministic: false,
emitTestCoverageData: false,
diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb
index e4896807e92d4..353fe79aeb27c 100644
--- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb
+++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb
@@ -376,6 +376,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Function() stream,
getPortablePdbStreamOpt:=Nothing,
nativePdbWriterOpt:=Nothing,
+ pdbOptionsBlobReader:=Nothing,
pdbPathOpt:=Nothing,
metadataOnly:=False,
isDeterministic:=False,
@@ -425,6 +426,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Function() stream,
getPortablePdbStreamOpt:=Nothing,
nativePdbWriterOpt:=Nothing,
+ pdbOptionsBlobReader:=Nothing,
pdbPathOpt:=Nothing,
metadataOnly:=False,
isDeterministic:=False,
@@ -475,6 +477,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Function() stream,
getPortablePdbStreamOpt:=Nothing,
nativePdbWriterOpt:=Nothing,
+ pdbOptionsBlobReader:=Nothing,
pdbPathOpt:=Nothing,
metadataOnly:=False,
isDeterministic:=False,
diff --git a/src/Tools/BuildValidator/BuildConstructor.cs b/src/Tools/BuildValidator/BuildConstructor.cs
index f3de200ad584c..7919972107383 100644
--- a/src/Tools/BuildValidator/BuildConstructor.cs
+++ b/src/Tools/BuildValidator/BuildConstructor.cs
@@ -5,16 +5,18 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VisualBasic;
-
+using Microsoft.Extensions.Logging;
using CS = Microsoft.CodeAnalysis.CSharp;
using VB = Microsoft.CodeAnalysis.VisualBasic;
@@ -27,29 +29,41 @@ internal class BuildConstructor
{
private readonly LocalReferenceResolver _referenceResolver;
private readonly LocalSourceResolver _sourceResolver;
+ private readonly ILogger _logger;
- public BuildConstructor(LocalReferenceResolver referenceResolver, LocalSourceResolver sourceResolver)
+ public BuildConstructor(LocalReferenceResolver referenceResolver, LocalSourceResolver sourceResolver, ILogger logger)
{
_referenceResolver = referenceResolver;
_sourceResolver = sourceResolver;
+ _logger = logger;
}
- public Compilation CreateCompilation(MetadataReader metadataReader, string name)
+ public Compilation CreateCompilation(CompilationOptionsReader compilationOptionsReader, string name)
{
- var pdbReader = new CompilationOptionsReader(metadataReader);
- var pdbCompilationOptions = pdbReader.GetCompilationOptions();
-
+ var pdbCompilationOptions = compilationOptionsReader.GetMetadataCompilationOptions();
if (pdbCompilationOptions.Length == 0)
{
throw new InvalidDataException("Did not find compilation options in pdb");
}
+ var metadataReferenceInfos = compilationOptionsReader.GetMetadataReferences();
+ var encoding = compilationOptionsReader.GetEncoding();
+ var sourceFileInfos = compilationOptionsReader.GetSourceFileInfos(encoding);
+
+ _logger.LogInformation("Locating metadata references");
+ var metadataReferences = _referenceResolver.ResolveReferences(metadataReferenceInfos);
+ logResolvedMetadataReferences();
+
+ var sourceLinks = ResolveSourceLinks(compilationOptionsReader);
+ var sources = ResolveSources(sourceFileInfos, sourceLinks, encoding);
+ logResolvedSources();
+
if (pdbCompilationOptions.TryGetUniqueOption("language", out var language))
{
var compilation = language switch
{
- LanguageNames.CSharp => CreateCSharpCompilation(pdbReader, name),
- LanguageNames.VisualBasic => CreateVisualBasicCompilation(pdbReader, name),
+ LanguageNames.CSharp => CreateCSharpCompilation(name, compilationOptionsReader, sources, metadataReferences),
+ LanguageNames.VisualBasic => CreateVisualBasicCompilation(name, compilationOptionsReader, sources, metadataReferences),
_ => throw new InvalidDataException($"{language} is not a known language")
};
@@ -57,81 +71,114 @@ public Compilation CreateCompilation(MetadataReader metadataReader, string name)
}
throw new InvalidDataException("Did not find language in compilation options");
+
+ void logResolvedMetadataReferences()
+ {
+ using var _ = _logger.BeginScope("Metadata References");
+ for (var i = 0; i < metadataReferenceInfos.Length; i++)
+ {
+ _logger.LogInformation($@"""{metadataReferences[i].Display}"" - {metadataReferenceInfos[i].Mvid}");
+ }
+ }
+
+ void logResolvedSources()
+ {
+ using var _ = _logger.BeginScope("Source Names");
+ foreach (var resolvedSource in sources)
+ {
+ var sourceFileInfo = resolvedSource.SourceFileInfo;
+ var hash = BitConverter.ToString(sourceFileInfo.Hash).Replace("-", "");
+ _logger.LogInformation($@"""{resolvedSource.DisplayPath}"" - {sourceFileInfo.HashAlgorithm} - {hash}");
+ }
+ }
}
- private ImmutableArray CreateMetadataReferences(CompilationOptionsReader pdbReader)
+ private ImmutableArray ResolveSourceLinks(CompilationOptionsReader compilationOptionsReader)
{
- var referenceInfos = pdbReader.GetMetadataReferences();
- return _referenceResolver.ResolveReferences(referenceInfos);
+ using var _ = _logger.BeginScope("Source Links");
+ var sourceLinks = compilationOptionsReader.GetSourceLinksOpt();
+ if (sourceLinks.IsDefault)
+ {
+ _logger.LogInformation("No source links found in pdb");
+ }
+ else
+ {
+ foreach (var link in sourceLinks)
+ {
+ _logger.LogInformation($@"""{link.Prefix}"": ""{link.Replace}""");
+ }
+ }
+ return sourceLinks;
}
- private ImmutableArray GetSources(CompilationOptionsReader pdbReader, Encoding encoding)
+ private ImmutableArray ResolveSources(
+ ImmutableArray sourceFileInfos,
+ ImmutableArray sourceLinks,
+ Encoding encoding)
{
- var builder = ImmutableArray.CreateBuilder();
+ _logger.LogInformation("Locating source files");
- foreach (var srcFile in pdbReader.GetSourceFileNames())
+ var sources = ImmutableArray.CreateBuilder();
+ foreach (var sourceFileInfo in sourceFileInfos)
{
- var text = _sourceResolver.ResolveSource(srcFile, encoding);
- builder.Add(text);
+ sources.Add(_sourceResolver.ResolveSource(sourceFileInfo, sourceLinks, encoding));
}
- return builder.ToImmutable();
+ return sources.ToImmutable();
}
#region CSharp
- private Compilation CreateCSharpCompilation(CompilationOptionsReader pdbReader, string assemblyName)
+ private Compilation CreateCSharpCompilation(
+ string assemblyName,
+ CompilationOptionsReader optionsReader,
+ ImmutableArray sources,
+ ImmutableArray metadataReferences)
{
- var (compilationOptions, parseOptions, encoding) = CreateCSharpCompilationOptions(pdbReader);
- var metadataReferences = CreateMetadataReferences(pdbReader);
- var sources = GetSources(pdbReader, encoding);
-
+ var (compilationOptions, parseOptions) = CreateCSharpCompilationOptions(optionsReader, assemblyName);
return CSharpCompilation.Create(
assemblyName,
- syntaxTrees: sources.Select(s => CSharpSyntaxTree.ParseText(s, options: parseOptions)).ToImmutableArray(),
+ syntaxTrees: sources.Select(s => CSharpSyntaxTree.ParseText(s.SourceText, options: parseOptions, path: s.SourceFileInfo.SourceFilePath)).ToImmutableArray(),
references: metadataReferences,
options: compilationOptions);
}
- private static (CSharpCompilationOptions, CSharpParseOptions, Encoding) CreateCSharpCompilationOptions(CompilationOptionsReader pdbReader)
+ private (CSharpCompilationOptions, CSharpParseOptions) CreateCSharpCompilationOptions(CompilationOptionsReader optionsReader, string assemblyName)
{
- var pdbCompilationOptions = pdbReader.GetCompilationOptions();
+ using var scope = _logger.BeginScope("Options");
+ var pdbCompilationOptions = optionsReader.GetMetadataCompilationOptions();
var langVersionString = pdbCompilationOptions.GetUniqueOption("language-version");
var optimization = pdbCompilationOptions.GetUniqueOption("optimization");
// TODO: Check portability policy if needed
// pdbCompilationOptions.TryGetValue("portability-policy", out var portabilityPolicyString);
- pdbCompilationOptions.TryGetUniqueOption("default-encoding", out var defaultEncoding);
- pdbCompilationOptions.TryGetUniqueOption("fallback-encoding", out var fallbackEncoding);
- pdbCompilationOptions.TryGetUniqueOption("define", out var define);
- pdbCompilationOptions.TryGetUniqueOption("checked", out var checkedString);
- pdbCompilationOptions.TryGetUniqueOption("nullable", out var nullable);
- pdbCompilationOptions.TryGetUniqueOption("unsafe", out var unsafeString);
-
- var encodingString = defaultEncoding ?? fallbackEncoding;
- var encoding = encodingString is null
- ? Encoding.UTF8
- : Encoding.GetEncoding(encodingString);
+ pdbCompilationOptions.TryGetUniqueOption(_logger, "define", out var define);
+ pdbCompilationOptions.TryGetUniqueOption(_logger, "checked", out var checkedString);
+ pdbCompilationOptions.TryGetUniqueOption(_logger, "nullable", out var nullable);
+ pdbCompilationOptions.TryGetUniqueOption(_logger, "unsafe", out var unsafeString);
CS.LanguageVersionFacts.TryParse(langVersionString, out var langVersion);
var preprocessorSymbols = define == null
? ImmutableArray.Empty
- : define.Split(';').ToImmutableArray();
+ : define.Split(',').ToImmutableArray();
var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(langVersion)
.WithPreprocessorSymbols(preprocessorSymbols);
- var (optimizationLevel, _) = GetOptimizationLevel(optimization);
+ var (optimizationLevel, plus) = GetOptimizationLevel(optimization);
var nullableOptions = nullable is null
? NullableContextOptions.Disable
: (NullableContextOptions)Enum.Parse(typeof(NullableContextOptions), nullable);
var compilationOptions = new CSharpCompilationOptions(
- pdbReader.GetOutputKind(),
+ optionsReader.GetOutputKind(),
reportSuppressedDiagnostics: false,
- moduleName: null,
- mainTypeName: null,
+
+ // TODO: can't rely on the implicity moduleName here. In the case of .NET Core EXE the output name will
+ // end with .dll but the inferred name will be .exe
+ moduleName: assemblyName + ".dll",
+ mainTypeName: optionsReader.GetMainTypeName(),
scriptClassName: null,
usings: null,
optimizationLevel,
@@ -139,24 +186,31 @@ private static (CSharpCompilationOptions, CSharpParseOptions, Encoding) CreateCS
!string.IsNullOrEmpty(unsafeString) && bool.Parse(unsafeString),
cryptoKeyContainer: null,
cryptoKeyFile: null,
- cryptoPublicKey: default,
+ cryptoPublicKey: optionsReader.GetPublicKey()?.ToImmutableArray() ?? default,
delaySign: null,
Platform.AnyCpu,
+
+ // presence of diagnostics is expected to not affect emit.
ReportDiagnostic.Suppress,
warningLevel: 4,
specificDiagnosticOptions: null,
+
concurrentBuild: true,
deterministic: true,
+
xmlReferenceResolver: null,
sourceReferenceResolver: null,
metadataReferenceResolver: null,
+
assemblyIdentityComparer: null,
strongNameProvider: null,
publicSign: false,
+
metadataImportOptions: MetadataImportOptions.Public,
nullableContextOptions: nullableOptions);
+ compilationOptions.DebugPlusMode = plus;
- return (compilationOptions, parseOptions, encoding);
+ return (compilationOptions, parseOptions);
}
private static (OptimizationLevel, bool) GetOptimizationLevel(string optimizationLevel)
@@ -171,22 +225,23 @@ private static (OptimizationLevel, bool) GetOptimizationLevel(string optimizatio
#endregion
#region Visual Basic
- private Compilation CreateVisualBasicCompilation(CompilationOptionsReader pdbReader, string assemblyName)
+ private Compilation CreateVisualBasicCompilation(
+ string assemblyName,
+ CompilationOptionsReader optionsReader,
+ ImmutableArray sources,
+ ImmutableArray metadataReferences)
{
- var compilationOptions = CreateVisualBasicCompilationOptions(pdbReader);
- var metadataReferences = CreateMetadataReferences(pdbReader);
- var sources = GetSources(pdbReader, Encoding.UTF8);
-
+ var compilationOptions = CreateVisualBasicCompilationOptions(optionsReader);
return VisualBasicCompilation.Create(
assemblyName,
- syntaxTrees: sources.Select(s => VisualBasicSyntaxTree.ParseText(s, options: compilationOptions.ParseOptions)).ToImmutableArray(),
+ syntaxTrees: sources.Select(s => VisualBasicSyntaxTree.ParseText(s.SourceText, options: compilationOptions.ParseOptions, path: s.DisplayPath)).ToImmutableArray(),
references: metadataReferences,
options: compilationOptions);
}
- private static VisualBasicCompilationOptions CreateVisualBasicCompilationOptions(CompilationOptionsReader pdbReader)
+ private static VisualBasicCompilationOptions CreateVisualBasicCompilationOptions(CompilationOptionsReader optionsReader)
{
- var pdbCompilationOptions = pdbReader.GetCompilationOptions();
+ var pdbCompilationOptions = optionsReader.GetMetadataCompilationOptions();
var langVersionString = pdbCompilationOptions.GetUniqueOption("language-version");
var optimization = pdbCompilationOptions.GetUniqueOption("optimization");
@@ -212,6 +267,8 @@ private static VisualBasicCompilationOptions CreateVisualBasicCompilationOptions
bool.TryParse(checkedString, out var isChecked);
bool.TryParse(strict, out var isStrict);
+ // TODO: rebuilding VB projects fails due to reference issues
+ // for example, core types like KeyValuePair are missing
return new VisualBasicCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
moduleName: null,
@@ -229,7 +286,7 @@ private static VisualBasicCompilationOptions CreateVisualBasicCompilationOptions
checkOverflow: isChecked,
cryptoKeyContainer: null,
cryptoKeyFile: null,
- cryptoPublicKey: default,
+ cryptoPublicKey: optionsReader.GetPublicKey()?.ToImmutableArray() ?? default,
delaySign: null,
platform: Platform.AnyCpu,
generalDiagnosticOption: ReportDiagnostic.Default,
diff --git a/src/Tools/BuildValidator/BuildValidator.csproj b/src/Tools/BuildValidator/BuildValidator.csproj
index 74a2abe36f0ce..1634c165010d1 100644
--- a/src/Tools/BuildValidator/BuildValidator.csproj
+++ b/src/Tools/BuildValidator/BuildValidator.csproj
@@ -2,17 +2,12 @@
-
Exe
- netcoreapp3.1
+ netcoreapp3.1;net472
AnyCPU
- true
-
- false
-
- true
enable
+ true
@@ -26,5 +21,8 @@
+
+
+
diff --git a/src/Tools/BuildValidator/BuildValidator.sln b/src/Tools/BuildValidator/BuildValidator.sln
new file mode 100644
index 0000000000000..23eabc7616be7
--- /dev/null
+++ b/src/Tools/BuildValidator/BuildValidator.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30905.15
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildValidator", "BuildValidator.csproj", "{F3EDD192-7E74-441D-B096-16C0FE2731E3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F3EDD192-7E74-441D-B096-16C0FE2731E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3EDD192-7E74-441D-B096-16C0FE2731E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F3EDD192-7E74-441D-B096-16C0FE2731E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3EDD192-7E74-441D-B096-16C0FE2731E3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {B7279C10-0E1A-43B3-B6E8-DB13A0795EDE}
+ EndGlobalSection
+EndGlobal
diff --git a/src/Tools/BuildValidator/CompilationDiff.cs b/src/Tools/BuildValidator/CompilationDiff.cs
index fc7b71f87b80a..ee3828d61bfd7 100644
--- a/src/Tools/BuildValidator/CompilationDiff.cs
+++ b/src/Tools/BuildValidator/CompilationDiff.cs
@@ -4,10 +4,22 @@
using System;
using System.Collections.Immutable;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Emit;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.Extensions.Logging;
+using Microsoft.Metadata.Tools;
namespace BuildValidator
{
@@ -16,7 +28,6 @@ internal class CompilationDiff
public bool? AreEqual { get; }
public string OriginalPath { get; }
public ImmutableArray Diagnostics { get; }
- public Exception? Exception { get; }
private CompilationDiff(
string originalPath,
@@ -26,44 +37,154 @@ private CompilationDiff(
OriginalPath = originalPath;
}
- private CompilationDiff(
- string originalPath,
- Exception exception)
- {
- OriginalPath = originalPath;
- Exception = exception;
- }
-
private CompilationDiff(ImmutableArray diagnostics, string originalPath)
{
Diagnostics = diagnostics;
OriginalPath = originalPath;
}
- public static CompilationDiff Create(FileInfo assemblyFile, Compilation producedCompilation)
+ public static unsafe CompilationDiff Create(
+ FileInfo originalBinaryPath,
+ CompilationOptionsReader optionsReader,
+ Compilation producedCompilation,
+ IMethodSymbol? debugEntryPoint,
+ ILogger logger,
+ Options options)
{
- using var peStream = new MemoryStream();
+ using var rebuildPeStream = new MemoryStream();
- var emitResult = producedCompilation.Emit(peStream);
- if (emitResult.Success)
- {
- using var originalStream = assemblyFile.OpenRead();
- var originalBytes = new byte[originalStream.Length];
- originalStream.Read(originalBytes, 0, (int)originalStream.Length);
+ // By default the Roslyn command line adds a resource that we need to replicate here.
+ using var win32ResourceStream = producedCompilation.CreateDefaultWin32Resources(
+ versionResource: true,
+ noManifest: producedCompilation.Options.OutputKind == OutputKind.DynamicallyLinkedLibrary,
+ manifestContents: null,
+ iconInIcoFormat: null);
- var newBytes = peStream.ToArray();
+ var sourceLink = optionsReader.GetSourceLinkUTF8();
+ var emitResult = producedCompilation.Emit(
+ peStream: rebuildPeStream,
+ pdbStream: null,
+ xmlDocumentationStream: null,
+ win32Resources: win32ResourceStream,
+ manifestResources: optionsReader.GetManifestResources(),
+ options: new EmitOptions(
+ debugInformationFormat: DebugInformationFormat.Embedded, highEntropyVirtualAddressSpace: true),
+ debugEntryPoint: debugEntryPoint,
+ metadataPEStream: null,
+ pdbOptionsBlobReader: optionsReader.GetMetadataCompilationOptionsBlobReader(),
+ sourceLinkStream: sourceLink != null ? new MemoryStream(sourceLink) : null,
+ embeddedTexts: producedCompilation.SyntaxTrees
+ .Select(st => (path: st.FilePath, text: st.GetText()))
+ .Where(pair => pair.text.CanBeEmbedded)
+ .Select(pair => EmbeddedText.FromSource(pair.path, pair.text)),
+ cancellationToken: CancellationToken.None);
- return new CompilationDiff(assemblyFile.FullName, newBytes.SequenceEqual(originalBytes));
+ if (!emitResult.Success)
+ {
+ using var diagsScope = logger.BeginScope($"Diagnostics");
+ foreach (var diag in emitResult.Diagnostics)
+ {
+ logger.LogError(diag.ToString());
+ }
+
+ return new CompilationDiff(emitResult.Diagnostics, originalBinaryPath.FullName);
}
else
{
- return new CompilationDiff(emitResult.Diagnostics, assemblyFile.FullName);
+ var originalBytes = File.ReadAllBytes(originalBinaryPath.FullName);
+ var rebuildBytes = rebuildPeStream.ToArray();
+
+ var bytesEqual = originalBytes.SequenceEqual(rebuildBytes);
+ if (!bytesEqual)
+ {
+ logger.LogError($"Rebuild of {originalBinaryPath.Name} was not equivalent to the original.");
+ if (!options.Debug)
+ {
+ logger.LogInformation("Pass the --debug argument and re-run to write the visualization of the original and rebuild to disk.");
+ }
+ else
+ {
+ logger.LogInformation("Creating a diff...");
+
+ var debugPath = options.DebugPath;
+ logger.LogInformation($@"Writing diffs to ""{Path.GetFullPath(debugPath)}""");
+
+ var assemblyName = Path.GetFileNameWithoutExtension(originalBinaryPath.Name);
+ var assemblyDebugPath = Path.Combine(debugPath, assemblyName);
+
+ var originalPath = Path.Combine(assemblyDebugPath, "original");
+ var rebuildPath = Path.Combine(assemblyDebugPath, "rebuild");
+ var sourcesPath = Path.Combine(assemblyDebugPath, "sources");
+
+ Directory.CreateDirectory(originalPath);
+ Directory.CreateDirectory(rebuildPath);
+ Directory.CreateDirectory(sourcesPath);
+
+ // TODO: output source files should include the entire relative path instead of just the file name.
+ foreach (var tree in producedCompilation.SyntaxTrees)
+ {
+ var sourceFilePath = Path.Combine(sourcesPath, Path.GetFileName(tree.FilePath));
+ using var file = File.OpenWrite(sourceFilePath);
+ var writer = new StreamWriter(file);
+ tree.GetText().Write(writer);
+ writer.Flush();
+ }
+
+ var originalAssemblyPath = Path.Combine(originalPath, originalBinaryPath.Name);
+ File.WriteAllBytes(originalAssemblyPath, originalBytes);
+
+ var rebuildAssemblyPath = Path.Combine(rebuildPath, originalBinaryPath.Name);
+ File.WriteAllBytes(rebuildAssemblyPath, rebuildBytes);
+
+ var originalPeMdvPath = Path.Combine(originalPath, assemblyName + ".pe.mdv");
+ var originalPdbMdvPath = Path.Combine(originalPath, assemblyName + ".pdb.mdv");
+ writeVisualization(originalPeMdvPath, optionsReader.PeReader.GetMetadataReader());
+ writeVisualization(originalPdbMdvPath, optionsReader.PdbReader);
+
+ var rebuildPeMdvPath = Path.Combine(rebuildPath, assemblyName + ".pe.mdv");
+ var rebuildPdbMdvPath = Path.Combine(rebuildPath, assemblyName + ".pdb.mdv");
+ fixed (byte* ptr = rebuildBytes)
+ {
+ using var rebuildPeReader = new PEReader(ptr, rebuildBytes.Length);
+ writeVisualization(rebuildPeMdvPath, rebuildPeReader.GetMetadataReader());
+
+ if (rebuildPeReader.TryOpenAssociatedPortablePdb(
+ rebuildAssemblyPath,
+ path => File.Exists(path) ? File.OpenRead(path) : null,
+ out var provider,
+ out _) && provider is { })
+ {
+ var rebuildPdbReader = provider.GetMetadataReader(MetadataReaderOptions.Default);
+ writeVisualization(rebuildPdbMdvPath, rebuildPdbReader);
+ }
+ }
+
+ var ildasmOriginalOutputPath = Path.Combine(originalPath, assemblyName + ".il");
+ var ildasmRebuildOutputPath = Path.Combine(rebuildPath, assemblyName + ".il");
+
+ // TODO: can we bundle ildasm in with the utility?
+ Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{originalBinaryPath.FullName} /out={ildasmOriginalOutputPath}").WaitForExit();
+ Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe", $@"{rebuildAssemblyPath} /out={ildasmRebuildOutputPath}").WaitForExit();
+
+ File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-pe.mdv.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{originalPeMdvPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{rebuildPeMdvPath.Substring(assemblyDebugPath.Length)}"")");
+ File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-pdb.mdv.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{originalPdbMdvPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{rebuildPdbMdvPath.Substring(assemblyDebugPath.Length)}"")");
+ File.WriteAllText(Path.Combine(assemblyDebugPath, "compare-il.ps1"), $@"code --diff (Join-Path $PSScriptRoot ""{ildasmOriginalOutputPath.Substring(assemblyDebugPath.Length)}"") (Join-Path $PSScriptRoot ""{ildasmRebuildOutputPath.Substring(assemblyDebugPath.Length)}"")");
+ }
+ }
+
+ return new CompilationDiff(originalBinaryPath.FullName, bytesEqual);
}
- }
- public static CompilationDiff Create(FileInfo assemblyFile, Exception exception)
- {
- return new CompilationDiff(originalPath: assemblyFile.FullName, exception);
+ void writeVisualization(string outPath, MetadataReader pdbReader)
+ {
+ using (var tempFile = File.OpenWrite(outPath))
+ {
+ var writer = new StreamWriter(tempFile);
+ var visualizer = new MetadataVisualizer(pdbReader, writer);
+ visualizer.Visualize();
+ writer.Flush();
+ }
+ }
}
}
}
diff --git a/src/Tools/BuildValidator/CompilationOptionsReader.cs b/src/Tools/BuildValidator/CompilationOptionsReader.cs
index b2560a2adb37e..9b9cd0f14776e 100644
--- a/src/Tools/BuildValidator/CompilationOptionsReader.cs
+++ b/src/Tools/BuildValidator/CompilationOptionsReader.cs
@@ -6,60 +6,297 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
+using System.IO.Compression;
using System.Linq;
+using System.Reflection;
using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Cci;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.Extensions.Logging;
+using Newtonsoft.Json;
namespace BuildValidator
{
+ internal readonly struct SourceFileInfo
+ {
+ internal string SourceFilePath { get; }
+ internal SourceHashAlgorithm HashAlgorithm { get; }
+ internal byte[] Hash { get; }
+ internal SourceText? EmbeddedText { get; }
+
+ internal SourceFileInfo(
+ string sourceFilePath,
+ SourceHashAlgorithm hashAlgorithm,
+ byte[] hash,
+ SourceText? embeddedText)
+ {
+ SourceFilePath = sourceFilePath;
+ HashAlgorithm = hashAlgorithm;
+ Hash = hash;
+ EmbeddedText = embeddedText;
+ }
+ }
+
internal class CompilationOptionsReader
{
+ // GUIDs specified in https://github.com/dotnet/runtime/blob/master/docs/design/specs/PortablePdb-Metadata.md#document-table-0x30
+ public static readonly Guid HashAlgorithmSha1 = unchecked(new Guid((int)0xff1816ec, (short)0xaa5e, 0x4d10, 0x87, 0xf7, 0x6f, 0x49, 0x63, 0x83, 0x34, 0x60));
+ public static readonly Guid HashAlgorithmSha256 = unchecked(new Guid((int)0x8829d00f, 0x11b8, 0x4213, 0x87, 0x8b, 0x77, 0x0e, 0x85, 0x97, 0xac, 0x16));
+
+ // https://github.com/dotnet/runtime/blob/master/docs/design/specs/PortablePdb-Metadata.md#compilation-metadata-references-c-and-vb-compilers
public static readonly Guid MetadataReferenceInfoGuid = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D");
+
+ // https://github.com/dotnet/runtime/blob/master/docs/design/specs/PortablePdb-Metadata.md#compilation-options-c-and-vb-compilers
public static readonly Guid CompilationOptionsGuid = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8");
+
+ // https://github.com/dotnet/runtime/blob/master/docs/design/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers
public static readonly Guid EmbeddedSourceGuid = new Guid("0E8A571B-6926-466E-B4AD-8AB04611F5FE");
+
+ // https://github.com/dotnet/runtime/blob/master/docs/design/specs/PortablePdb-Metadata.md#source-link-c-and-vb-compilers
public static readonly Guid SourceLinkGuid = new Guid("CC110556-A091-4D38-9FEC-25AB9A351A6A");
- private readonly MetadataReader _metadataReader;
+ public MetadataReader PdbReader { get; }
+ public PEReader PeReader { get; }
+ private readonly ILogger _logger;
- private MetadataCompilationOptions? _compilationOptions;
+ private MetadataCompilationOptions? _metadataCompilationOptions;
private ImmutableArray _metadataReferenceInfo;
+ private byte[]? _sourceLinkUTF8;
- public CompilationOptionsReader(MetadataReader metadataReader)
+ public CompilationOptionsReader(ILogger logger, MetadataReader pdbReader, PEReader peReader)
{
- _metadataReader = metadataReader;
+ _logger = logger;
+ PdbReader = pdbReader;
+ PeReader = peReader;
}
- public MetadataCompilationOptions GetCompilationOptions()
+ public BlobReader GetMetadataCompilationOptionsBlobReader()
{
- if (_compilationOptions is null)
+ if (!TryGetCustomDebugInformationBlobReader(CompilationOptionsGuid, out var optionsBlob))
{
- var optionsBlob = GetCustomDebugInformationBlobReader(CompilationOptionsGuid);
- _compilationOptions = new MetadataCompilationOptions(ParseCompilationOptions(optionsBlob));
+ throw new InvalidOperationException();
}
- return _compilationOptions;
+ return optionsBlob;
+ }
+
+ public MetadataCompilationOptions GetMetadataCompilationOptions()
+ {
+ if (_metadataCompilationOptions is null)
+ {
+ var optionsBlob = GetMetadataCompilationOptionsBlobReader();
+ _metadataCompilationOptions = new MetadataCompilationOptions(ParseCompilationOptions(optionsBlob));
+ }
+
+ return _metadataCompilationOptions;
+ }
+
+ public Encoding GetEncoding()
+ {
+ using var scope = _logger.BeginScope("Encoding");
+
+ var optionsReader = GetMetadataCompilationOptions();
+ optionsReader.TryGetUniqueOption(_logger, "default-encoding", out var defaultEncoding);
+ optionsReader.TryGetUniqueOption(_logger, "fallback-encoding", out var fallbackEncoding);
+
+ var encodingString = defaultEncoding ?? fallbackEncoding;
+ var encoding = encodingString is null
+ ? Encoding.UTF8
+ : Encoding.GetEncoding(encodingString);
+
+ return encoding;
+ }
+
+ public ImmutableArray GetSourceLinksOpt()
+ {
+ var sourceLinkUTF8 = GetSourceLinkUTF8();
+ if (sourceLinkUTF8 is null)
+ {
+ return default;
+ }
+
+ var parseResult = JsonConvert.DeserializeAnonymousType(Encoding.UTF8.GetString(sourceLinkUTF8), new { documents = (Dictionary?)null });
+ return parseResult.documents.Select(makeSourceLink).ToImmutableArray();
+
+ static SourceLink makeSourceLink(KeyValuePair entry)
+ {
+ // TODO: determine if this subsitution is correct
+ var (key, value) = (entry.Key, entry.Value); // TODO: use Deconstruct in .NET Core
+ var prefix = key.Remove(key.LastIndexOf("*"));
+ var replace = value.Remove(value.LastIndexOf("*"));
+ return new SourceLink(prefix, replace);
+ }
+ }
+
+ public byte[]? GetSourceLinkUTF8()
+ {
+ if (_sourceLinkUTF8 is null && TryGetCustomDebugInformationBlobReader(SourceLinkGuid, out var optionsBlob))
+ {
+ _sourceLinkUTF8 = optionsBlob.ReadBytes(optionsBlob.Length);
+ }
+ return _sourceLinkUTF8;
}
public ImmutableArray GetMetadataReferences()
{
if (_metadataReferenceInfo.IsDefault)
{
- var referencesBlob = GetCustomDebugInformationBlobReader(MetadataReferenceInfoGuid);
+ if (!TryGetCustomDebugInformationBlobReader(MetadataReferenceInfoGuid, out var referencesBlob))
+ {
+ throw new InvalidOperationException();
+ }
+
_metadataReferenceInfo = ParseMetadataReferenceInfo(referencesBlob).ToImmutableArray();
}
return _metadataReferenceInfo;
}
- public OutputKind GetOutputKind() => OutputKind.DynamicallyLinkedLibrary;
+ public OutputKind GetOutputKind() =>
+ (PdbReader.DebugMetadataHeader is { } header && !header.EntryPoint.IsNil)
+ ? OutputKind.ConsoleApplication
+ : OutputKind.DynamicallyLinkedLibrary;
+
+ public string? GetMainTypeName() => GetMainMethodInfo() is { } tuple
+ ? tuple.MainTypeName
+ : null;
+
+ public string? GetMainMethodName() => GetMainMethodInfo() is { } tuple
+ ? tuple.MainMethodName
+ : null;
+
+ private (string MainTypeName, string MainMethodName)? GetMainMethodInfo()
+ {
+ if (!(PdbReader.DebugMetadataHeader is { } header) ||
+ header.EntryPoint.IsNil)
+ {
+ return null;
+ }
+
+ var mdReader = PeReader.GetMetadataReader();
+ var methodDefinition = mdReader.GetMethodDefinition(header.EntryPoint);
+ var methodName = mdReader.GetString(methodDefinition.Name);
+ var typeHandle = methodDefinition.GetDeclaringType();
+ var typeDefinition = mdReader.GetTypeDefinition(typeHandle);
+ var typeName = mdReader.GetString(typeDefinition.Name);
+ if (!typeDefinition.Namespace.IsNil)
+ {
+ var namespaceName = mdReader.GetString(typeDefinition.Namespace);
+ typeName = namespaceName + "." + typeName;
+ }
+
+ return (typeName, methodName);
+ }
+
+ private SourceText? ResolveEmbeddedSource(DocumentHandle document, SourceHashAlgorithm hashAlgorithm, Encoding encoding)
+ {
+ byte[] bytes = (from handle in PdbReader.GetCustomDebugInformation(document)
+ let cdi = PdbReader.GetCustomDebugInformation(handle)
+ where PdbReader.GetGuid(cdi.Kind) == EmbeddedSourceGuid
+ select PdbReader.GetBlobBytes(cdi.Value)).SingleOrDefault();
+
+ if (bytes == null)
+ {
+ return null;
+ }
+
+ int uncompressedSize = BitConverter.ToInt32(bytes, 0);
+ var stream = new MemoryStream(bytes, sizeof(int), bytes.Length - sizeof(int));
+
+ if (uncompressedSize != 0)
+ {
+ var decompressed = new MemoryStream(uncompressedSize);
- public IEnumerable GetSourceFileNames()
+ using (var deflater = new DeflateStream(stream, CompressionMode.Decompress))
+ {
+ deflater.CopyTo(decompressed);
+ }
+
+ if (decompressed.Length != uncompressedSize)
+ {
+ throw new InvalidDataException();
+ }
+
+ stream = decompressed;
+ }
+
+ using (stream)
+ {
+ // todo: IVT and EncodedStringText.Create?
+ return SourceText.From(stream, encoding: encoding, checksumAlgorithm: hashAlgorithm, canBeEmbedded: true);
+ }
+ }
+
+ public byte[]? GetPublicKey()
+ {
+ var metadataReader = PeReader.GetMetadataReader();
+ var blob = metadataReader.GetAssemblyDefinition().PublicKey;
+ if (blob.IsNil)
+ {
+ return null;
+ }
+
+ var reader = metadataReader.GetBlobReader(blob);
+ return reader.ReadBytes(reader.Length);
+ }
+
+ public unsafe ResourceDescription[]? GetManifestResources()
+ {
+ var metadataReader = PeReader.GetMetadataReader();
+ if (PeReader.PEHeaders.CorHeader is not { } corHeader
+ || !PeReader.PEHeaders.TryGetDirectoryOffset(corHeader.ResourcesDirectory, out var resourcesOffset))
+ {
+ return null;
+ }
+
+ var result = metadataReader.ManifestResources.Select(handle =>
+ {
+ var resource = metadataReader.GetManifestResource(handle);
+ var name = metadataReader.GetString(resource.Name);
+
+ var resourceStart = PeReader.GetEntireImage().Pointer + resourcesOffset + resource.Offset;
+ var length = *(int*)resourceStart;
+ var contentPtr = resourceStart + sizeof(int);
+ var content = new byte[length];
+ Marshal.Copy(new IntPtr(contentPtr), content, 0, length);
+
+ var isPublic = (resource.Attributes & ManifestResourceAttributes.Public) != 0;
+ var description = new ResourceDescription(name, dataProvider: () => new MemoryStream(content), isPublic);
+ return description;
+ }).ToArray();
+
+ return result;
+ }
+
+ public ImmutableArray GetSourceFileInfos(Encoding encoding)
{
- foreach (var documentHandle in _metadataReader.Documents)
+ var sourceFileCount = int.Parse(
+ GetMetadataCompilationOptions()
+ .GetUniqueOption(CompilationOptionNames.SourceFileCount));
+
+ var builder = ImmutableArray.CreateBuilder(sourceFileCount);
+ foreach (var documentHandle in PdbReader.Documents.Take(sourceFileCount))
{
- var document = _metadataReader.GetDocument(documentHandle);
- yield return _metadataReader.GetString(document.Name);
+ var document = PdbReader.GetDocument(documentHandle);
+ var name = PdbReader.GetString(document.Name);
+
+ var hashAlgorithmGuid = PdbReader.GetGuid(document.HashAlgorithm);
+ var hashAlgorithm =
+ hashAlgorithmGuid == HashAlgorithmSha1 ? SourceHashAlgorithm.Sha1
+ : hashAlgorithmGuid == HashAlgorithmSha256 ? SourceHashAlgorithm.Sha256
+ : SourceHashAlgorithm.None;
+
+ var hash = PdbReader.GetBlobBytes(document.Hash);
+ var embeddedContent = ResolveEmbeddedSource(documentHandle, hashAlgorithm, encoding);
+
+ builder.Add(new SourceFileInfo(name, hashAlgorithm, hash, embeddedContent));
}
+
+ return builder.MoveToImmutable();
}
private static IEnumerable ParseMetadataReferenceInfo(BlobReader blobReader)
@@ -117,19 +354,21 @@ private static IEnumerable ParseMetadataReferenceInfo(Blo
}
}
- private BlobReader GetCustomDebugInformationBlobReader(Guid infoGuid)
+ private bool TryGetCustomDebugInformationBlobReader(Guid infoGuid, out BlobReader blobReader)
{
- var blobs = from cdiHandle in _metadataReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition)
- let cdi = _metadataReader.GetCustomDebugInformation(cdiHandle)
- where _metadataReader.GetGuid(cdi.Kind) == infoGuid
- select _metadataReader.GetBlobReader(cdi.Value);
+ var blobs = from cdiHandle in PdbReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition)
+ let cdi = PdbReader.GetCustomDebugInformation(cdiHandle)
+ where PdbReader.GetGuid(cdi.Kind) == infoGuid
+ select PdbReader.GetBlobReader(cdi.Value);
if (blobs.Any())
{
- return blobs.Single();
+ blobReader = blobs.Single();
+ return true;
}
- throw new InvalidDataException($"No blob found for {infoGuid}");
+ blobReader = default;
+ return false;
}
private static ImmutableArray<(string, string)> ParseCompilationOptions(BlobReader blobReader)
diff --git a/src/Tools/BuildValidator/DemoLogger.cs b/src/Tools/BuildValidator/DemoLogger.cs
new file mode 100644
index 0000000000000..055a3088cd868
--- /dev/null
+++ b/src/Tools/BuildValidator/DemoLogger.cs
@@ -0,0 +1,60 @@
+// 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.Collections.Generic;
+using System.Text;
+using Microsoft.Extensions.Logging;
+
+namespace BuildValidator
+{
+ internal sealed class DemoLogger : ILogger
+ {
+ private const int IndentIncrement = 2;
+
+ private sealed class Scope : IDisposable
+ {
+ private readonly DemoLogger _demoLogger;
+
+ public Scope(DemoLogger demoLogger)
+ {
+ _demoLogger = demoLogger;
+ _demoLogger._indent += IndentIncrement;
+ }
+
+ public void Dispose()
+ {
+ _demoLogger._indent -= IndentIncrement;
+ }
+ }
+
+ private int _indent;
+
+ public IDisposable BeginScope(TState state)
+ {
+ LogCore(state?.ToString());
+ return new Scope(this);
+ }
+
+ public bool IsEnabled(LogLevel logLevel) => true;
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) =>
+ LogCore(formatter(state, exception));
+
+ private void LogCore(string? message)
+ {
+ Console.Write(new string(' ', _indent));
+ Console.WriteLine(message);
+ }
+ }
+
+ internal sealed class DemoLoggerProvider : ILoggerProvider
+ {
+ public ILogger CreateLogger(string categoryName) => new DemoLogger();
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/src/Tools/BuildValidator/LocalReferenceResolver.cs b/src/Tools/BuildValidator/LocalReferenceResolver.cs
index 75942498a53de..72de32d852b86 100644
--- a/src/Tools/BuildValidator/LocalReferenceResolver.cs
+++ b/src/Tools/BuildValidator/LocalReferenceResolver.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -26,11 +27,25 @@ internal class LocalReferenceResolver
private readonly HashSet _indexDirectories = new HashSet();
private readonly ILogger _logger;
- public LocalReferenceResolver(ILoggerFactory loggerFactory)
+ public LocalReferenceResolver(Options options, ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger();
- _indexDirectories.Add(GetArtifactsDirectory());
+ foreach (var directoryInfo in GetRefAssembliesDirectories())
+ {
+ _indexDirectories.Add(directoryInfo);
+ }
+ _indexDirectories.Add(new DirectoryInfo(options.AssembliesPath));
_indexDirectories.Add(GetNugetCacheDirectory());
+ foreach (var path in options.ReferencesPaths)
+ {
+ _indexDirectories.Add(new DirectoryInfo(path));
+ }
+
+ using var _ = _logger.BeginScope("Assembly Reference Search Paths");
+ foreach (var directory in _indexDirectories)
+ {
+ _logger.LogInformation($@"""{directory}""");
+ }
}
public static DirectoryInfo GetNugetCacheDirectory()
@@ -44,12 +59,32 @@ public static DirectoryInfo GetNugetCacheDirectory()
return new DirectoryInfo(nugetPackageDirectory);
}
+ public static DirectoryInfo[] GetRefAssembliesDirectories()
+ {
+ // TODO: Don't hardcode the paths here.
+ return new[]
+ {
+ new DirectoryInfo(@"C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref"),
+ new DirectoryInfo(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref")
+ };
+ }
+
+ public string GetReferencePath(MetadataReferenceInfo referenceInfo)
+ {
+ if (_cache.TryGetValue(referenceInfo.Mvid, out var value))
+ {
+ return value;
+ }
+
+ throw new Exception($"Could not find referenced assembly {referenceInfo}");
+ }
+
public ImmutableArray ResolveReferences(IEnumerable references)
{
var referenceArray = references.ToImmutableArray();
CacheNames(referenceArray);
- var files = referenceArray.Select(r => _cache[r.Mvid]);
+ var files = referenceArray.Select(r => GetReferencePath(r));
var metadataReferences = files.Select(f => MetadataReference.CreateFromFile(f)).Cast().ToImmutableArray();
return metadataReferences;
@@ -76,7 +111,7 @@ public void CacheNames(ImmutableArray names)
continue;
}
- if (!(GetMvidForFile(file) is { } mvid) || !_cache.ContainsKey(mvid))
+ if (GetMvidForFile(file) is not { } mvid || _cache.ContainsKey(mvid))
{
continue;
}
@@ -96,7 +131,12 @@ public void CacheNames(ImmutableArray names)
if (uncached.Any())
{
- _logger.LogDebug($"Unable to find files for the following metadata references: {uncached}");
+ using var _ = _logger.BeginScope($"Missing metadata references:");
+ foreach (var missingReference in uncached)
+ {
+ _logger.LogError($@"{missingReference.Name} - {missingReference.Mvid}");
+ }
+ throw new FileNotFoundException();
}
}
@@ -118,23 +158,5 @@ public void CacheNames(ImmutableArray names)
}
}
}
-
- public static DirectoryInfo GetArtifactsDirectory()
- {
- var assemblyLocation = typeof(LocalReferenceResolver).Assembly.Location;
- var binDir = Directory.GetParent(assemblyLocation);
-
- while (binDir != null && !binDir.FullName.EndsWith("artifacts\\bin", FileNameEqualityComparer.StringComparison))
- {
- binDir = binDir.Parent;
- }
-
- if (binDir == null)
- {
- throw new Exception();
- }
-
- return binDir;
- }
}
}
diff --git a/src/Tools/BuildValidator/LocalSourceResolver.cs b/src/Tools/BuildValidator/LocalSourceResolver.cs
index 753f35d090ebf..3efff5de6dad6 100644
--- a/src/Tools/BuildValidator/LocalSourceResolver.cs
+++ b/src/Tools/BuildValidator/LocalSourceResolver.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
@@ -12,76 +13,61 @@
namespace BuildValidator
{
- ///
- /// Roslyn specific implementation for looking for files
- /// in the Roslyn repo
- ///
+ internal record ResolvedSource(
+ string? OnDiskPath,
+ SourceText SourceText,
+ SourceFileInfo SourceFileInfo)
+ {
+ public string DisplayPath => OnDiskPath ?? ("[embedded]" + SourceFileInfo.SourceFilePath);
+ }
+
internal class LocalSourceResolver
{
- private readonly DirectoryInfo _baseDirectory;
+ private readonly Options _options;
private readonly ILogger _logger;
- public LocalSourceResolver(ILoggerFactory loggerFactory)
+ public LocalSourceResolver(Options options, ILoggerFactory loggerFactory)
{
- _baseDirectory = GetSourceDirectory();
+ _options = options;
_logger = loggerFactory.CreateLogger();
-
- _logger.LogInformation($"Source Base Directory: {_baseDirectory}");
}
- public SourceText ResolveSource(string name, Encoding encoding)
+ public ResolvedSource ResolveSource(SourceFileInfo sourceFileInfo, ImmutableArray sourceLinks, Encoding encoding)
{
- if (!File.Exists(name))
- {
- _logger.LogTrace($"{name} doesn't exist, adding base directory");
- name = Path.Combine(_baseDirectory.FullName, name);
- }
- if (File.Exists(name))
+ var pdbDocumentPath = sourceFileInfo.SourceFilePath;
+ if (sourceFileInfo.EmbeddedText is { } embeddedText)
{
- using var fileStream = File.OpenRead(name);
- var sourceText = SourceText.From(fileStream, encoding: encoding);
- return sourceText;
+ return new ResolvedSource(OnDiskPath: null, embeddedText, sourceFileInfo);
}
-
- throw new FileNotFoundException(name);
- }
-
- private static DirectoryInfo GetSourceDirectory()
- {
- var assemblyLocation = typeof(LocalSourceResolver).Assembly.Location;
- var srcDir = Directory.GetParent(assemblyLocation);
-
- while (srcDir != null)
+ else
{
- var potentialDir = srcDir.GetDirectories().FirstOrDefault(IsSourceDirectory);
- if (potentialDir is null)
- {
- srcDir = srcDir.Parent;
- }
- else
+ string? onDiskPath = null;
+ foreach (var link in sourceLinks)
{
- srcDir = potentialDir;
- break;
+ if (sourceFileInfo.SourceFilePath.StartsWith(link.Prefix))
+ {
+ onDiskPath = Path.GetFullPath(Path.Combine(_options.SourcePath, pdbDocumentPath.Substring(link.Prefix.Length)));
+ if (File.Exists(onDiskPath))
+ {
+ break;
+ }
+ }
}
- }
-
- if (srcDir == null)
- {
- throw new Exception("Unable to find src directory");
- }
- return srcDir;
+ // if no source links exist to let us prefix the source path,
+ // then assume the file path in the pdb points to the on-disk location of the file.
+ onDiskPath ??= pdbDocumentPath;
- static bool IsSourceDirectory(DirectoryInfo directoryInfo)
- {
- if (FileNameEqualityComparer.StringComparer.Equals(directoryInfo.Name, "src"))
+ using var fileStream = File.OpenRead(onDiskPath);
+ var sourceText = SourceText.From(fileStream, encoding: encoding, checksumAlgorithm: SourceHashAlgorithm.Sha256, canBeEmbedded: false);
+ if (!sourceText.GetChecksum().AsSpan().SequenceEqual(sourceFileInfo.Hash))
{
- // Check that src/compilers exists to be more accurate about getting the correct src directory
- return directoryInfo.GetDirectories().Any(d => FileNameEqualityComparer.StringComparer.Equals(d.Name, "compilers"));
+ _logger.LogError($@"File ""{onDiskPath}"" has incorrect hash");
}
-
- return false;
+ return new ResolvedSource(onDiskPath, sourceText, sourceFileInfo);
}
+
+ throw new FileNotFoundException(pdbDocumentPath);
}
}
}
diff --git a/src/Tools/BuildValidator/MetadataCompilationOptions.cs b/src/Tools/BuildValidator/MetadataCompilationOptions.cs
index 28218a1928e0b..3c3e9d5b4c8f0 100644
--- a/src/Tools/BuildValidator/MetadataCompilationOptions.cs
+++ b/src/Tools/BuildValidator/MetadataCompilationOptions.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System;
+using Microsoft.Extensions.Logging;
namespace BuildValidator
{
@@ -20,11 +21,18 @@ public MetadataCompilationOptions(ImmutableArray<(string optionName, string valu
public int Length => _options.Length;
+ public bool TryGetUniqueOption(ILogger logger, string optionName, [NotNullWhen(true)] out string? value)
+ {
+ var result = TryGetUniqueOption(optionName, out value);
+ logger.LogInformation($"{optionName} - {value}");
+ return result;
+ }
+
///
/// Attempts to get an option value. Returns false if the option value does not
/// exist OR if it exists more than once
///
- public bool TryGetUniqueOption(string optionName, out string? value)
+ public bool TryGetUniqueOption(string optionName, [NotNullWhen(true)] out string? value)
{
value = null;
diff --git a/src/Tools/BuildValidator/Options.cs b/src/Tools/BuildValidator/Options.cs
index 91376a845bfd8..b7a23f5e1c982 100644
--- a/src/Tools/BuildValidator/Options.cs
+++ b/src/Tools/BuildValidator/Options.cs
@@ -2,44 +2,19 @@
// 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.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
using System.IO;
namespace BuildValidator
{
- internal class Options
- {
- private Options()
- { }
-
- public bool ConsoleOutput { get; private set; } = true;
- public bool Verbose { get; private set; }
- public bool IgnoreCompilerVersion { get; private set; }
-
- public static Options Create(string[] args)
- {
- var options = new Options();
-
- for (var i = 0; i < args.Length;)
- {
- var arg = args[i++];
-
- switch (arg)
- {
- case "/verbose":
- options.Verbose = true;
- break;
-
- case "/quiet":
- options.ConsoleOutput = false;
- break;
-
- case "/ignorecompilerversion":
- options.IgnoreCompilerVersion = true;
- break;
- }
- }
-
- return options;
- }
- }
+ internal record Options(
+ string AssembliesPath,
+ string[] ReferencesPaths,
+ string SourcePath,
+ bool Verbose,
+ bool Quiet,
+ bool Debug,
+ string DebugPath);
}
diff --git a/src/Tools/BuildValidator/Program.cs b/src/Tools/BuildValidator/Program.cs
index 7367832c32a65..941eea0f318b1 100644
--- a/src/Tools/BuildValidator/Program.cs
+++ b/src/Tools/BuildValidator/Program.cs
@@ -4,8 +4,11 @@
using System;
using System.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
+using System.Net.Http.Headers;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
@@ -14,6 +17,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
namespace BuildValidator
{
@@ -24,7 +28,9 @@ namespace BuildValidator
///
class Program
{
- private static ILogger? s_logger;
+ const int ExitSuccess = 0;
+ const int ExitFailure = 1;
+
private static readonly Regex[] s_ignorePatterns = new Regex[]
{
new Regex(@"\\runtimes?\\"),
@@ -32,105 +38,151 @@ class Program
new Regex(@"\.resources?\.")
};
- static void Main(string[] args)
+ static int Main(string[] args)
{
- Options options;
- try
+ var rootCommand = new RootCommand
{
- options = Options.Create(args);
- }
- catch (InvalidDataException)
+ new Option(
+ "--assembliesPath", "Path to assemblies to rebuild"
+ ) { IsRequired = true },
+ new Option(
+ "--sourcePath", "Path to sources to use in rebuild"
+ ) { IsRequired = true },
+ new Option(
+ "--referencesPaths", "Additional paths to referenced assemblies"
+ ),
+ new Option(
+ "--verbose", "Output verbose log information"
+ ),
+ new Option(
+ "--quiet", "Do not output log information to console"
+ ),
+ new Option(
+ "--debug", "Output debug info when rebuild is not equal to the original"
+ ),
+ new Option(
+ "--debugPath", "Path to output debug info. Defaults to the user temp directory. Note that a unique debug path should be specified for every instance of the tool running with `--debug` enabled."
+ )
+ };
+ rootCommand.Handler = CommandHandler.Create(HandleCommand);
+ return rootCommand.Invoke(args);
+ }
+
+ static int HandleCommand(string assembliesPath, string sourcePath, string[]? referencesPaths, bool verbose, bool quiet, bool debug, string? debugPath)
+ {
+ // If user provided a debug path then assume we should write debug outputs.
+ debug |= debugPath is object;
+ debugPath ??= Path.Combine(Path.GetTempPath(), $"BuildValidator");
+ referencesPaths ??= Array.Empty();
+
+ var options = new Options(assembliesPath, referencesPaths, sourcePath, verbose, quiet, debug, debugPath);
+
+ // TODO: remove the DemoLoggerProvider, update this dependency,
+ // and move to the built in logger.
+ var loggerFactory = new LoggerFactory(
+ new[] { new ConsoleLoggerProvider(new ConsoleLoggerSettings()) },
+ new LoggerFilterOptions()
+ {
+ MinLevel = options.Verbose ? LogLevel.Trace : LogLevel.Information
+ });
+
+ if (!options.Quiet)
{
- PrintHelp();
- return;
+ loggerFactory.AddProvider(new DemoLoggerProvider());
}
- var loggerFactory = new LoggerFactory(Enumerable.Empty(), new LoggerFilterOptions()
+ var logger = loggerFactory.CreateLogger();
+ try
{
- MinLevel = options.Verbose ? LogLevel.Trace : LogLevel.Information
- });
-
- if (options.ConsoleOutput)
+ var fullDebugPath = Path.GetFullPath(debugPath);
+ logger.LogInformation($@"Using debug folder: ""{fullDebugPath}""");
+ Directory.Delete(debugPath, recursive: true);
+ logger.LogInformation($@"Cleaned debug folder: ""{fullDebugPath}""");
+ }
+ catch (IOException)
{
- loggerFactory.AddConsole();
+ // no-op
}
- s_logger = loggerFactory.CreateLogger();
-
- var sourceResolver = new LocalSourceResolver(loggerFactory);
- var referenceResolver = new LocalReferenceResolver(loggerFactory);
+ try
+ {
+ var sourceResolver = new LocalSourceResolver(options, loggerFactory);
+ var referenceResolver = new LocalReferenceResolver(options, loggerFactory);
- var buildConstructor = new BuildConstructor(referenceResolver, sourceResolver);
+ var buildConstructor = new BuildConstructor(referenceResolver, sourceResolver, logger);
- var artifactsDir = LocalReferenceResolver.GetArtifactsDirectory();
- var thisCompilerVersion = options.IgnoreCompilerVersion
- ? null
- : typeof(Compilation).Assembly.GetCustomAttribute()?.InformationalVersion;
+ var artifactsDir = new DirectoryInfo(options.AssembliesPath);
- var filesToValidate = artifactsDir.EnumerateFiles("*.exe", SearchOption.AllDirectories)
- .Concat(artifactsDir.EnumerateFiles("*.dll", SearchOption.AllDirectories))
- .Distinct(FileNameEqualityComparer.Instance);
+ var filesToValidate = artifactsDir.EnumerateFiles("*.exe", SearchOption.AllDirectories)
+ .Concat(artifactsDir.EnumerateFiles("*.dll", SearchOption.AllDirectories))
+ .Distinct(FileNameEqualityComparer.Instance);
- ValidateFiles(filesToValidate, buildConstructor, thisCompilerVersion);
+ var success = ValidateFiles(filesToValidate, buildConstructor, logger, options);
+ Console.Out.Flush();
+ return success ? ExitSuccess : ExitFailure;
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, ex.Message);
+ throw;
+ }
}
- private static void ValidateFiles(IEnumerable files, BuildConstructor buildConstructor, string? thisCompilerVersion)
+ // TODO: it feels like "logger" and "options" should be instance variables of something
+ private static bool ValidateFiles(IEnumerable originalBinaries, BuildConstructor buildConstructor, ILogger logger, Options options)
{
var assembliesCompiled = new List();
-
- foreach (var file in files)
+ foreach (var file in originalBinaries)
{
- var compilationDiff = ValidateFile(file, buildConstructor, thisCompilerVersion);
+ var compilationDiff = ValidateFile(file, buildConstructor, logger, options);
if (compilationDiff is null)
{
+ logger.LogInformation($"Ignoring {file.FullName}");
continue;
}
assembliesCompiled.Add(compilationDiff);
}
- var sb = new StringBuilder();
+ bool success = true;
- sb.AppendLine("====================");
- sb.AppendLine("Summary:");
- sb.AppendLine();
- sb.AppendLine("Successful Tests:");
-
- foreach (var diff in assembliesCompiled.Where(a => a.AreEqual == true))
+ using var summary = logger.BeginScope("Summary");
+ using (logger.BeginScope("Successful rebuilds"))
{
- sb.AppendLine($"\t{diff.OriginalPath}");
+ foreach (var diff in assembliesCompiled.Where(a => a.AreEqual == true))
+ {
+ logger.LogInformation($"\t{diff.OriginalPath}");
+ }
}
- sb.AppendLine();
-
- sb.AppendLine("Failed Tests:");
- foreach (var diff in assembliesCompiled.Where(a => a.AreEqual == false))
+ using (logger.BeginScope("Rebuilds with output differences"))
{
- sb.AppendLine($"\t{diff.OriginalPath}");
+ foreach (var diff in assembliesCompiled.Where(a => a.AreEqual == false))
+ {
+ // TODO: can we include the path to any diff artifacts?
+ logger.LogWarning($"\t{diff.OriginalPath}");
+ success = false;
+ }
}
- sb.AppendLine();
- sb.AppendLine("Error Cases:");
- foreach (var diff in assembliesCompiled.Where(a => !a.AreEqual.HasValue))
+ using (logger.BeginScope("Rebuilds with compilation errors"))
{
- sb.AppendLine($"\t{diff.OriginalPath}");
- if (diff.Exception != null)
+ foreach (var diff in assembliesCompiled.Where(a => a.AreEqual == null))
{
- sb.AppendLine($"\tException: {diff.Exception.Message}");
+ logger.LogError($"{diff.OriginalPath} had {diff.Diagnostics.Length} diagnostics.");
+ success = false;
}
}
- sb.AppendLine("====================");
- s_logger.LogInformation(sb.ToString());
+ return success;
}
- private static CompilationDiff? ValidateFile(FileInfo file, BuildConstructor buildConstructor, string? thisCompilerVersion)
+ private static CompilationDiff? ValidateFile(FileInfo originalBinary, BuildConstructor buildConstructor, ILogger logger, Options options)
{
-
- if (s_ignorePatterns.Any(r => r.IsMatch(file.FullName)))
+ if (s_ignorePatterns.Any(r => r.IsMatch(originalBinary.FullName)))
{
- s_logger.LogTrace($"Ignoring {file.FullName}");
+ logger.LogTrace($"Ignoring {originalBinary.FullName}");
return null;
}
@@ -139,48 +191,55 @@ private static void ValidateFiles(IEnumerable files, BuildConstructor
try
{
// Find the embedded pdb
- using var fileStream = file.OpenRead();
- using var peReader = new PEReader(fileStream);
+ using var originalBinaryStream = originalBinary.OpenRead();
+ using var originalPeReader = new PEReader(originalBinaryStream);
- var pdbOpened = peReader.TryOpenAssociatedPortablePdb(
- peImagePath: file.FullName,
+ var pdbOpened = originalPeReader.TryOpenAssociatedPortablePdb(
+ peImagePath: originalBinary.FullName,
filePath => File.Exists(filePath) ? File.OpenRead(filePath) : null,
out pdbReaderProvider,
out var pdbPath);
if (!pdbOpened || pdbReaderProvider is null)
{
- s_logger.LogError($"Could not find pdb for {file.FullName}");
+ logger.LogError($"Could not find pdb for {originalBinary.FullName}");
return null;
}
- s_logger.LogInformation($"Compiling {file.FullName} with pdb {pdbPath ?? "[embedded]"}");
+ using var _ = logger.BeginScope($"Verifying {originalBinary.FullName} with pdb {pdbPath ?? "[embedded]"}");
- var reader = pdbReaderProvider.GetMetadataReader();
+ var pdbReader = pdbReaderProvider.GetMetadataReader();
+ var optionsReader = new CompilationOptionsReader(logger, pdbReader, originalPeReader);
- // TODO: Check compilation version using the PEReader
+ var compilation = buildConstructor.CreateCompilation(
+ optionsReader,
+ Path.GetFileNameWithoutExtension(originalBinary.Name));
- var compilation = buildConstructor.CreateCompilation(reader, file.Name);
- return CompilationDiff.Create(file, compilation);
- }
- catch (Exception e)
- {
- s_logger.LogError(e, file.FullName);
- return CompilationDiff.Create(file, e);
+ var compilationDiff = CompilationDiff.Create(originalBinary, optionsReader, compilation, getDebugEntryPoint(), logger, options);
+ return compilationDiff;
+
+ IMethodSymbol? getDebugEntryPoint()
+ {
+ if (optionsReader.GetMainTypeName() is { } mainTypeName &&
+ optionsReader.GetMainMethodName() is { } mainMethodName)
+ {
+ var typeSymbol = compilation.GetTypeByMetadataName(mainTypeName);
+ if (typeSymbol is object)
+ {
+ var methodSymbols = typeSymbol
+ .GetMembers(mainMethodName)
+ .OfType();
+ return methodSymbols.FirstOrDefault();
+ }
+ }
+
+ return null;
+ }
}
finally
{
pdbReaderProvider?.Dispose();
}
}
-
- private static void PrintHelp()
- {
- Console.WriteLine("Usage: BuildValidator [options]");
- Console.WriteLine("Options:");
- Console.WriteLine("/verbose Output verbose log information");
- Console.WriteLine("/quiet Do not output log information to console");
- Console.WriteLine("/ignorecompilerversion Do not verify compiler version that assemblies were generated with");
- }
}
}
diff --git a/src/Tools/BuildValidator/SourceLink.cs b/src/Tools/BuildValidator/SourceLink.cs
new file mode 100644
index 0000000000000..125ed77bcc0ad
--- /dev/null
+++ b/src/Tools/BuildValidator/SourceLink.cs
@@ -0,0 +1,23 @@
+// 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.Collections.Generic;
+using System.Text;
+
+namespace BuildValidator
+{
+ /// An entry in the source-link.json dictionary.
+ internal readonly struct SourceLink
+ {
+ public string Prefix { get; }
+ public string Replace { get; }
+
+ public SourceLink(string prefix, string replace)
+ {
+ Prefix = prefix;
+ Replace = replace;
+ }
+ }
+}
diff --git a/src/Tools/ManifestGenerator/ManifestGenerator.csproj b/src/Tools/ManifestGenerator/ManifestGenerator.csproj
new file mode 100644
index 0000000000000..7e0af0067323f
--- /dev/null
+++ b/src/Tools/ManifestGenerator/ManifestGenerator.csproj
@@ -0,0 +1,12 @@
+
+
+
+
+ Exe
+ net5.0
+ false
+
+
+
+
+
diff --git a/src/Tools/ManifestGenerator/Program.cs b/src/Tools/ManifestGenerator/Program.cs
new file mode 100644
index 0000000000000..df05cd5b531f2
--- /dev/null
+++ b/src/Tools/ManifestGenerator/Program.cs
@@ -0,0 +1,43 @@
+// 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.Threading.Tasks;
+using Mono.Options;
+
+internal static class Program
+{
+ internal const int ExitFailure = 1;
+ internal const int ExitSuccess = 0;
+
+ public static int Main(string[] args)
+ {
+ string? dllPath = null;
+ string? pdbPath = null;
+
+ var options = new OptionSet()
+ {
+ { "dll=", "Path to assembly file", (string s) => dllPath = s },
+ { "pdb=", "Path to PDB file", (string s) => pdbPath = s }
+ };
+ options.Parse(args);
+
+ if (dllPath is null)
+ {
+ Console.Error.WriteLine($"--dll is required");
+ return ExitFailure;
+ }
+
+ if (pdbPath is null)
+ {
+ Console.Error.WriteLine($"--pdb is required");
+ return ExitFailure;
+ }
+
+ Console.WriteLine("DLL path: " + dllPath);
+ Console.WriteLine("PDB path: " + pdbPath);
+
+ return ExitSuccess;
+ }
+}
diff --git a/src/Tools/ManifestGenerator/README.md b/src/Tools/ManifestGenerator/README.md
new file mode 100644
index 0000000000000..ef0eaaf06037e
--- /dev/null
+++ b/src/Tools/ManifestGenerator/README.md
@@ -0,0 +1,4 @@
+# dotnet-roslyn-manifest-generator
+
+https://github.com/dotnet/roslyn/blob/efd69176d6ad7f9dbe1d87525201760a6c44e7a0/docs/compilers/terrapin.md#artifacts-manifest-file
+
diff --git a/src/Tools/Source/RunTests/RunTests.csproj b/src/Tools/Source/RunTests/RunTests.csproj
index ad17ab0a98755..69d50f50eab30 100644
--- a/src/Tools/Source/RunTests/RunTests.csproj
+++ b/src/Tools/Source/RunTests/RunTests.csproj
@@ -7,6 +7,7 @@
true
false
false
+ true