Skip to content

Commit

Permalink
[release/9.0.1xx] [StaticWebAssets] Improve Up to date check logic (#…
Browse files Browse the repository at this point in the history
…43156)

Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
Co-authored-by: campersau <buchholz.bastian@googlemail.com>
  • Loading branch information
3 people committed Sep 10, 2024
1 parent 65dbcdb commit b8e84b1
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<Project>
<PropertyGroup>
<CollectUpToDateCheckInputDesignTimeDependsOn>
$(CollectUpToDateCheckInputDesignTimeDependsOn);
ResolveStaticWebAssetsConfiguration;
ResolveProjectStaticWebAssets;
CollectStaticWebAssetInputsDesignTime;
</CollectUpToDateCheckInputDesignTimeDependsOn>
<CollectUpToDateCheckOutputDesignTimeDependsOn>
$(CollectUpToDateCheckOutputDesignTimeDependsOn);
ResolveStaticWebAssetsConfiguration;
CollectStaticWebAssetOutputsDesignTime;
</CollectUpToDateCheckOutputDesignTimeDependsOn>
</PropertyGroup>

<Target Name="CollectStaticWebAssetInputsDesignTime">

<ReadLinesFromFile
File="$(StaticWebAssetUpToDateCheckManifestPath)"
Condition="Exists('$(StaticWebAssetUpToDateCheckManifestPath)')"
>
<Output TaskParameter="Lines" ItemName="_StaticWebAssetUpToDateCheckInput" />
</ReadLinesFromFile>

<ReadLinesFromFile
File="$(StaticWebAssetReferencesUpToDateCheckManifestPath)"
Condition="Exists('$(StaticWebAssetReferencesUpToDateCheckManifestPath)')"
>
<Output TaskParameter="Lines" ItemName="_StaticWebAssetReferenceUpToDateCheckInput" />
</ReadLinesFromFile>

<ItemGroup>
<_UpToDateCheckStaticWebAssetResolved Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
</ItemGroup>

<ItemGroup>
<_UpToDateCheckStaticWebAssetResolvedCandidate Include="@(_UpToDateCheckStaticWebAssetResolved->'%(OriginalItemSpec)')" />
<_StaticWebAssetUpToDateCheckInput Include="@(_UpToDateCheckStaticWebAssetResolvedCandidate->Distinct()->'%(FullPath)')" />
</ItemGroup>

<ItemGroup>
<_ExistingStaticWebAssetUpToDateCheckInput Include="%(_StaticWebAssetUpToDateCheckInput.FullPath)" Condition="Exists('%(_StaticWebAssetUpToDateCheckInput.FullPath)')" />
<_NonExistingStaticWebAssetUpToDateCheckInput Include="%(_StaticWebAssetUpToDateCheckInput.FullPath)" Condition="!Exists('%(_StaticWebAssetUpToDateCheckInput.FullPath)')" />
</ItemGroup>

<WriteLinesToFile
File="$(StaticWebAssetUpToDateCheckRemovedManifestPath)"
Lines="@(_NonExistingStaticWebAssetUpToDateCheckInput)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />

<ItemGroup>
<UpToDateCheckInput Condition="'@(_NonExistingStaticWebAssetUpToDateCheckInput)' != ''" Include="$(StaticWebAssetUpToDateCheckRemovedManifestPath)" Set="StaticWebAssets" />
<UpToDateCheckInput Include="@(_ExistingStaticWebAssetUpToDateCheckInput)" Set="StaticWebAssets" />
<UpToDateCheckInput Include="@(_StaticWebAssetReferenceUpToDateCheckInput)" Set="StaticWebAssets" />
</ItemGroup>

</Target>

<Target Name="CollectStaticWebAssetOutputsDesignTime">

<ItemGroup>
<UpToDateCheckOutput Include="$(StaticWebAssetBuildManifestPath)" Set="StaticWebAssets" />
</ItemGroup>

</Target>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ Copyright (c) .NET Foundation. All rights reserved.
<Output TaskParameter="TargetOutputs" ItemName="_ReferencedProjectsConfiguration" />
</MSBuild>

<ItemGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<_ReferenceManifestPath Include="@(_ReferencedProjectsConfiguration->'%(BuildManifestPath)')" Condition="'%(_ReferencedProjectsConfiguration.BuildManifestPath)' != ''" />
</ItemGroup>

<WriteLinesToFile Condition="'$(BuildingInsideVisualStudio)' == 'true'"
File="$(StaticWebAssetReferencesUpToDateCheckManifestPath)"
Lines="@(_ReferenceManifestPath)"
Overwrite="false"
WriteOnlyWhenDifferent="true" />

<MergeConfigurationProperties
CandidateConfigurations="@(_ReferencedProjectsConfiguration)"
ProjectReferences="@(_StaticWebAssetProjectReference)">
Expand Down Expand Up @@ -85,6 +95,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<GetPublishAssetsTargets>$(StaticWebAssetsGetPublishAssetsTargets)</GetPublishAssetsTargets>
<AdditionalPublishProperties>$(StaticWebAssetsAdditionalPublishProperties)</AdditionalPublishProperties>
<AdditionalPublishPropertiesToRemove>$(StaticWebAssetsAdditionalPublishPropertiesToRemove)</AdditionalPublishPropertiesToRemove>
<!-- Build manifest -->
<BuildManifestPath>$([System.IO.Path]::GetFullPath('$(StaticWebAssetBuildManifestPath)'))</BuildManifestPath>
</_StaticWebAssetThisProjectConfiguration>
</ItemGroup>
</Target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Copyright (c) .NET Foundation. All rights reserved.
TaskName="Microsoft.AspNetCore.StaticWebAssets.Tasks.FilterStaticWebAssetEndpoints"
AssemblyFile="$(StaticWebAssetsSdkBuildTasksAssembly)"
Condition="'$(StaticWebAssetsSdkBuildTasksAssembly)' != ''" />

<UsingTask
TaskName="Microsoft.AspNetCore.StaticWebAssets.Tasks.UpdateStaticWebAssetEndpoints"
AssemblyFile="$(StaticWebAssetsSdkBuildTasksAssembly)"
Expand Down Expand Up @@ -285,7 +285,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<!-- For preview only, to ease in the new implementation -->
<PrepareForRunDependsOn>StaticWebAssetsPrepareForRun;$(PrepareForRunDependsOn)</PrepareForRunDependsOn>

<StaticWebAssetsPrepareForRunDependsOn>$(StaticWebAssetsPrepareForRunDependsOn);ResolveBuildStaticWebAssets;GenerateStaticWebAssetsManifest;CopyStaticWebAssetsToOutputDirectory</StaticWebAssetsPrepareForRunDependsOn>
<StaticWebAssetsPrepareForRunDependsOn>$(StaticWebAssetsPrepareForRunDependsOn);ResolveBuildStaticWebAssets;GenerateStaticWebAssetsManifest;CopyStaticWebAssetsToOutputDirectory;</StaticWebAssetsPrepareForRunDependsOn>
<StaticWebAssetsPrepareForRunDependsOn Condition="'$(BuildingInsideVisualStudio)' == 'true'">$(StaticWebAssetsPrepareForRunDependsOn);WriteStaticWebAssetsUpToDateCheck;</StaticWebAssetsPrepareForRunDependsOn>

<!-- This is a hook for features like scoped CSS and Blazor can ensure all the assets have been generated -->
<GenerateComputedBuildStaticWebAssetsDependsOn>ResolveCoreStaticWebAssets;$(GenerateComputedBuildStaticWebAssetsDependsOn)</GenerateComputedBuildStaticWebAssetsDependsOn>
Expand Down Expand Up @@ -457,6 +458,11 @@ Copyright (c) .NET Foundation. All rights reserved.
<_StaticWebAssetsGeneratedBuildPropsFileImportPath>..\build\$(PackageId).props</_StaticWebAssetsGeneratedBuildPropsFileImportPath>
<_StaticWebAssetsGeneratedBuildMultiTargetingPropsFileImportPath>..\buildMultiTargeting\$(PackageId).props</_StaticWebAssetsGeneratedBuildMultiTargetingPropsFileImportPath>

<!-- Design -->
<StaticWebAssetUpToDateCheckManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.upToDateCheck.txt</StaticWebAssetUpToDateCheckManifestPath>
<StaticWebAssetReferencesUpToDateCheckManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.references.upToDateCheck.txt</StaticWebAssetReferencesUpToDateCheckManifestPath>
<StaticWebAssetUpToDateCheckRemovedManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.removed.txt</StaticWebAssetUpToDateCheckRemovedManifestPath>

</PropertyGroup>

<MakeDir
Expand Down Expand Up @@ -528,6 +534,39 @@ Copyright (c) .NET Foundation. All rights reserved.

</Target>

<Target Name="WriteStaticWebAssetsUpToDateCheck" DependsOnTargets="_BuildCopyStaticWebAssetsPreserveNewest;_BuildCopyStaticWebAssetsAlways">

<ItemGroup>
<_UpToDateCheckStaticWebAssetCandidate Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
</ItemGroup>

<ComputeReferenceStaticWebAssetItems
Assets="@(_UpToDateCheckStaticWebAssetCandidate)"
Patterns="@(DiscoveryPattern)"
ProjectMode="$(StaticWebAssetProjectMode)"
AssetKind="Build"
Source="$(PackageId)"
>
<Output TaskParameter="StaticWebAssets" ItemName="_UpToDateCheckStaticWebAssetResolved" />
</ComputeReferenceStaticWebAssetItems>

<ItemGroup>
<_UpToDateCheckStaticWebAssetResolvedCandidate Include="@(_UpToDateCheckStaticWebAssetResolved->'%(OriginalItemSpec)')" />
<_UpToDateCheckStaticWebAsset Include="@(_UpToDateCheckStaticWebAssetResolvedCandidate->Distinct())" />
</ItemGroup>

<WriteLinesToFile
File="$(StaticWebAssetUpToDateCheckManifestPath)"
Lines="@(_UpToDateCheckStaticWebAsset)"
Overwrite="true"
WriteOnlyWhenDifferent="true"
Condition="'@(_UpToDateCheckStaticWebAsset)' != ''" />

<ItemGroup>
<FileWrites Include="$(StaticWebAssetUpToDateCheckManifestPath)" />
</ItemGroup>
</Target>

<Target Name="AddStaticWebAssetsManifest" DependsOnTargets="ResolveStaticWebAssetsConfiguration">
<ItemGroup>
<ContentWithTargetPath Condition="Exists('$(StaticWebAssetDevelopmentManifestPath)')"
Expand Down Expand Up @@ -607,7 +646,6 @@ Copyright (c) .NET Foundation. All rights reserved.

</Target>


<Target Name="ResolveCoreStaticWebAssets" DependsOnTargets="$(ResolveCoreStaticWebAssetsDependsOn)" />

<Target Name="ResolveStaticWebAssetsInputs" DependsOnTargets="$(ResolveStaticWebAssetsInputsDependsOn)" />
Expand Down Expand Up @@ -656,7 +694,7 @@ Copyright (c) .NET Foundation. All rights reserved.

</Target>

<Target Name="UpdateExistingPackageStaticWebAssets">
<Target Name="UpdateExistingPackageStaticWebAssets" Condition="'$(DesignTimeBuild)' != 'true'">
<UpdatePackageStaticWebAssets Assets="@(StaticWebAsset)">
<Output TaskParameter="UpdatedAssets" ItemName="_UpdatedPackageAssets" />
<Output TaskParameter="OriginalAssets" ItemName="_OriginalPackageAssets" />
Expand All @@ -672,6 +710,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.References.targets" />

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.Design.targets" Condition="'$(DesignTimeBuild)' == 'true' and '$(BuildingInsideVisualStudio)' == 'true'" />

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.EmbeddedAssets.targets" />

<Import Project="Microsoft.NET.Sdk.StaticWebAssets.Pack.targets" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ public class StaticWebAssetEndpointsManifest()
{
public int Version { get; set; }

public string ManifestType { get; set; }

public StaticWebAssetEndpoint[] Endpoints { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public override bool Execute()
var manifest = new StaticWebAssetEndpointsManifest()
{
Version = 1,
ManifestType = ManifestType,
Endpoints = [.. filteredEndpoints]
};

Expand Down
161 changes: 161 additions & 0 deletions test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsDesignTimeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.NET.Sdk.Razor.Tests;

public class StaticWebAssetsDesignTimeTest(ITestOutputHelper log) : AspNetSdkBaselineTest(log)
{
#if DEBUG
public const string Configuration = "Debug";
#else
public const string Configuration = "Release";
#endif

[Fact]
public void CollectUpToDateCheckInputOutputsDesignTime_ReportsAddedFiles()
{
// Arrange
var testAsset = "RazorAppWithP2PReference";
ProjectDirectory = AddIntrospection(CreateAspNetSdkTestAsset(testAsset));

var build = CreateBuildCommand(ProjectDirectory, "ClassLibrary");

build.Execute("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:build.binlog").Should().Pass();

File.WriteAllText(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "file.js"), "New File");

var msbuild = CreateMSBuildCommand(
ProjectDirectory,
"ClassLibrary",
"ResolveStaticWebAssetsConfiguration;ResolveProjectStaticWebAssets;CollectStaticWebAssetInputsDesignTime;CollectStaticWebAssetOutputsDesignTime");

msbuild.ExecuteWithoutRestore("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:design.binlog").Should().Pass();

// Check the contents of the input and output files
var inputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCInput.txt");
new FileInfo(inputFilePath).Should().Exist();
var inputFiles = File.ReadAllLines(inputFilePath);
inputFiles.Should().HaveCount(3);
inputFiles.Should().Contain(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "file.js"));
inputFiles.Should().Contain(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "project-transitive-dep.js"));
inputFiles.Should().Contain(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "project-transitive-dep.v4.js"));

var outputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCOutput.txt");
new FileInfo(outputFilePath).Should().Exist();
var outputFiles = File.ReadAllLines(outputFilePath);
outputFiles.Should().ContainSingle();
Path.GetFileName(outputFiles[0]).Should().Be("staticwebassets.build.json");
}

[Fact]
public void CollectUpToDateCheckInputOutputsDesignTime_ReportsRemovedFiles_Once()
{
// Arrange
var testAsset = "RazorAppWithP2PReference";
ProjectDirectory = AddIntrospection(CreateAspNetSdkTestAsset(testAsset));

var build = CreateBuildCommand(ProjectDirectory, "ClassLibrary");

build.Execute("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:build.binlog").Should().Pass();

File.Delete(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "project-transitive-dep.js"));

var msbuild = CreateMSBuildCommand(
ProjectDirectory,
"ClassLibrary",
"ResolveStaticWebAssetsConfiguration;ResolveProjectStaticWebAssets;CollectStaticWebAssetInputsDesignTime;CollectStaticWebAssetOutputsDesignTime");

msbuild.ExecuteWithoutRestore("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:design.binlog").Should().Pass();

// Check the contents of the input and output files
var inputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCInput.txt");
new FileInfo(inputFilePath).Should().Exist();
var inputFiles = File.ReadAllLines(inputFilePath);
inputFiles.Should().HaveCount(2);
inputFiles.Should().Contain(Path.Combine(build.GetIntermediateDirectory().FullName, "staticwebassets.removed.txt"));
inputFiles.Should().Contain(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "wwwroot", "js", "project-transitive-dep.v4.js"));

var outputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCOutput.txt");
new FileInfo(outputFilePath).Should().Exist();
var outputFiles = File.ReadAllLines(outputFilePath);
outputFiles.Should().ContainSingle();
Path.GetFileName(outputFiles[0]).Should().Be("staticwebassets.build.json");
}

[Fact]
public void CollectUpToDateCheckInputOutputsDesignTime_IncludesReferencedProjectsManifests()
{
// Arrange
var testAsset = "RazorAppWithP2PReference";
ProjectDirectory = AddIntrospection(CreateAspNetSdkTestAsset(testAsset));

var build = CreateBuildCommand(ProjectDirectory, "AppWithP2PReference");

build.Execute("/bl:build.binlog").Should().Pass();
build.Execute("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:build.binlog").Should().Pass();

var msbuild = CreateMSBuildCommand(
ProjectDirectory,
"AppWithP2PReference",
"ResolveStaticWebAssetsConfiguration;ResolveProjectStaticWebAssets;CollectStaticWebAssetInputsDesignTime;CollectStaticWebAssetOutputsDesignTime");

msbuild.ExecuteWithoutRestore("/p:DesignTimeBuild=true", "/p:BuildingInsideVisualStudio=true", "/bl:design.binlog").Should().Pass();

// Check the contents of the input and output files
var inputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCInput.txt");
new FileInfo(inputFilePath).Should().Exist();
var inputFiles = File.ReadAllLines(inputFilePath);
inputFiles.Should().HaveCount(1);
inputFiles.Should().Contain(Path.Combine(ProjectDirectory.Path, "ClassLibrary", "obj", "Debug", DefaultTfm, "staticwebassets.build.json"));

var outputFilePath = Path.Combine(build.GetIntermediateDirectory().FullName, "StaticWebAssetsUTDCOutput.txt");
new FileInfo(outputFilePath).Should().Exist();
var outputFiles = File.ReadAllLines(outputFilePath);
outputFiles.Should().ContainSingle();
Path.GetFileName(outputFiles[0]).Should().Be("staticwebassets.build.json");
}

private MSBuildCommand CreateMSBuildCommand(TestAsset testAsset, string relativeProjectPath, string targets)
{
return (MSBuildCommand)new MSBuildCommand(testAsset.Log, targets, testAsset.TestRoot, relativeProjectPath)
.WithWorkingDirectory(testAsset.TestRoot);
}

private TestAsset AddIntrospection(TestAsset testAsset)
{
return testAsset
.WithProjectChanges((name, project) =>
{
project.Document.Root.LastNode.AddAfterSelf(
XElement.Parse("""
<Target Name="ReportInputOutputs" AfterTargets="CollectStaticWebAssetOutputsDesignTime">
<ItemGroup>
<_StaticWebAssetsUTDCInput Include="@(UpToDateCheckInput)" Condition="'%(UpToDateCheckInput.Set)' == 'StaticWebAssets'" />
<_StaticWebAssetsUTDCOutput Include="@(UpToDateCheckOutput)" Condition="'%(UpToDateCheckOutput.Set)' == 'StaticWebAssets'" />
</ItemGroup>
<WriteLinesToFile Condition="'@(_StaticWebAssetsUTDCInput)' != ''"
File="$(IntermediateOutputPath)\StaticWebAssetsUTDCInput.txt"
Lines="@(_StaticWebAssetsUTDCInput->'%(FullPath)')"
Overwrite="true"
Encoding="UTF-8"
/>
<WriteLinesToFile Condition="'@(_StaticWebAssetsUTDCOutput)' != ''"
File="$(IntermediateOutputPath)\StaticWebAssetsUTDCOutput.txt"
Lines="@(_StaticWebAssetsUTDCOutput->'%(FullPath)')"
Overwrite="true"
Encoding="UTF-8"
/>
</Target>
"""
));
});
}
}

0 comments on commit b8e84b1

Please sign in to comment.