Skip to content

Commit 9b1f0a8

Browse files
lewingCopilot
andcommitted
Use Framework SourceType for WASM pass-through assets in multi-client solutions
When multiple Blazor WebAssembly client projects reference the same runtime pack, pass-through files (JS, maps, ICU data, native wasm) share a NuGet cache path. This causes duplicate Identity keys in the static web assets pipeline, crashing DiscoverPrecompressedAssets with an ArgumentException. Instead of copying pass-throughs to the intermediate output path (which risks staleness on incremental builds when the runtime pack changes), materialize them to a per-project obj/fx/{PackageId}/ directory using the Framework SourceType convention from the SWA SDK. This gives each project a unique Identity while properly modeling the relationship: these are framework assets adopted by each consuming project. - Pass-through files: copied to obj/fx/{PackageId}/, registered with SourceType=Framework - WebCil-converted files: remain in obj/webcil/, registered with SourceType=Computed - Satellite assemblies: placed in culture subdirectories in both cases - Both groups get per-item ContentRoot for correct Identity resolution Requires SDK support for SourceType=Framework (dotnet/sdk#53135). Fixes duplicate-key crash introduced by #124125. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0d0e129 commit 9b1f0a8

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,23 +353,60 @@ Copyright (c) .NET Foundation. All rights reserved.
353353
<PropertyGroup>
354354
<_WasmBuildWebcilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'webcil'))</_WasmBuildWebcilPath>
355355
<_WasmBuildTmpWebcilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'tmp-webcil'))</_WasmBuildTmpWebcilPath>
356+
<_WasmFrameworkAssetPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'fx', '$(PackageId)'))</_WasmFrameworkAssetPath>
356357
</PropertyGroup>
357358

358359
<ConvertDllsToWebcil Candidates="@(_BuildAssetsCandidates)" IntermediateOutputPath="$(_WasmBuildTmpWebcilPath)" OutputPath="$(_WasmBuildWebcilPath)" IsEnabled="$(_WasmEnableWebcil)">
359360
<Output TaskParameter="WebcilCandidates" ItemName="_WebcilAssetsCandidates" />
360361
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
361362
</ConvertDllsToWebcil>
362363

364+
<!-- Materialize framework pass-through files (JS, maps, ICU, native wasm, and when WebCil is
365+
disabled, DLLs) from shared locations (e.g. NuGet cache) to a per-project directory.
366+
Webcil-converted files already live in per-project $(IntermediateOutputPath)webcil/ and
367+
don't need materialization. Framework assets are copied to obj/fx/{PackageId}/ following
368+
the SWA Framework SourceType convention so each project gets a unique Identity.
369+
Satellite assemblies are placed in culture subdirectories to match ConvertDllsToWebcil. -->
363370
<ItemGroup>
371+
<_WasmFrameworkCandidates Include="@(_WebcilAssetsCandidates)"
372+
Condition="!$([System.String]::new('%(Identity)').StartsWith($(_WasmBuildWebcilPath)))" />
373+
<_WebcilAssetsCandidates Remove="@(_WasmFrameworkCandidates)" />
374+
</ItemGroup>
375+
376+
<!-- Separate satellite (culture) assets from regular ones so we can place them in subdirs -->
377+
<ItemGroup>
378+
<_WasmCultureFrameworkAssets Include="@(_WasmFrameworkCandidates)"
379+
Condition="'%(_WasmFrameworkCandidates.AssetTraitName)' == 'Culture'" />
380+
<_WasmFrameworkCandidates Remove="@(_WasmCultureFrameworkAssets)" />
381+
</ItemGroup>
382+
383+
<Copy SourceFiles="@(_WasmFrameworkCandidates)"
384+
DestinationFolder="$(_WasmFrameworkAssetPath)"
385+
SkipUnchangedFiles="true" />
386+
387+
<Copy SourceFiles="@(_WasmCultureFrameworkAssets)"
388+
DestinationFiles="@(_WasmCultureFrameworkAssets->'$(_WasmFrameworkAssetPath)%(AssetTraitValue)/%(Filename)%(Extension)')"
389+
SkipUnchangedFiles="true"
390+
Condition="'@(_WasmCultureFrameworkAssets)' != ''" />
391+
392+
<ItemGroup>
393+
<FileWrites Include="@(_WasmFrameworkCandidates->'$(_WasmFrameworkAssetPath)%(Filename)%(Extension)')" />
394+
<FileWrites Include="@(_WasmCultureFrameworkAssets->'$(_WasmFrameworkAssetPath)%(AssetTraitValue)/%(Filename)%(Extension)')" />
395+
<_WasmMaterializedFrameworkAssets Include="@(_WasmFrameworkCandidates->'$(_WasmFrameworkAssetPath)%(Filename)%(Extension)')" />
396+
<_WasmMaterializedFrameworkAssets Include="@(_WasmCultureFrameworkAssets->'$(_WasmFrameworkAssetPath)%(AssetTraitValue)/%(Filename)%(Extension)')" />
364397
<!-- Set per-item ContentRoot so each asset's Identity matches its actual file on disk -->
365398
<_WebcilAssetsCandidates Update="@(_WebcilAssetsCandidates)" ContentRoot="%(RootDir)%(Directory)" />
399+
<_WasmMaterializedFrameworkAssets Update="@(_WasmMaterializedFrameworkAssets)" ContentRoot="%(RootDir)%(Directory)" />
366400
<_WasmFingerprintPatterns Include="WasmFiles" Pattern="*.wasm" Expression="#[.{fingerprint}]!" />
367401
<_WasmFingerprintPatterns Include="DllFiles" Pattern="*.dll" Expression="#[.{fingerprint}]!" />
368402
<_WasmFingerprintPatterns Include="DatFiles" Pattern="*.dat" Expression="#[.{fingerprint}]!" />
369403
<_WasmFingerprintPatterns Include="Pdb" Pattern="*.pdb" Expression="#[.{fingerprint}]!" />
370404
<_WasmFingerprintPatterns Include="Symbols" Pattern="*.js.symbols" Expression="#[.{fingerprint}]!" />
371405
</ItemGroup>
372406

407+
<!-- Webcil-converted assets are per-project (Computed); materialized framework assets
408+
are adopted from the runtime pack (Framework). Define them separately so the SWA
409+
pipeline can handle Framework materialization/ownership correctly. -->
373410
<DefineStaticWebAssets
374411
CandidateAssets="@(_WebcilAssetsCandidates)"
375412
SourceId="$(PackageId)"
@@ -385,6 +422,21 @@ Copyright (c) .NET Foundation. All rights reserved.
385422
<Output TaskParameter="Assets" ItemName="WasmStaticWebAsset" />
386423
</DefineStaticWebAssets>
387424

425+
<DefineStaticWebAssets
426+
CandidateAssets="@(_WasmMaterializedFrameworkAssets)"
427+
SourceId="$(PackageId)"
428+
SourceType="Framework"
429+
AssetKind="Build"
430+
AssetRole="Primary"
431+
CopyToOutputDirectory="PreserveNewest"
432+
CopyToPublishDirectory="Never"
433+
FingerprintCandidates="$(_WasmFingerprintAssets)"
434+
FingerprintPatterns="@(FingerprintPatterns);@(_WasmFingerprintPatterns)"
435+
BasePath="$(StaticWebAssetBasePath)"
436+
>
437+
<Output TaskParameter="Assets" ItemName="WasmStaticWebAsset" />
438+
</DefineStaticWebAssets>
439+
388440
<DefineStaticWebAssets
389441
CandidateAssets="@(_WasmDiscoveredFileCandidates)"
390442
AssetTraitName="WasmResource"

0 commit comments

Comments
 (0)