Skip to content

Commit e1dcf8e

Browse files
CR: Avoid S.T.J. in MSBuild task. Revert to DataContractJsonSerializer.
1 parent 945b8f0 commit e1dcf8e

File tree

4 files changed

+264
-89
lines changed

4 files changed

+264
-89
lines changed

src/Components/Blazor/Build/src/Microsoft.AspNetCore.Blazor.Build.csproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>$(DefaultNetCoreTargetFramework);net461</TargetFrameworks>
4+
<TargetFrameworks>$(DefaultNetCoreTargetFramework);net46</TargetFrameworks>
55
<TargetName>Microsoft.AspNetCore.Blazor.Build.Tasks</TargetName>
66
<AssemblyName>Microsoft.AspNetCore.Blazor.Build</AssemblyName>
77
<Description>Build mechanism for ASP.NET Core Blazor applications.</Description>
@@ -39,8 +39,7 @@
3939

4040
<Reference Include="Microsoft.Build.Framework" ExcludeAssets="Runtime" />
4141
<Reference Include="Microsoft.Build.Utilities.Core" ExcludeAssets="Runtime" />
42-
<Reference Include="System.Reflection.Metadata" Condition="'$(TargetFramework)' == 'net461'" />
43-
<Reference Include="System.Text.Json" />
42+
<Reference Include="System.Reflection.Metadata" Condition="'$(TargetFramework)' == 'net46'" />
4443
</ItemGroup>
4544

4645
<Target Name="CopyBuildTask" BeforeTargets="Build" Condition="'$(DotNetBuildFromSource)' != 'true' AND '$(IsInnerBuild)' != 'true'">
@@ -57,12 +56,12 @@
5756

5857
<ItemGroup>
5958
<_NetCoreFilesToCopy Include="$(OutputPath)$(DefaultNetCoreTargetFramework)\*" TargetPath="netcoreapp\" />
60-
<_DesktopFilesToCopy Include="$(OutputPath)net461\*" TargetPath="netfx\" />
59+
<_DesktopFilesToCopy Include="$(OutputPath)net46\*" TargetPath="netfx\" />
6160
<_AllFilesToCopy Include="@(_NetCoreFilesToCopy);@(_DesktopFilesToCopy)" />
6261
</ItemGroup>
6362

6463
<Error Text="No files found in $(OutputPath)$(DefaultNetCoreTargetFramework)" Condition="@(_NetCoreFilesToCopy->Count()) == 0" />
65-
<Error Text="No files found in $(OutputPath)net461" Condition="@(_DesktopFilesToCopy->Count()) == 0" />
64+
<Error Text="No files found in $(OutputPath)net46" Condition="@(_DesktopFilesToCopy->Count()) == 0" />
6665

6766
<Copy SourceFiles="@(_AllFilesToCopy)" DestinationFiles="@(_AllFilesToCopy->'$(OutputPath)tools\%(TargetPath)%(FileName)%(Extension)')" SkipUnchangedFiles="true" Retries="1" ContinueOnError="true">
6867
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.IO;
67
using System.Reflection;
7-
using System.Text.Json;
8+
using System.Runtime.Serialization.Json;
9+
using System.Text;
810
using Microsoft.Build.Framework;
911
using Microsoft.Build.Utilities;
1012

@@ -32,67 +34,132 @@ public class GenerateBlazorBootJson : Task
3234

3335
public override bool Execute()
3436
{
37+
using var fileStream = File.Create(OutputPath);
38+
var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
39+
WriteBootJson(fileStream, entryAssemblyName);
40+
41+
return true;
42+
}
43+
44+
// Internal for tests
45+
internal void WriteBootJson(Stream output, string entryAssemblyName)
46+
{
47+
var result = new BootJsonData
48+
{
49+
entryAssembly = entryAssemblyName,
50+
cacheBootResources = CacheBootResources,
51+
debugBuild = DebugBuild,
52+
linkerEnabled = LinkerEnabled,
53+
resources = new Dictionary<ResourceType, ResourceList>()
54+
};
55+
3556
// Build a two-level dictionary of the form:
3657
// - BootResourceType (e.g., "assembly")
3758
// - UriPath (e.g., "System.Text.Json.dll")
3859
// - ContentHash (e.g., "4548fa2e9cf52986")
39-
var resourcesByGroup = new Dictionary<string, Dictionary<string, string>>();
40-
foreach (var resource in Resources)
60+
if (Resources != null)
4161
{
42-
var resourceType = resource.GetMetadata("BootResourceType");
43-
if (string.IsNullOrEmpty(resourceType))
62+
foreach (var resource in Resources)
4463
{
45-
continue;
46-
}
64+
var resourceTypeMetadata = resource.GetMetadata("BootResourceType");
65+
if (!Enum.TryParse<ResourceType>(resourceTypeMetadata, out var resourceType))
66+
{
67+
throw new NotSupportedException($"Unsupported BootResourceType metadata value: {resourceTypeMetadata}");
68+
}
4769

48-
if (!resourcesByGroup.TryGetValue(resourceType, out var group))
49-
{
50-
group = new Dictionary<string, string>();
51-
resourcesByGroup.Add(resourceType, group);
52-
}
70+
if (!result.resources.TryGetValue(resourceType, out var resourceList))
71+
{
72+
resourceList = new ResourceList();
73+
result.resources.Add(resourceType, resourceList);
74+
}
5375

54-
var uriPath = GetUriPath(resource);
55-
if (!group.ContainsKey(uriPath))
56-
{
57-
// It's safe to truncate to a fairly short string, since the hash is not used for any
58-
// security purpose - the developer produces these files themselves, and the hash is
59-
// only used to check whether an earlier cached copy is up-to-date.
60-
// This truncation halves the size of blazor.boot.json in typical cases.
61-
group.Add(uriPath, resource.GetMetadata("FileHash").Substring(0, 16).ToLowerInvariant());
76+
var resourceFileRelativePath = GetResourceFileRelativePath(resource);
77+
if (!resourceList.ContainsKey(resourceFileRelativePath))
78+
{
79+
// It's safe to truncate to a fairly short string, since the hash is not used for any
80+
// security purpose - the developer produces these files themselves, and the hash is
81+
// only used to check whether an earlier cached copy is up-to-date.
82+
// This truncation halves the size of blazor.boot.json in typical cases.
83+
resourceList.Add(resourceFileRelativePath, resource.GetMetadata("FileHash").Substring(0, 16).ToLowerInvariant());
84+
}
6285
}
6386
}
6487

65-
var bootJsonData = new
88+
var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings
6689
{
67-
EntryAssembly = AssemblyName.GetAssemblyName(AssemblyPath).Name,
68-
Resources = resourcesByGroup,
69-
DebugBuild,
70-
LinkerEnabled,
71-
CacheBootResources,
72-
};
73-
74-
using (var fileStream = File.Create(OutputPath))
75-
using (var utf8Writer = new Utf8JsonWriter(fileStream, new JsonWriterOptions { Indented = true }))
76-
{
77-
JsonSerializer.Serialize(utf8Writer, bootJsonData, new JsonSerializerOptions
78-
{
79-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
80-
});
81-
utf8Writer.Flush();
82-
}
90+
UseSimpleDictionaryFormat = true
91+
});
8392

84-
return true;
93+
using var writer = JsonReaderWriterFactory.CreateJsonWriter(output, Encoding.UTF8, ownsStream: false, indent: true);
94+
serializer.WriteObject(writer, result);
8595
}
8696

87-
private static string GetUriPath(ITaskItem item)
97+
private static string GetResourceFileRelativePath(ITaskItem item)
8898
{
99+
// The build targets use RelativeOutputPath in the case of satellite assemblies, which
100+
// will have relative paths like "fr\\SomeAssembly.resources.dll". If RelativeOutputPath
101+
// is specified, we want to use all of it.
89102
var outputPath = item.GetMetadata("RelativeOutputPath");
103+
90104
if (string.IsNullOrEmpty(outputPath))
91105
{
106+
// If RelativeOutputPath was not specified, we assume the item will be placed at the
107+
// root of whatever directory is used for its resource type (e.g., assemblies go in _bin)
92108
outputPath = Path.GetFileName(item.ItemSpec);
93109
}
94110

95111
return outputPath.Replace('\\', '/');
96112
}
113+
114+
#pragma warning disable IDE1006 // Naming Styles
115+
/// <summary>
116+
/// Defines the structure of a Blazor boot JSON file
117+
/// </summary>
118+
public class BootJsonData
119+
{
120+
/// <summary>
121+
/// Gets the name of the assembly with the application entry point
122+
/// </summary>
123+
public string entryAssembly { get; set; }
124+
125+
/// <summary>
126+
/// Gets the set of resources needed to boot the application. This includes the transitive
127+
/// closure of .NET assemblies (including the entrypoint assembly), the dotnet.wasm file,
128+
/// and any PDBs to be loaded.
129+
/// </summary>
130+
public Dictionary<ResourceType, ResourceList> resources { get; set; }
131+
132+
/// <summary>
133+
/// Gets a value that determines whether to enable caching of the <see cref="resources"/>
134+
/// inside a CacheStorage instance within the browser.
135+
/// </summary>
136+
public bool cacheBootResources { get; set; }
137+
138+
/// <summary>
139+
/// Gets a value that determines if this is a debug build.
140+
/// </summary>
141+
public bool debugBuild { get; set; }
142+
143+
/// <summary>
144+
/// Gets a value that determines if the linker is enabled.
145+
/// </summary>
146+
public bool linkerEnabled { get; set; }
147+
}
148+
149+
public enum ResourceType
150+
{
151+
assembly,
152+
pdb,
153+
wasm
154+
}
155+
156+
/// <summary>
157+
/// Represents a set of resources used when booting a Blazor WebAssembly application.
158+
/// The dictionary keys are the resource names; values are SHA-256 hashes of the corresponding files.
159+
/// </summary>
160+
public class ResourceList : Dictionary<string, string>
161+
{
162+
}
163+
#pragma warning restore IDE1006 // Naming Styles
97164
}
98165
}

src/Components/Blazor/Build/test/BootJsonWriterTest.cs

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)