Skip to content

Commit 4ab5fe6

Browse files
authored
Handle satellite assemblies in the Blazor build targets (#18207)
* Handle satellite assemblies in the Blazor build targets * Pass the same closure of assemblies that is used by the SDK's linker to Blazor's linker and ResolveBlazorRuntimeDependencies task * Quote the mono linker path Fixes #17644 Fixes #17754
2 parents d4a348e + 80a6787 commit 4ab5fe6

File tree

8 files changed

+222
-51
lines changed

8 files changed

+222
-51
lines changed

src/Components/Blazor/Build/src/Tasks/BlazorILLink.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ private string DotNetPath
6666

6767
protected override string GenerateFullPathToTool() => DotNetPath;
6868

69-
protected override string GenerateCommandLineCommands() => ILLinkPath;
69+
protected override string GenerateCommandLineCommands()
70+
{
71+
var args = new StringBuilder();
72+
args.Append(Quote(ILLinkPath));
73+
return args.ToString();
74+
}
7075

7176
private static string Quote(string path)
7277
{

src/Components/Blazor/Build/src/Tasks/GenerateBlazorBootJson.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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.IO;
56
using System.Linq;
67
using System.Reflection;
@@ -27,12 +28,23 @@ public class GenerateBlazorBootJson : Task
2728
public override bool Execute()
2829
{
2930
var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
30-
var assemblies = References.Select(c => Path.GetFileName(c.ItemSpec)).ToArray();
31+
var assemblies = References.Select(GetUriPath).OrderBy(c => c, StringComparer.Ordinal).ToArray();
3132

3233
using var fileStream = File.Create(OutputPath);
3334
WriteBootJson(fileStream, entryAssemblyName, assemblies, LinkerEnabled);
3435

3536
return true;
37+
38+
static string GetUriPath(ITaskItem item)
39+
{
40+
var outputPath = item.GetMetadata("RelativeOutputPath");
41+
if (string.IsNullOrEmpty(outputPath))
42+
{
43+
outputPath = Path.GetFileName(item.ItemSpec);
44+
}
45+
46+
return outputPath.Replace('\\', '/');
47+
}
3648
}
3749

3850
internal static void WriteBootJson(Stream stream, string entryAssemblyName, string[] assemblies, bool linkerEnabled)

src/Components/Blazor/Build/src/targets/Blazor.MonoRuntime.targets

Lines changed: 83 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,16 @@
7474
</ItemGroup>
7575
</Target>
7676

77-
<Target Name="_ResolveBlazorInputs">
77+
<Target Name="_ResolveBlazorInputs" DependsOnTargets="ResolveReferences;ResolveRuntimePackAssets">
7878
<PropertyGroup>
7979
<!-- /obj/<<configuration>>/<<targetframework>>/blazor -->
8080
<BlazorIntermediateOutputPath>$(IntermediateOutputPath)blazor\</BlazorIntermediateOutputPath>
8181

8282
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker.descriptor.xml -->
8383
<GeneratedBlazorLinkerDescriptor>$(BlazorIntermediateOutputPath)linker.descriptor.xml</GeneratedBlazorLinkerDescriptor>
8484

85+
<_TypeGranularityLinkerDescriptor>$(BlazorIntermediateOutputPath)linker.typegranularityconfig.xml</_TypeGranularityLinkerDescriptor>
86+
8587
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker/ -->
8688
<BlazorIntermediateLinkerOutputPath>$(BlazorIntermediateOutputPath)linker/</BlazorIntermediateLinkerOutputPath>
8789

@@ -94,8 +96,6 @@
9496
</PropertyGroup>
9597

9698
<ItemGroup>
97-
<_BlazorDependencyInput Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll')->'%(FullPath)')" />
98-
9999
<_WebAssemblyBCLFolder Include="
100100
$(DotNetWebAssemblyBCLPath);
101101
$(DotNetWebAssemblyBCLFacadesPath);
@@ -104,13 +104,50 @@
104104
<_WebAssemblyBCLAssembly Include="%(_WebAssemblyBCLFolder.Identity)*.dll" />
105105
</ItemGroup>
106106

107+
<!--
108+
Calculate the assemblies that act as inputs to calculate assembly closure. Based on _ComputeAssembliesToPostprocessOnPublish which is used as input to SDK's linker
109+
https://github.com/dotnet/sdk/blob/d597e7b09d7657ba4e326d6734e14fcbf8473564/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets#L864-L873
110+
-->
111+
<ItemGroup>
112+
<!-- Assemblies from packages -->
113+
<_BlazorManagedRuntimeAssemby Include="@(RuntimeCopyLocalItems)" />
114+
115+
<!-- Assemblies from other references -->
116+
<_BlazorUserRuntimeAssembly Include="@(ReferencePath->WithMetadataValue('CopyLocal', 'true'))" />
117+
<_BlazorUserRuntimeAssembly Include="@(ReferenceDependencyPaths->WithMetadataValue('CopyLocal', 'true'))" />
118+
119+
<_BlazorManagedRuntimeAssemby Include="@(_BlazorUserRuntimeAssembly)" />
120+
<_BlazorManagedRuntimeAssemby Include="@(IntermediateAssembly)" />
121+
</ItemGroup>
122+
107123
<MakeDir Directories="$(BlazorIntermediateOutputPath)" />
108124
</Target>
109125

110126
<Target Name="_ResolveBlazorOutputs" DependsOnTargets="_ResolveBlazorOutputsWhenLinked;_ResolveBlazorOutputsWhenNotLinked">
111127
<Error
112128
Message="Unrecongnized value for BlazorLinkOnBuild: '$(BlazorLinkOnBuild)'. Valid values are 'true' or 'false'."
113129
Condition="'$(BlazorLinkOnBuild)' != 'true' AND '$(BlazorLinkOnBuild)' != 'false'" />
130+
131+
<ItemGroup>
132+
<!--
133+
ReferenceCopyLocalPaths includes all files that are part of the build out with CopyLocalLockFileAssemblies on.
134+
Remove assemblies that are inputs to calculating the assembly closure. Instead use the resolved outputs, since it is the minimal set.
135+
-->
136+
<_BlazorCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" />
137+
<_BlazorCopyLocalPaths Remove="@(_BlazorManagedRuntimeAssemby)" />
138+
139+
<BlazorOutputWithTargetPath Include="@(_BlazorCopyLocalPaths)">
140+
<BlazorRuntimeFile>true</BlazorRuntimeFile>
141+
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</TargetOutputPath>
142+
<RelativeOutputPath>%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</RelativeOutputPath>
143+
</BlazorOutputWithTargetPath>
144+
145+
<BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)">
146+
<BlazorRuntimeFile>true</BlazorRuntimeFile>
147+
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
148+
<RelativeOutputPath>%(FileName)%(Extension)</RelativeOutputPath>
149+
</BlazorOutputWithTargetPath>
150+
</ItemGroup>
114151
</Target>
115152

116153
<!--
@@ -124,18 +161,34 @@
124161
<Target
125162
Name="_ResolveBlazorOutputsWhenLinked"
126163
Condition="'$(BlazorLinkOnBuild)' == 'true'"
127-
DependsOnTargets="_GenerateBlazorLinkerDescriptor;_LinkBlazorApplication">
164+
DependsOnTargets="_PrepareBlazorLinkerInputs;_GenerateBlazorLinkerDescriptor;_GenerateTypeGranularLinkerDescriptor;_LinkBlazorApplication">
128165

129166
<!-- _BlazorLinkerOutputCache records files linked during the last incremental build of the target. Read the contents and assign linked files to be copied to the output. -->
130167
<ReadLinesFromFile File="$(_BlazorLinkerOutputCache)">
131-
<Output TaskParameter="Lines" ItemName="_BlazorLinkedFile"/>
168+
<Output TaskParameter="Lines" ItemName="_BlazorResolvedAssembly"/>
132169
</ReadLinesFromFile>
170+
</Target>
133171

172+
<Target Name="_PrepareBlazorLinkerInputs">
134173
<ItemGroup>
135-
<BlazorOutputWithTargetPath Include="%(_BlazorLinkedFile.Identity)">
136-
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
137-
</BlazorOutputWithTargetPath>
174+
<_BlazorRuntimeCopyLocalItems Include="@(RuntimeCopyLocalItems)" />
175+
176+
<!--
177+
Any assembly from a package reference that starts with System. file name is allowed to be linked.
178+
Assemblies from Microsoft.AspNetCore and Microsoft.Extensions, are also linked but with TypeGranularity.
179+
-->
180+
<_BlazorRuntimeCopyLocalItems IsLinkable="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('System.'))" />
181+
<_BlazorRuntimeCopyLocalItems IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))" />
182+
<_BlazorRuntimeCopyLocalItems IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))" />
183+
184+
<_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" />
185+
<_BlazorAssemblyToLink Include="@(_BlazorRuntimeCopyLocalItems)" Condition="'%(_BlazorRuntimeCopyLocalItems.IsLinkable)' == 'true'" />
186+
187+
<_BlazorLinkerRoot Include="@(IntermediateAssembly)" />
188+
<_BlazorLinkerRoot Include="@(_BlazorUserRuntimeAssembly)" />
189+
<_BlazorLinkerRoot Include="@(_BlazorRuntimeCopyLocalItems)" Condition="'%(_BlazorRuntimeCopyLocalItems.IsLinkable)' != 'true'" />
138190
</ItemGroup>
191+
139192
</Target>
140193

141194
<UsingTask TaskName="BlazorCreateRootDescriptorFile" AssemblyFile="$(BlazorTasksPath)" />
@@ -157,31 +210,30 @@
157210
</ItemGroup>
158211
</Target>
159212

160-
<UsingTask TaskName="BlazorILLink" AssemblyFile="$(BlazorTasksPath)" />
161213
<UsingTask TaskName="GenerateTypeGranularityLinkingConfig" AssemblyFile="$(BlazorTasksPath)" />
214+
<Target Name="_GenerateTypeGranularLinkerDescriptor"
215+
Inputs="@(_BlazorAssemblyToLink->WithMetadataValue('TypeGranularity', 'true'))"
216+
Outputs="$(_TypeGranularityLinkerDescriptor)">
217+
218+
<GenerateTypeGranularityLinkingConfig
219+
Assemblies="@(_BlazorAssemblyToLink->WithMetadataValue('TypeGranularity', 'true'))"
220+
OutputPath="$(_TypeGranularityLinkerDescriptor)" />
221+
222+
<ItemGroup>
223+
<BlazorLinkerDescriptor Include="$(_TypeGranularityLinkerDescriptor)" />
224+
<FileWrites Include="$(_TypeGranularityLinkerDescriptor)" />
225+
</ItemGroup>
226+
</Target>
162227

228+
<UsingTask TaskName="BlazorILLink" AssemblyFile="$(BlazorTasksPath)" />
163229
<Target
164230
Name="_LinkBlazorApplication"
165231
Inputs="$(ProjectAssetsFile);
166-
@(IntermediateAssembly);
167-
@(_BlazorDependencyInput);
232+
@(_BlazorManagedRuntimeAssemby);
168233
@(BlazorLinkerDescriptor);
169234
$(MSBuildAllProjects)"
170235
Outputs="$(_BlazorLinkerOutputCache)">
171236

172-
<ItemGroup>
173-
<_BlazorDependencyAssembly Include="@(_BlazorDependencyInput)" />
174-
<_BlazorDependencyAssembly IsLinkable="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('System.'))" />
175-
<_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))" />
176-
<_BlazorDependencyAssembly IsLinkable="true" TypeGranularity="true" Condition="$([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.'))" />
177-
178-
<_BlazorAssemblyToLink Include="@(_WebAssemblyBCLAssembly)" />
179-
<_BlazorAssemblyToLink Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' == 'true'" />
180-
181-
<_BlazorLinkerRoot Include="@(IntermediateAssembly)" />
182-
<_BlazorLinkerRoot Include="@(_BlazorDependencyAssembly)" Condition="'%(_BlazorDependencyAssembly.IsLinkable)' != 'true'" />
183-
</ItemGroup>
184-
185237
<PropertyGroup>
186238
<_BlazorLinkerAdditionalOptions>-l $(MonoLinkerI18NAssemblies) $(AdditionalMonoLinkerOptions)</_BlazorLinkerAdditionalOptions>
187239
</PropertyGroup>
@@ -203,15 +255,6 @@
203255
<_DotNetHostFileName Condition=" '$(OS)' == 'Windows_NT' ">dotnet.exe</_DotNetHostFileName>
204256
</PropertyGroup>
205257

206-
<PropertyGroup>
207-
<_TypeGranularityLinkingConfig>$(BlazorIntermediateOutputPath)linker.typegranularityconfig.xml</_TypeGranularityLinkingConfig>
208-
</PropertyGroup>
209-
<GenerateTypeGranularityLinkingConfig Assemblies="@(_BlazorAssemblyToLink->WithMetadataValue('TypeGranularity', 'true'))" OutputPath="$(_TypeGranularityLinkingConfig)" />
210-
<ItemGroup>
211-
<BlazorLinkerDescriptor Include="$(_TypeGranularityLinkingConfig)" />
212-
<FileWrites Include="$(_TypeGranularityLinkingConfig)" />
213-
</ItemGroup>
214-
215258
<BlazorILLink
216259
ILLinkPath="$(MonoLinkerPath)"
217260
AssemblyPaths="@(_BlazorAssemblyToLink)"
@@ -230,29 +273,22 @@
230273
<WriteLinesToFile File="$(_BlazorLinkerOutputCache)" Lines="@(_LinkerResult)" Overwrite="true" />
231274
</Target>
232275

233-
234276
<UsingTask TaskName="ResolveBlazorRuntimeDependencies" AssemblyFile="$(BlazorTasksPath)" />
235277
<Target
236278
Name="_ResolveBlazorOutputsWhenNotLinked"
237279
DependsOnTargets="_ResolveBlazorRuntimeDependencies"
238280
Condition="'$(BlazorLinkOnBuild)' != 'true'">
239281

240-
<ReadLinesFromFile File="$(_BlazorApplicationAssembliesCacheFile)" Condition="'@(_BlazorResolvedRuntimeDependencies->Count())' == '0'">
241-
<Output TaskParameter="Lines" ItemName="_BlazorResolvedRuntimeDependencies"/>
282+
<ReadLinesFromFile File="$(_BlazorApplicationAssembliesCacheFile)" Condition="'@(_BlazorResolvedAssembly->Count())' == '0'">
283+
<Output TaskParameter="Lines" ItemName="_BlazorResolvedAssembly"/>
242284
</ReadLinesFromFile>
243-
244-
<ItemGroup>
245-
<BlazorOutputWithTargetPath Include="@(_BlazorResolvedRuntimeDependencies)">
246-
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
247-
</BlazorOutputWithTargetPath>
248-
</ItemGroup>
249285
</Target>
250286

251287
<Target
252288
Name="_ResolveBlazorRuntimeDependencies"
253289
Inputs="$(ProjectAssetsFile);
254290
@(IntermediateAssembly);
255-
@(_BlazorDependencyInput)"
291+
@(_BlazorManagedRuntimeAssemby)"
256292
Outputs="$(_BlazorApplicationAssembliesCacheFile)">
257293

258294
<!--
@@ -262,10 +298,10 @@
262298
-->
263299
<ResolveBlazorRuntimeDependencies
264300
EntryPoint="@(IntermediateAssembly)"
265-
ApplicationDependencies="@(_BlazorDependencyInput)"
301+
ApplicationDependencies="@(_BlazorManagedRuntimeAssemby)"
266302
WebAssemblyBCLAssemblies="@(_WebAssemblyBCLAssembly)">
267303

268-
<Output TaskParameter="Dependencies" ItemName="_BlazorResolvedRuntimeDependencies" />
304+
<Output TaskParameter="Dependencies" ItemName="_BlazorResolvedAssembly" />
269305
</ResolveBlazorRuntimeDependencies>
270306

271307
<WriteLinesToFile File="$(_BlazorApplicationAssembliesCacheFile)" Lines="@(_BlazorResolvedRuntimeDependencies)" Overwrite="true" />
@@ -282,13 +318,12 @@
282318
Inputs="@(BlazorOutputWithTargetPath)"
283319
Outputs="$(BlazorBootJsonIntermediateOutputPath)">
284320
<ItemGroup>
285-
<_AppReferences Include="@(BlazorOutputWithTargetPath->WithMetadataValue('Extension','.dll'))" />
286-
<_AppReferences Include="@(BlazorOutputWithTargetPath->WithMetadataValue('Extension','.pdb'))" Condition="'$(BlazorEnableDebugging)' == 'true'" />
321+
<_BlazorRuntimeFile Include="@(BlazorOutputWithTargetPath->WithMetadataValue('BlazorRuntimeFile', 'true'))" />
287322
</ItemGroup>
288323

289324
<GenerateBlazorBootJson
290325
AssemblyPath="@(IntermediateAssembly)"
291-
References="@(_AppReferences)"
326+
References="@(_BlazorRuntimeFile)"
292327
LinkerEnabled="$(BlazorLinkOnBuild)"
293328
OutputPath="$(BlazorBootJsonIntermediateOutputPath)" />
294329

src/Components/Blazor/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,66 @@ public async Task Build_WithLinkOnBuildDisabled_Works()
7070
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
7171
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output.
7272
}
73+
74+
[Fact]
75+
public async Task Build_SatelliteAssembliesAreCopiedToBuildOutput()
76+
{
77+
// Arrange
78+
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
79+
project.AddProjectFileContent(
80+
@"
81+
<PropertyGroup>
82+
<DefineConstants>$(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies</DefineConstants>
83+
</PropertyGroup>
84+
<ItemGroup>
85+
<ProjectReference Include=""..\classlibrarywithsatelliteassemblies\classlibrarywithsatelliteassemblies.csproj"" />
86+
</ItemGroup>");
87+
88+
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
89+
90+
Assert.BuildPassed(result);
91+
92+
var buildOutputDirectory = project.BuildOutputDirectory;
93+
94+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
95+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "classlibrarywithsatelliteassemblies.dll");
96+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll");
97+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
98+
99+
var bootJsonPath = Path.Combine(buildOutputDirectory, "dist", "_framework", "blazor.boot.json");
100+
Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
101+
Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
102+
}
103+
104+
[Fact]
105+
public async Task Build_WithBlazorLinkOnBuildFalse_SatelliteAssembliesAreCopiedToBuildOutput()
106+
{
107+
// Arrange
108+
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
109+
project.AddProjectFileContent(
110+
@"
111+
<PropertyGroup>
112+
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
113+
<DefineConstants>$(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies</DefineConstants>
114+
</PropertyGroup>
115+
<ItemGroup>
116+
<ProjectReference Include=""..\classlibrarywithsatelliteassemblies\classlibrarywithsatelliteassemblies.csproj"" />
117+
</ItemGroup>");
118+
119+
var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/restore");
120+
121+
Assert.BuildPassed(result);
122+
123+
var buildOutputDirectory = project.BuildOutputDirectory;
124+
125+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "standalone.dll");
126+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "classlibrarywithsatelliteassemblies.dll");
127+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll");
128+
Assert.FileExists(result, buildOutputDirectory, "dist", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
129+
130+
var bootJsonPath = Path.Combine(buildOutputDirectory, "dist", "_framework", "blazor.boot.json");
131+
Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
132+
Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
133+
}
73134
}
74135
}

src/Components/Blazor/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,35 @@ public async Task Publish_WithLinkOnBuildDisabled_Works()
112112
Assert.FileExists(result, publishDirectory, "web.config");
113113
}
114114

115+
[Fact]
116+
public async Task Publish_SatelliteAssemblies_AreCopiedToBuildOutput()
117+
{
118+
// Arrange
119+
using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary", "classlibrarywithsatelliteassemblies" });
120+
project.AddProjectFileContent(
121+
@"
122+
<PropertyGroup>
123+
<DefineConstants>$(DefineConstants);REFERENCE_classlibrarywithsatelliteassemblies</DefineConstants>
124+
</PropertyGroup>
125+
<ItemGroup>
126+
<ProjectReference Include=""..\classlibrarywithsatelliteassemblies\classlibrarywithsatelliteassemblies.csproj"" />
127+
</ItemGroup>");
128+
129+
var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args: "/restore");
130+
131+
Assert.BuildPassed(result);
132+
133+
var publishDirectory = project.PublishOutputDirectory;
134+
var blazorPublishDirectory = Path.Combine(publishDirectory, Path.GetFileNameWithoutExtension(project.ProjectFilePath));
135+
136+
Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "Microsoft.CodeAnalysis.CSharp.dll");
137+
Assert.FileExists(result, blazorPublishDirectory, "dist", "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.CSharp.resources.dll"); // Verify satellite assemblies are present in the build output.
138+
139+
var bootJsonPath = Path.Combine(blazorPublishDirectory, "dist", "_framework", "blazor.boot.json");
140+
Assert.FileContains(result, bootJsonPath, "\"Microsoft.CodeAnalysis.CSharp.dll\"");
141+
Assert.FileContains(result, bootJsonPath, "\"fr\\/Microsoft.CodeAnalysis.CSharp.resources.dll\"");
142+
}
143+
115144
[Fact]
116145
public async Task Publish_HostedApp_Works()
117146
{

0 commit comments

Comments
 (0)