|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Reflection;
using System.Runtime.Serialization.Json;
using Microsoft.Build.Framework;
using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>;
namespace Microsoft.NET.Sdk.BlazorWebAssembly
{
public class GenerateBlazorWebAssemblyBootJson50 : Task
{
[Required]
public string AssemblyPath { get; set; }
[Required]
public ITaskItem[] Resources { get; set; }
[Required]
public bool DebugBuild { get; set; }
[Required]
public bool LinkerEnabled { get; set; }
[Required]
public bool CacheBootResources { get; set; }
public bool LoadAllICUData { get; set; }
public string InvariantGlobalization { get; set; }
public ITaskItem[] ConfigurationFiles { get; set; }
[Required]
public string OutputPath { get; set; }
public ITaskItem[] LazyLoadedAssemblies { get; set; }
public override bool Execute()
{
using var fileStream = File.Create(OutputPath);
var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
try
{
WriteBootJson(fileStream, entryAssemblyName);
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
}
return !Log.HasLoggedErrors;
}
// Internal for tests
public void WriteBootJson(Stream output, string entryAssemblyName)
{
var icuDataMode = ICUDataMode50.Sharded;
if (string.Equals(InvariantGlobalization, "true", StringComparison.OrdinalIgnoreCase))
{
icuDataMode = ICUDataMode50.Invariant;
}
else if (LoadAllICUData)
{
icuDataMode = ICUDataMode50.All;
}
var result = new BootJsonData50
{
entryAssembly = entryAssemblyName,
cacheBootResources = CacheBootResources,
debugBuild = DebugBuild,
linkerEnabled = LinkerEnabled,
resources = new ResourcesData50(),
config = new List<string>(),
icuDataMode = icuDataMode,
};
// Build a two-level dictionary of the form:
// - assembly:
// - UriPath (e.g., "System.Text.Json.dll")
// - ContentHash (e.g., "4548fa2e9cf52986")
// - runtime:
// - UriPath (e.g., "dotnet.js")
// - ContentHash (e.g., "3448f339acf512448")
if (Resources != null)
{
var resourceData = result.resources;
foreach (var resource in Resources)
{
ResourceHashesByNameDictionary resourceList;
var fileName = resource.GetMetadata("FileName");
var extension = resource.GetMetadata("Extension");
var resourceCulture = resource.GetMetadata("Culture");
var assetType = resource.GetMetadata("AssetType");
var resourceName = $"{fileName}{extension}";
if (IsLazyLoadedAssembly(resourceName))
{
resourceData.lazyAssembly ??= new ResourceHashesByNameDictionary();
resourceList = resourceData.lazyAssembly;
}
else if (!string.IsNullOrEmpty(resourceCulture))
{
resourceData.satelliteResources ??= new Dictionary<string, ResourceHashesByNameDictionary>(StringComparer.OrdinalIgnoreCase);
resourceName = resourceCulture + "/" + resourceName;
if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList))
{
resourceList = new ResourceHashesByNameDictionary();
resourceData.satelliteResources.Add(resourceCulture, resourceList);
}
}
else if (string.Equals(extension, ".pdb", StringComparison.OrdinalIgnoreCase))
{
resourceData.pdb ??= new ResourceHashesByNameDictionary();
if (IsLazyLoadedAssembly($"{fileName}.dll"))
{
resourceList = resourceData.lazyAssembly;
}
else
{
resourceList = resourceData.pdb;
}
}
else if (string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase))
{
resourceList = resourceData.assembly;
}
else if (string.Equals(assetType, "native", StringComparison.OrdinalIgnoreCase))
{
resourceList = resourceData.runtime;
}
else
{
// This should include items such as XML doc files, which do not need to be recorded in the manifest.
continue;
}
if (!resourceList.ContainsKey(resourceName))
{
resourceList.Add(resourceName, $"sha256-{resource.GetMetadata("FileHash")}");
}
}
}
if (ConfigurationFiles != null)
{
foreach (var configFile in ConfigurationFiles)
{
result.config.Add(Path.GetFileName(configFile.ItemSpec));
}
}
var serializer = new DataContractJsonSerializer(typeof(BootJsonData50), new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true
});
using var writer = JsonReaderWriterFactory.CreateJsonWriter(output, Encoding.UTF8, ownsStream: false, indent: true);
serializer.WriteObject(writer, result);
}
private bool IsLazyLoadedAssembly(string fileName)
{
return LazyLoadedAssemblies != null && LazyLoadedAssemblies.Any(a => a.ItemSpec == fileName);
}
}
}
|