Fix missing pdbs in Blazor build (#20257)

* Fix missing pdbs in Blazor build

* Allow tests to run in Debug and Release independent of the configuration test project is built in

Fixes https://github.com/dotnet/aspnetcore/issues/20242
This commit is contained in:
Pranav K 2020-03-28 14:50:11 -07:00 committed by GitHub
parent c379bae8e9
commit 56b944f46c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 175 additions and 55 deletions

View File

@ -10,9 +10,11 @@
<PropertyGroup>
<ComponentsRoot Condition="'$(ComponentsRoot)'==''">$(MSBuildThisFileDirectory)..\..\..\</ComponentsRoot>
<_BlazorJsPath>$(ComponentsRoot)Web.JS\dist\$(Configuration)\blazor.webassembly.js</_BlazorJsPath>
<_BlazorJsMapPath>$(ComponentsRoot)Web.JS\dist\$(Configuration)\blazor.webassembly.js.map</_BlazorJsMapPath>
<_BlazorToolsDir>$(MSBuildThisFileDirectory)bin\$(Configuration)\tools\</_BlazorToolsDir>
<BlazorBuildConfiguration Condition="'$(BlazorBuildConfiguration)'==''">$(Configuration)</BlazorBuildConfiguration>
<_BlazorJsPath>$(ComponentsRoot)Web.JS\dist\$(BlazorBuildConfiguration)\blazor.webassembly.js</_BlazorJsPath>
<_BlazorJsMapPath>$(ComponentsRoot)Web.JS\dist\$(BlazorBuildConfiguration)\blazor.webassembly.js.map</_BlazorJsMapPath>
<_BlazorToolsDir>$(MSBuildThisFileDirectory)bin\$(BlazorBuildConfiguration)\tools\</_BlazorToolsDir>
</PropertyGroup>
<Target Name="Check_BlazorJSFiles" BeforeTargets="Build">

View File

@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
resourceList = resourceData.assembly;
break;
case "pdb":
resourceData.pdb = new ResourceHashesByNameDictionary();
resourceData.pdb ??= new ResourceHashesByNameDictionary();
resourceList = resourceData.pdb;
break;
case "satellite":

View File

@ -93,6 +93,7 @@
<_BlazorOutputWithTargetPath Include="@(_BlazorCopyLocalPaths)">
<!-- This group is for satellite assemblies. We set the resource name to include a path, e.g. "fr\\SomeAssembly.resources.dll" -->
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Culture)' == '' AND '%(_BlazorCopyLocalPaths.Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Culture)' != '' AND '%(_BlazorCopyLocalPaths.Extension)' == '.dll'">satellite</BootManifestResourceType>
<BootManifestResourceName>%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</BootManifestResourceName>
@ -108,9 +109,9 @@
<_BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)">
<BootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceName>%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</TargetOutputPath>
<BootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceName>%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
</_BlazorOutputWithTargetPath>
<_BlazorOutputWithTargetPath Include="@(_DotNetWasmRuntimeFile)">

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_WithLinkerAndCompression_IsIncremental()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_WithoutLinkerAndCompression_IsIncremental()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false");
Assert.BuildPassed(result);
@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_CompressesAllFrameworkFiles()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_DisabledCompression_DoesNotCompressFiles()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
// Act
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorEnableCompression=false");

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_WithLinker_IsIncremental()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);

View File

@ -15,7 +15,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_WithDefaultSettings_Works()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
project.Configuration = "Debug";
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
@ -27,12 +28,100 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", DotNetJsFileName);
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.pdb");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.pdb");
var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "standalone.StaticWebAssets.xml");
Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot"));
}
[Fact]
public async Task Build_InRelease_Works()
{
// Arrange
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
project.Configuration = "Release";
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
var buildOutputDirectory = project.BuildOutputDirectory;
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", DotNetJsFileName);
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.dll");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.pdb");
Assert.FileDoesNotExist(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "RazorClassLibrary.pdb");
var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "standalone.StaticWebAssets.xml");
Assert.FileContains(result, staticWebAssets, Path.Combine("netstandard2.1", "wwwroot"));
}
[Fact]
public async Task Build_ProducesBootJsonDataWithExpectedContent()
{
// Arrange
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
project.Configuration = "Debug";
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
var buildOutputDirectory = project.BuildOutputDirectory;
var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
var bootJsonData = ReadBootJsonData(result, bootJsonPath);
var runtime = bootJsonData.resources.runtime.Keys;
Assert.Contains(DotNetJsFileName, runtime);
Assert.Contains("dotnet.wasm", runtime);
var assemblies = bootJsonData.resources.assembly.Keys;
Assert.Contains("standalone.dll", assemblies);
Assert.Contains("RazorClassLibrary.dll", assemblies);
Assert.Contains("Microsoft.Extensions.Logging.Abstractions.dll", assemblies);
var pdb = bootJsonData.resources.pdb.Keys;
Assert.Contains("standalone.pdb", pdb);
Assert.Contains("RazorClassLibrary.pdb", pdb);
Assert.Null(bootJsonData.resources.satelliteResources);
}
[Fact]
public async Task Build_InRelease_ProducesBootJsonDataWithExpectedContent()
{
// Arrange
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
project.Configuration = "Release";
var result = await MSBuildProcessManager.DotnetMSBuild(project);
Assert.BuildPassed(result);
var buildOutputDirectory = project.BuildOutputDirectory;
var bootJsonPath = Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
var bootJsonData = ReadBootJsonData(result, bootJsonPath);
var runtime = bootJsonData.resources.runtime.Keys;
Assert.Contains(DotNetJsFileName, runtime);
Assert.Contains("dotnet.wasm", runtime);
var assemblies = bootJsonData.resources.assembly.Keys;
Assert.Contains("standalone.dll", assemblies);
Assert.Contains("RazorClassLibrary.dll", assemblies);
Assert.Contains("Microsoft.Extensions.Logging.Abstractions.dll", assemblies);
Assert.Null(bootJsonData.resources.pdb);
Assert.Null(bootJsonData.resources.satelliteResources);
}
[Fact]
public async Task Build_Hosted_Works()
{
@ -58,7 +147,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public async Task Build_WithLinkOnBuildDisabled_Works()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
project.AddProjectFileContent(
@"<PropertyGroup>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
@ -91,6 +180,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
<ItemGroup>
<ProjectReference Include=""..\classlibrarywithsatelliteassemblies\classlibrarywithsatelliteassemblies.csproj"" />
</ItemGroup>");
var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt");
File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
@ -123,6 +214,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
<ProjectReference Include=""..\classlibrarywithsatelliteassemblies\classlibrarywithsatelliteassemblies.csproj"" />
</ItemGroup>");
var resxfileInProject = Path.Combine(project.DirectoryPath, "Resources.ja.resx.txt");
File.Move(resxfileInProject, Path.Combine(project.DirectoryPath, "Resource.ja.resx"));
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
Assert.BuildPassed(result);
@ -134,9 +228,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll");
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
var bootJson = JsonSerializer.Deserialize<GenerateBlazorBootJson.BootJsonData>(
File.ReadAllText(Path.Combine(project.DirectoryPath, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json")),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
var bootJson = ReadBootJsonData(result, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"));
var satelliteResources = bootJson.resources.satelliteResources;
Assert.NotNull(satelliteResources);
@ -148,5 +240,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.Contains("ja", satelliteResources.Keys);
Assert.Contains("ja/standalone.resources.dll", satelliteResources["ja"].Keys);
}
private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path)
{
return JsonSerializer.Deserialize<GenerateBlazorBootJson.BootJsonData>(
File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
}
}

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
buildArgumentList.Add($"/t:{target}");
buildArgumentList.Add($"/p:Configuration={project.Configuration}");
buildArgumentList.Add($"/p:BlazorBuildConfiguration={ProjectDirectory.TestProjectConfiguration}");
buildArgumentList.Add(args);
var buildArguments = string.Join(" ", buildArgumentList);

View File

@ -14,6 +14,16 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
{
public bool PreserveWorkingDirectory { get; set; } = false;
// Configuration the test project is building in.
public static readonly string TestProjectConfiguration
#if DEBUG
= "Debug";
#elif RELEASE
= "Release";
#else
#error Configuration not supported
#endif
private static readonly string RepoRoot = GetTestAttribute("Testing.RepoRoot");
public static ProjectDirectory Create(string projectName, string baseDirectory = "", string[] additionalProjects = null)
@ -142,13 +152,7 @@ $@"<Project>
public string TargetFramework { get; set; } = "netstandard2.1";
#if DEBUG
public string Configuration => "Debug";
#elif RELEASE
public string Configuration => "Release";
#else
#error Configuration not supported
#endif
public string Configuration { get; set; } = TestProjectConfiguration;
public string IntermediateOutputDirectory => Path.Combine("obj", Configuration, TargetFramework);

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
public void ProjectDirectory_IsNotSetToBePreserved()
{
// Arrange
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
// Act & Assert
// This flag is only meant for local debugging and should not be set when checking in.

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
{
// Arrange
var expectedExtensions = new[] { ".dll", ".pdb", ".js", ".wasm" };
using var project = ProjectDirectory.Create("standalone");
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" });
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:ServiceWorkerAssetsManifest=service-worker-assets.js");
Assert.BuildPassed(result);

View File

@ -37,6 +37,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
name: "otherdir/SomePdb.pdb", // Can specify Linux-style path
fileHash: "pdbhashpdbhashpdbhash"),
CreateResourceTaskItem(
"pdb",
name: "My.Assembly1.pdb",
fileHash: "pdbdefghikjlmnopqrstuvwxyz"),
CreateResourceTaskItem(
"runtime",
name: "some-runtime-file", // Can specify path with no extension
@ -77,8 +82,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
Assert.Equal("sha256-012345678901234567890123456789", resources["dir/My.Assembly2.ext2"]); // Paths are converted to use URL-style separators
resources = parsedContent.resources.pdb;
Assert.Single(resources);
Assert.Equal(2, resources.Count);
Assert.Equal("sha256-pdbhashpdbhashpdbhash", resources["otherdir/SomePdb.pdb"]);
Assert.Equal("sha256-pdbdefghikjlmnopqrstuvwxyz", resources["My.Assembly1.pdb"]);
resources = parsedContent.resources.runtime;
Assert.Single(resources);

View File

@ -0,0 +1,6 @@
namespace RazorClassLibrary
{
public class Class1
{
}
}

View File

@ -1,4 +1,4 @@
using System;
using System;
namespace standalone
{
@ -6,6 +6,7 @@ namespace standalone
{
public static void Main(string[] args)
{
GC.KeepAlive(typeof(RazorClassLibrary.Class1));
#if REFERENCE_classlibrarywithsatelliteassemblies
GC.KeepAlive(typeof(classlibrarywithsatelliteassemblies.Class1));
#endif

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->