diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets index 3d48cf2d7cf0f..941993a9e83d4 100644 --- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets +++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets @@ -169,6 +169,8 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmInvariantGlobalization Condition="'$(_WasmInvariantGlobalization)' == ''">true <_WasmCopyOutputSymbolsToOutputDirectory>$(CopyOutputSymbolsToOutputDirectory) <_WasmCopyOutputSymbolsToOutputDirectory Condition="'$(_WasmCopyOutputSymbolsToOutputDirectory)'==''">true + <_WasmEnableThreads>$(WasmEnableThreads) + <_WasmEnableThreads Condition="'$(_WasmEnableThreads)' == ''">false <_WasmEnableWebcil>$(WasmEnableWebcil) <_WasmEnableWebcil Condition="'$(_WasmEnableWebcil)' == ''">false <_BlazorWebAssemblyStartupMemoryCache>$(BlazorWebAssemblyStartupMemoryCache) @@ -203,6 +205,7 @@ Copyright (c) .NET Foundation. All rights reserved. CopySymbols="$(_WasmCopyOutputSymbolsToOutputDirectory)" OutputPath="$(OutputPath)" FingerprintDotNetJs="$(WasmFingerprintDotnetJs)" + EnableThreads="$(_WasmEnableThreads)" > @@ -374,6 +377,7 @@ Copyright (c) .NET Foundation. All rights reserved. ExistingAssets="@(_WasmPublishPrefilteredAssets)" DotNetJsVersion="$(_DotNetJsVersion)" FingerprintDotNetJs="$(WasmFingerprintDotnetJs)" + EnableThreads="$(_WasmEnableThreads)" IsWebCilEnabled="$(_WasmEnableWebcil)" > diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs index 2854594ae1054..46bc35c0325ef 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs @@ -12,21 +12,26 @@ namespace Microsoft.NET.Sdk.WebAssembly; public class AssetsComputingHelper { + private static readonly string[] monoPackageIds = new[] + { + "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", + "Microsoft.NETCore.App.Runtime.Mono.multithread.browser-wasm", + "Microsoft.NETCore.App.Runtime.Mono.perftrace.browser-wasm", + }; + public static bool ShouldFilterCandidate( ITaskItem candidate, bool timezoneSupport, bool invariantGlobalization, bool copySymbols, string customIcuCandidateFilename, + bool enableThreads, out string reason) { var extension = candidate.GetMetadata("Extension"); var fileName = candidate.GetMetadata("FileName"); var assetType = candidate.GetMetadata("AssetType"); - var fromMonoPackage = string.Equals( - candidate.GetMetadata("NuGetPackageId"), - "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", - StringComparison.Ordinal); + bool fromMonoPackage = IsFromMonoPackage(candidate); reason = extension switch { @@ -45,7 +50,7 @@ public static bool ShouldFilterCandidate( ".json" when fromMonoPackage && (fileName == "emcc-props" || fileName == "package") => $"{fileName}{extension} is not used by Blazor", ".ts" when fromMonoPackage && fileName == "dotnet.d" => "dotnet type definition is not used by Blazor", ".ts" when fromMonoPackage && fileName == "dotnet-legacy.d" => "dotnet type definition is not used by Blazor", - ".js" when assetType == "native" && fileName != "dotnet" => $"{fileName}{extension} is not used by Blazor", + ".js" when assetType == "native" && !(fileName == "dotnet" || enableThreads && fileName == "dotnet.worker") => $"{fileName}{extension} is not used by Blazor", ".pdb" when !copySymbols => "copying symbols is disabled", ".symbols" when fromMonoPackage => "extension .symbols is not required.", _ => null @@ -54,6 +59,12 @@ public static bool ShouldFilterCandidate( return reason != null; } + private static bool IsFromMonoPackage(ITaskItem candidate) + { + string packageId = candidate.GetMetadata("NuGetPackageId"); + return monoPackageIds.Contains(packageId, StringComparer.Ordinal); + } + public static string GetCandidateRelativePath(ITaskItem candidate) { var destinationSubPath = candidate.GetMetadata("DestinationSubPath"); diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs index 68a563322f613..97caad5a38574 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs @@ -45,6 +45,8 @@ public class ComputeWasmBuildAssets : Task public bool FingerprintDotNetJs { get; set; } + public bool EnableThreads { get; set; } + [Output] public ITaskItem[] AssetCandidates { get; set; } @@ -79,7 +81,7 @@ public override bool Execute() for (int i = 0; i < Candidates.Length; i++) { var candidate = Candidates[i]; - if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, out var reason)) + if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, out var reason)) { Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason); filesToRemove.Add(candidate); @@ -104,14 +106,15 @@ public override bool Execute() continue; } - if (candidate.GetMetadata("FileName") == "dotnet" && candidate.GetMetadata("Extension") == ".js") + string candidateFileName = candidate.GetMetadata("FileName"); + if ((candidateFileName == "dotnet" || candidateFileName == "dotnet.worker") && candidate.GetMetadata("Extension") == ".js") { string newDotnetJSFileName = null; string newDotNetJSFullPath = null; if (FingerprintDotNetJs) { var itemHash = FileHasher.GetFileHash(candidate.ItemSpec); - newDotnetJSFileName = $"dotnet.{candidate.GetMetadata("NuGetPackageVersion")}.{itemHash}.js"; + newDotnetJSFileName = $"{candidateFileName}.{candidate.GetMetadata("NuGetPackageVersion")}.{itemHash}.js"; var originalFileFullPath = Path.GetFullPath(candidate.ItemSpec); var originalFileDirectory = Path.GetDirectoryName(originalFileFullPath); diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs index ed7115d3f36c4..78d886ce57636 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs @@ -55,6 +55,8 @@ public class ComputeWasmPublishAssets : Task public bool FingerprintDotNetJs { get; set; } + public bool EnableThreads { get; set; } + public bool IsWebCilEnabled { get; set; } [Output] @@ -163,8 +165,10 @@ private List ProcessNativeAssets( var key = kvp.Key; var asset = kvp.Value; var isDotNetJs = IsDotNetJs(key); + var isDotNetWorkerJs = IsDotNetWorkerJs(key); var isDotNetWasm = IsDotNetWasm(key); - if (!isDotNetJs && !isDotNetWasm) + + if (!isDotNetJs && !isDotNetWasm && !isDotNetWorkerJs) { if (resolvedNativeAssetToPublish.TryGetValue(Path.GetFileName(asset.GetMetadata("OriginalItemSpec")), out var existing)) { @@ -197,11 +201,16 @@ private List ProcessNativeAssets( { var aotDotNetJs = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.js"); ITaskItem newDotNetJs = null; - if (aotDotNetJs != null && FingerprintDotNetJs) + if (aotDotNetJs != null) { newDotNetJs = new TaskItem(Path.GetFullPath(aotDotNetJs.ItemSpec), asset.CloneCustomMetadata()); newDotNetJs.SetMetadata("OriginalItemSpec", aotDotNetJs.ItemSpec); - newDotNetJs.SetMetadata("RelativePath", $"_framework/{$"dotnet.{DotNetJsVersion}.{FileHasher.GetFileHash(aotDotNetJs.ItemSpec)}.js"}"); + + string relativePath = FingerprintDotNetJs + ? $"_framework/{$"dotnet.{DotNetJsVersion}.{FileHasher.GetFileHash(aotDotNetJs.ItemSpec)}.js"}" + : "_framework/dotnet.js"; + + newDotNetJs.SetMetadata("RelativePath", relativePath); updateMap.Add(asset.ItemSpec, newDotNetJs); Log.LogMessage(MessageImportance.Low, "Replacing asset '{0}' with AoT version '{1}'", asset.ItemSpec, newDotNetJs.ItemSpec); @@ -221,6 +230,35 @@ private List ProcessNativeAssets( continue; } + if (isDotNetWorkerJs) + { + var aotDotNetWorkerJs = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.worker.js"); + ITaskItem newDotNetWorkerJs = null; + if (aotDotNetWorkerJs != null) + { + newDotNetWorkerJs = new TaskItem(Path.GetFullPath(aotDotNetWorkerJs.ItemSpec), asset.CloneCustomMetadata()); + newDotNetWorkerJs.SetMetadata("OriginalItemSpec", aotDotNetWorkerJs.ItemSpec); + newDotNetWorkerJs.SetMetadata("RelativePath", "_framework/dotnet.worker.js"); + + updateMap.Add(asset.ItemSpec, newDotNetWorkerJs); + Log.LogMessage(MessageImportance.High, "Replacing asset '{0}' with AoT version '{1}'", asset.ItemSpec, newDotNetWorkerJs.ItemSpec); + } + else + { + newDotNetWorkerJs = new TaskItem(asset); + newDotNetWorkerJs.SetMetadata("RelativePath", "_framework/dotnet.worker.js"); + Log.LogMessage(MessageImportance.High, "Promoting asset '{0}' to Publish asset.", asset.ItemSpec); + } + + ApplyPublishProperties(newDotNetWorkerJs); + nativeStaticWebAssets.Add(newDotNetWorkerJs); + if (resolvedNativeAssetToPublish.TryGetValue("dotnet.worker.js", out var resolved)) + { + filesToRemove.Add(resolved); + } + continue; + } + if (isDotNetWasm) { var aotDotNetWasm = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.wasm"); @@ -262,6 +300,8 @@ static bool IsDotNetJs(string key) return fileName.StartsWith("dotnet.", StringComparison.Ordinal) && fileName.EndsWith(".js", StringComparison.Ordinal) && !fileName.Contains("worker"); } + static bool IsDotNetWorkerJs(string key) => string.Equals("dotnet.worker.js", Path.GetFileName(key), StringComparison.Ordinal); + static bool IsDotNetWasm(string key) => string.Equals("dotnet.wasm", Path.GetFileName(key), StringComparison.Ordinal); } @@ -540,7 +580,7 @@ private void GroupResolvedFilesToPublish( foreach (var candidate in resolvedFilesToPublish) { - if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, out var reason)) + if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, out var reason)) { Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason); if (!resolvedFilesToPublishToRemove.ContainsKey(candidate.ItemSpec))