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 707ce66956448e..01d02adbc23fc8 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 @@ -349,7 +349,6 @@ Copyright (c) .NET Foundation. All rights reserved. <_WasmBuildWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'webcil')) <_WasmBuildTmpWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'tmp-webcil')) - <_WasmBuildOuputPath>$([MSBuild]::NormalizeDirectory('$(OutputPath)', 'wwwroot')) @@ -358,6 +357,8 @@ Copyright (c) .NET Foundation. All rights reserved. + + <_WebCilAssetsCandidates Update="@(_WebCilAssetsCandidates)" ContentRoot="%(RootDir)%(Directory)" /> <_WasmFingerprintPatterns Include="WasmFiles" Pattern="*.wasm" Expression="#[.{fingerprint}]!" /> <_WasmFingerprintPatterns Include="DllFiles" Pattern="*.dll" Expression="#[.{fingerprint}]!" /> <_WasmFingerprintPatterns Include="DatFiles" Pattern="*.dat" Expression="#[.{fingerprint}]!" /> @@ -372,7 +373,6 @@ Copyright (c) .NET Foundation. All rights reserved. AssetRole="Primary" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" - ContentRoot="$(_WasmBuildOuputPath)" FingerprintCandidates="$(_WasmFingerprintAssets)" FingerprintPatterns="@(FingerprintPatterns);@(_WasmFingerprintPatterns)" BasePath="$(StaticWebAssetBasePath)" @@ -518,7 +518,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssetTraitValue="manifest" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="Never" - ContentRoot="$(OutDir)wwwroot" + ContentRoot="$(IntermediateOutputPath)" BasePath="$(StaticWebAssetBasePath)" > @@ -584,18 +584,10 @@ Copyright (c) .NET Foundation. All rights reserved. - - <_WasmBootConfigFileNameWithoutExtension>$([System.IO.Path]::GetFileNameWithoutExtension('$(_WasmBootConfigFileName)')) - <_WasmBootConfigFileExtension>$([System.IO.Path]::GetExtension('$(_WasmBootConfigFileName)')) - - - <_WasmPreloadBuildScriptAsset Include="@(StaticWebAsset)" Condition="'$(_WasmFingerprintAssets)' == 'true' and '$(_WasmFingerprintBootConfig)' == 'true' and '%(AssetKind)' != 'Publish' and '%(FileName)%(Extension)' == '$(_WasmBootConfigFileNameWithoutExtension).%(Fingerprint)$(_WasmBootConfigFileExtension)'" /> - <_WasmPreloadBuildScriptAsset Include="@(StaticWebAsset)" Condition="('$(_WasmFingerprintAssets)' != 'true' or '$(_WasmFingerprintBootConfig)' != 'true') and '%(AssetKind)' != 'Publish' and '%(FileName)%(Extension)' == '$(_WasmBootConfigFileName)'" /> - @@ -614,13 +606,10 @@ Copyright (c) .NET Foundation. All rights reserved. - - <_WasmPreloadPublishScriptAsset Include="@(StaticWebAsset)" Condition="'%(AssetKind)' != 'Build' and '%(FileName)%(Extension)' == '$(_WasmPublishBootConfigFileName)'" /> - @@ -729,6 +718,18 @@ Copyright (c) .NET Foundation. All rights reserved. <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Include="@(_NewWebCilPublishStaticWebAssetsCandidates)" RemoveMetadata="Integrity;Fingerprint" /> + + <_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata Update="@(_NewWebCilPublishStaticWebAssetsCandidatesNoMetadata)" ContentRoot="%(RootDir)%(Directory)" /> + <_PromotedWasmPublishStaticWebAssets Update="@(_PromotedWasmPublishStaticWebAssets)" ContentRoot="%(RootDir)%(Directory)" Fingerprint="" Integrity="" /> + + <_PromotedWasmPublishStaticWebAssets + Update="@(_PromotedWasmPublishStaticWebAssets)" + Condition="'%(_PromotedWasmPublishStaticWebAssets.AssetTraitName)' == 'Culture'" + AssetRole="Primary" + RelatedAsset="" /> 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 69205e6f3b982f..dfa1bce752144f 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs @@ -332,23 +332,11 @@ static bool IsDotNetWasm(string key) private TaskItem CreatePromotedAsset(ITaskItem asset) { - string newAssetItemSpec = asset.ItemSpec; - string newAssetRelativePath = asset.GetMetadata("RelativePath"); - - if (FingerprintAssets) - { - string assetDirectory = Path.GetDirectoryName(asset.ItemSpec); - string assetFileNameToFingerprint = Path.GetFileName(newAssetRelativePath); - string fingerprint = asset.GetMetadata("Fingerprint"); - string newAssetFingerprintedFileName = assetFileNameToFingerprint.Replace("#[.{fingerprint}]!", $".{fingerprint}"); - if (newAssetFingerprintedFileName != assetFileNameToFingerprint) - { - newAssetItemSpec = $"{assetDirectory}/{newAssetFingerprintedFileName}"; - } - } - - var newAsset = new TaskItem(newAssetItemSpec, asset.CloneCustomMetadata()); - newAsset.SetMetadata("RelativePath", newAssetRelativePath); + // Keep ItemSpec pointing to the actual file on disk. + // DefineStaticWebAssets will resolve fingerprint placeholders in RelativePath + // and compute Fingerprint/Integrity from the real file. + var newAsset = new TaskItem(asset.ItemSpec, asset.CloneCustomMetadata()); + newAsset.SetMetadata("RelativePath", asset.GetMetadata("RelativePath")); ApplyPublishProperties(newAsset); return newAsset; @@ -446,6 +434,10 @@ private void ComputeUpdatedAssemblies( // when the original assembly they depend on has been linked out. var assetsToUpdate = new Dictionary(); var linkedAssets = new Dictionary(); + // Secondary lookup by normalized filename for satellite matching. + // RelatedAsset may use a different base path (e.g., OutputPath/wwwroot) + // than the asset's build-time Identity (e.g., IntermediateOutputPath/webcil). + var assetsToUpdateByFileName = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var kvp in assemblyAssets) { @@ -459,6 +451,8 @@ private void ComputeUpdatedAssemblies( { // We found the assembly, so it'll have to be updated. assetsToUpdate.Add(assetToUpdateItemSpec, asset); + if (!assetsToUpdateByFileName.ContainsKey(fileName)) + assetsToUpdateByFileName[fileName] = assetToUpdateItemSpec; filesToRemove.Add(existing); if (!string.Equals(asset.ItemSpec, existing.GetMetadata("FullPath"), StringComparison.Ordinal)) { @@ -477,6 +471,18 @@ private void ComputeUpdatedAssemblies( var satelliteAssembly = kvp.Value; var relatedAsset = satelliteAssembly.GetMetadata("RelatedAsset"); + // Try exact match first, then fall back to filename-based lookup. + // Normalize to .dll when webcil is enabled since assetsToUpdateByFileName + // keys are normalized to .dll (line 448) but RelatedAsset paths use .wasm. + var relatedAssetFileName = Path.GetFileName(relatedAsset); + if (IsWebCilEnabled) + relatedAssetFileName = Path.ChangeExtension(relatedAssetFileName, ".dll"); + if (!assetsToUpdate.ContainsKey(relatedAsset) + && assetsToUpdateByFileName.TryGetValue(relatedAssetFileName, out var matchedKey)) + { + relatedAsset = matchedKey; + } + if (assetsToUpdate.ContainsKey(relatedAsset)) { assetsToUpdate.Add(satelliteAssembly.ItemSpec, satelliteAssembly); @@ -590,9 +596,24 @@ private List ProcessCompressedAssets( private static void UpdateRelatedAssetProperty(ITaskItem asset, TaskItem newAsset, Dictionary updatedAssetsMap) { - if (!updatedAssetsMap.TryGetValue(asset.GetMetadata("RelatedAsset"), out var updatedRelatedAsset)) + var relatedAsset = asset.GetMetadata("RelatedAsset"); + if (!updatedAssetsMap.TryGetValue(relatedAsset, out var updatedRelatedAsset)) { - throw new InvalidOperationException("Related asset not found."); + // Fall back to filename matching when RelatedAsset uses a different base path + // than the asset's build-time Identity (e.g., OutputPath/wwwroot vs obj/webcil). + var relatedBaseName = Path.GetFileNameWithoutExtension(relatedAsset); + foreach (var kvp in updatedAssetsMap) + { + if (string.Equals(Path.GetFileNameWithoutExtension(kvp.Key), relatedBaseName, StringComparison.OrdinalIgnoreCase)) + { + updatedRelatedAsset = kvp.Value; + break; + } + } + if (updatedRelatedAsset == null) + { + throw new InvalidOperationException("Related asset not found."); + } } newAsset.SetMetadata("RelatedAsset", updatedRelatedAsset.ItemSpec);