From bf2a49d13b50ba62604d4a3b063dd3446ff98f77 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 17 Feb 2021 15:11:20 -0800 Subject: [PATCH] Rebuild Microsoft.CodeAnalysis (#50968) Co-authored-by: Jared Parsons --- .vscode/launch.json | 16 + azure-pipelines.yml | 35 +++ eng/Versions.props | 3 +- eng/build-utils.ps1 | 2 +- eng/test-rebuild.ps1 | 43 +++ .../CSharp/Test/Emit/PDB/PDBAsyncTests.cs | 12 +- .../CSharp/Test/Emit/PDB/PDBIteratorTests.cs | 25 +- .../Test/Emit/PDB/PDBSourceLinkTests.cs | 1 + .../CSharp/Test/Emit/PDB/PDBTests.cs | 4 + .../Portable/CommandLine/CommonCompiler.cs | 1 + .../Core/Portable/Compilation/Compilation.cs | 38 +++ .../Compilation/CompilationOptions.cs | 2 +- .../Portable/Microsoft.CodeAnalysis.csproj | 4 +- .../PEWriter/MetadataWriter.PortablePdb.cs | 73 +++-- .../Core/Portable/PEWriter/MetadataWriter.cs | 6 +- .../Core/Portable/PEWriter/PeWriter.cs | 2 + .../Core/Compilation/CompilationExtensions.cs | 1 + .../Core/Compilation/IRuntimeEnvironment.cs | 1 + .../Test/Emit/PDB/PDBAsyncTests.vb | 15 +- .../Test/Emit/PDB/PDBIteratorTests.vb | 22 +- .../ExpressionCompiler/EvaluationContext.cs | 4 + .../ExpressionCompilerTestHelpers.cs | 4 +- .../ExpressionCompiler/EvaluationContext.vb | 3 + src/Tools/BuildValidator/BuildConstructor.cs | 161 ++++++---- .../BuildValidator/BuildValidator.csproj | 12 +- src/Tools/BuildValidator/BuildValidator.sln | 25 ++ src/Tools/BuildValidator/CompilationDiff.cs | 169 +++++++++-- .../CompilationOptionsReader.cs | 283 ++++++++++++++++-- src/Tools/BuildValidator/DemoLogger.cs | 60 ++++ .../BuildValidator/LocalReferenceResolver.cs | 68 +++-- .../BuildValidator/LocalSourceResolver.cs | 88 +++--- .../MetadataCompilationOptions.cs | 10 +- src/Tools/BuildValidator/Options.cs | 47 +-- src/Tools/BuildValidator/Program.cs | 221 +++++++++----- src/Tools/BuildValidator/SourceLink.cs | 23 ++ .../ManifestGenerator.csproj | 12 + src/Tools/ManifestGenerator/Program.cs | 43 +++ src/Tools/ManifestGenerator/README.md | 4 + src/Tools/Source/RunTests/RunTests.csproj | 1 + 39 files changed, 1173 insertions(+), 371 deletions(-) create mode 100644 eng/test-rebuild.ps1 create mode 100644 src/Tools/BuildValidator/BuildValidator.sln create mode 100644 src/Tools/BuildValidator/DemoLogger.cs create mode 100644 src/Tools/BuildValidator/SourceLink.cs create mode 100644 src/Tools/ManifestGenerator/ManifestGenerator.csproj create mode 100644 src/Tools/ManifestGenerator/Program.cs create mode 100644 src/Tools/ManifestGenerator/README.md 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 e557a8282a42a..232567d6adb1d 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 e5cb498af0921..b28a6bb9d620c 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -2454,6 +2454,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) { @@ -2549,6 +2578,7 @@ public EmitResult Emit( debugEntryPoint, sourceLinkStream, embeddedTexts, + pdbOptionsBlobReader, testData: null, cancellationToken: cancellationToken); } @@ -2568,6 +2598,7 @@ internal EmitResult Emit( IMethodSymbol? debugEntryPoint, Stream? sourceLinkStream, IEnumerable? embeddedTexts, + BlobReader? pdbOptionsBlobReader, CompilationTestData? testData, CancellationToken cancellationToken) { @@ -2648,6 +2679,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, @@ -2809,6 +2841,7 @@ internal bool SerializeToPeStream( EmitStreamProvider peStreamProvider, EmitStreamProvider? metadataPEStreamProvider, EmitStreamProvider? pdbStreamProvider, + BlobReader? pdbOptionsBlobReader, Func? testSymWriterFactory, DiagnosticBag diagnostics, EmitOptions emitOptions, @@ -2882,6 +2915,7 @@ internal bool SerializeToPeStream( getPortablePdbStream, nativePdbWriter, pePdbFilePath, + pdbOptionsBlobReader, emitOptions.EmitMetadataOnly, emitOptions.IncludePrivateMembers, deterministic, @@ -2963,6 +2997,7 @@ internal static bool SerializePeToStream( Func? getPortablePdbStreamOpt, Cci.PdbWriter? nativePdbWriterOpt, string? pdbPathOpt, + BlobReader? pdbOptionsBlobReader, bool metadataOnly, bool includePrivateMembers, bool isDeterministic, @@ -2980,6 +3015,7 @@ internal static bool SerializePeToStream( getPeStream, getPortablePdbStreamOpt, nativePdbWriterOpt, + pdbOptionsBlobReader, pdbPathOpt, metadataOnly, deterministicPrimaryOutput, @@ -3002,6 +3038,7 @@ internal static bool SerializePeToStream( getMetadataPeStreamOpt, getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, + pdbOptionsBlobReader: null, pdbPathOpt: null, metadataOnly: true, isDeterministic: true, @@ -3057,6 +3094,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 dfb6bf08da6ce..b6221f785c822 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -69,6 +69,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