Skip to content

Commit 02ba8fd

Browse files
pranavkmHaoK
authored andcommitted
Avoid doing unncecessary work when generating component declaration files. (#24445)
The output of the declaration file for Razor components are unaffected by all inputs other than the input .razor file. Consequently we can avoid regenerating these files if the output is newer than the input. This is the same heuristic we apply to Blazor WebAsssembly's compression artifacts. This PR combines these two improvements for a ~90ms (10%) improvement in the inner loop. ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 22 ms Copy 8 calls 39 ms ProcessFrameworkReferences 1 calls 40 ms RazorTagHelper 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms GetFileHash 1 calls 80 ms RazorGenerate 2 calls 111 ms Csc 2 calls Time Elapsed 00:00:00.95 ``` ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 21 ms Copy 8 calls 37 ms ProcessFrameworkReferences 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms Csc 1 calls 72 ms GetFileHash 1 calls 79 ms RazorGenerate 2 calls Time Elapsed 00:00:00.86 ``` In after: Csc calls reduced to one, RazorTagHelper call removed.
1 parent 0f37658 commit 02ba8fd

File tree

3 files changed

+78
-4
lines changed

3 files changed

+78
-4
lines changed

src/Razor/Microsoft.AspNetCore.Razor.Tools/src/GenerateCommand.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ private int ExecuteCore(
201201
if (GenerateDeclaration.HasValue())
202202
{
203203
b.Features.Add(new SetSuppressPrimaryMethodBodyOptionFeature());
204+
b.Features.Add(new SuppressChecksumOptionsFeature());
204205
}
205206

206207
if (RootNamespace.HasValue())
@@ -227,6 +228,7 @@ private int ExecuteCore(
227228
});
228229

229230
var results = GenerateCode(engine, sourceItems);
231+
var isGeneratingDeclaration = GenerateDeclaration.HasValue();
230232

231233
foreach (var result in results)
232234
{
@@ -255,6 +257,18 @@ private int ExecuteCore(
255257
{
256258
// Only output the file if we generated it without errors.
257259
var outputFilePath = result.InputItem.OutputPath;
260+
var generatedCode = result.CSharpDocument.GeneratedCode;
261+
if (isGeneratingDeclaration)
262+
{
263+
// When emiting declarations, only write if it the contents are different.
264+
// This allows build incrementalism to kick in when the declaration remains unchanged between builds.
265+
if (File.Exists(outputFilePath) &&
266+
string.Equals(File.ReadAllText(outputFilePath), generatedCode, StringComparison.Ordinal))
267+
{
268+
continue;
269+
}
270+
}
271+
258272
File.WriteAllText(outputFilePath, result.CSharpDocument.GeneratedCode);
259273
}
260274
}

src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIncrementalismTest.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,61 @@ async Task VerifyError()
172172
}
173173
}
174174

175+
[Fact]
176+
[InitializeTestProject("MvcWithComponents")]
177+
public async Task BuildComponents_DoesNotRegenerateComponentDefinition_WhenDefinitionIsUnchanged()
178+
{
179+
// Act - 1
180+
var updatedContent = "Some content";
181+
var tagHelperOutputCache = Path.Combine(IntermediateOutputPath, "MvcWithComponents.TagHelpers.output.cache");
182+
183+
var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs");
184+
var generatedDefinitionFile = Path.Combine(RazorComponentIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs");
185+
186+
// Assert - 1
187+
var result = await DotnetMSBuild("Build");
188+
189+
Assert.BuildPassed(result);
190+
var outputFile = Path.Combine(OutputPath, "MvcWithComponents.dll");
191+
Assert.FileExists(result, OutputPath, "MvcWithComponents.dll");
192+
var outputAssemblyThumbprint = GetThumbPrint(outputFile);
193+
194+
Assert.FileExists(result, generatedDefinitionFile);
195+
var generatedDefinitionThumbprint = GetThumbPrint(generatedDefinitionFile);
196+
Assert.FileExists(result, generatedFile);
197+
var generatedFileThumbprint = GetThumbPrint(generatedFile);
198+
199+
Assert.FileExists(result, tagHelperOutputCache);
200+
Assert.FileContains(
201+
result,
202+
tagHelperOutputCache,
203+
@"""Name"":""MvcWithComponents.Views.Shared.NavMenu""");
204+
205+
var definitionThumbprint = GetThumbPrint(tagHelperOutputCache);
206+
207+
// Act - 2
208+
ReplaceContent(updatedContent, "Views", "Shared", "NavMenu.razor");
209+
result = await DotnetMSBuild("Build");
210+
211+
// Assert - 2
212+
Assert.FileExists(result, generatedDefinitionFile);
213+
// Definition file remains unchanged.
214+
Assert.Equal(generatedDefinitionThumbprint, GetThumbPrint(generatedDefinitionFile));
215+
Assert.FileExists(result, generatedFile);
216+
// Generated file should change and include the new content.
217+
Assert.NotEqual(generatedFileThumbprint, GetThumbPrint(generatedFile));
218+
Assert.FileContains(result, generatedFile, updatedContent);
219+
220+
// TagHelper cache should remain unchanged.
221+
Assert.Equal(definitionThumbprint, GetThumbPrint(tagHelperOutputCache));
222+
}
223+
175224
[Fact]
176225
[InitializeTestProject("MvcWithComponents")]
177226
public async Task BuildComponents_RegeneratesComponentDefinition_WhenFilesChange()
178227
{
179228
// Act - 1
229+
var updatedContent = "@code { [Parameter] public string AParameter { get; set; } }";
180230
var tagHelperOutputCache = Path.Combine(IntermediateOutputPath, "MvcWithComponents.TagHelpers.output.cache");
181231

182232
var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs");
@@ -204,7 +254,7 @@ public async Task BuildComponents_RegeneratesComponentDefinition_WhenFilesChange
204254
var definitionThumbprint = GetThumbPrint(tagHelperOutputCache);
205255

206256
// Act - 2
207-
ReplaceContent("Different things", "Views", "Shared", "NavMenu.razor");
257+
ReplaceContent(updatedContent, "Views", "Shared", "NavMenu.razor");
208258
result = await DotnetMSBuild("Build");
209259

210260
// Assert - 2
@@ -222,8 +272,12 @@ public async Task BuildComponents_RegeneratesComponentDefinition_WhenFilesChange
222272
tagHelperOutputCache,
223273
@"""Name"":""MvcWithComponents.Views.Shared.NavMenu""");
224274

225-
// TODO:
226-
Assert.Equal(definitionThumbprint, GetThumbPrint(tagHelperOutputCache));
275+
Assert.FileContains(
276+
result,
277+
tagHelperOutputCache,
278+
"AParameter");
279+
280+
Assert.NotEqual(definitionThumbprint, GetThumbPrint(tagHelperOutputCache));
227281
}
228282

229283
[Fact]

src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Component.targets

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Copyright (c) .NET Foundation. All rights reserved.
2929
<!-- Used for tracking inputs to component generation -->
3030
<_RazorComponentInputHash></_RazorComponentInputHash>
3131
<_RazorComponentInputCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).RazorComponent.input.cache</_RazorComponentInputCacheFile>
32+
<_RazorComponentDeclarationOutputCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).RazorComponent.output.cache</_RazorComponentDeclarationOutputCacheFile>
3233
</PropertyGroup>
3334

3435
<ItemGroup>
@@ -85,7 +86,7 @@ Copyright (c) .NET Foundation. All rights reserved.
8586
Name="RazorGenerateComponentDeclaration"
8687
DependsOnTargets="$(RazorGenerateComponentDeclarationDependsOn)"
8788
Inputs="$(MSBuildAllProjects);@(RazorComponentWithTargetPath);$(_RazorComponentInputCacheFile)"
88-
Outputs="@(_RazorComponentDeclaration)"
89+
Outputs="$(_RazorComponentDeclarationOutputCacheFile)"
8990
Condition="'@(RazorComponentWithTargetPath->Count())'!='0'">
9091

9192
<ItemGroup>
@@ -120,8 +121,13 @@ Copyright (c) .NET Foundation. All rights reserved.
120121
TagHelperManifest="$(_RazorComponentDeclarationManifest)"
121122
GenerateDeclaration="true" />
122123

124+
<Touch
125+
Files="$(_RazorComponentDeclarationOutputCacheFile)"
126+
AlwaysCreate="true" />
127+
123128
<ItemGroup>
124129
<FileWrites Include="@(_RazorComponentDeclaration)" />
130+
<FileWrites Include="$(_RazorComponentDeclarationOutputCacheFile)" />
125131
</ItemGroup>
126132
</Target>
127133

0 commit comments

Comments
 (0)