From b8e84b16546b108ca575e9714fcbd98831d94131 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:51:27 +0200 Subject: [PATCH] [release/9.0.1xx] [StaticWebAssets] Improve Up to date check logic (#43156) Co-authored-by: Javier Calvarro Nelson Co-authored-by: campersau --- ...oft.NET.Sdk.StaticWebAssets.Design.targets | 68 ++++++++ ...NET.Sdk.StaticWebAssets.References.targets | 12 ++ .../Microsoft.NET.Sdk.StaticWebAssets.targets | 48 +++++- .../Data/StaticWebAssetEndpointsManifest.cs | 2 + ...GenerateStaticWebAssetEndpointsManifest.cs | 1 + .../StaticWebAssetsDesignTimeTest.cs | 161 ++++++++++++++++++ 6 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Design.targets create mode 100644 test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsDesignTimeTest.cs diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Design.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Design.targets new file mode 100644 index 000000000000..a08b453e177d --- /dev/null +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.Design.targets @@ -0,0 +1,68 @@ + + + + $(CollectUpToDateCheckInputDesignTimeDependsOn); + ResolveStaticWebAssetsConfiguration; + ResolveProjectStaticWebAssets; + CollectStaticWebAssetInputsDesignTime; + + + $(CollectUpToDateCheckOutputDesignTimeDependsOn); + ResolveStaticWebAssetsConfiguration; + CollectStaticWebAssetOutputsDesignTime; + + + + + + + + + + + + + + + <_UpToDateCheckStaticWebAssetResolved Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" /> + + + + <_UpToDateCheckStaticWebAssetResolvedCandidate Include="@(_UpToDateCheckStaticWebAssetResolved->'%(OriginalItemSpec)')" /> + <_StaticWebAssetUpToDateCheckInput Include="@(_UpToDateCheckStaticWebAssetResolvedCandidate->Distinct()->'%(FullPath)')" /> + + + + <_ExistingStaticWebAssetUpToDateCheckInput Include="%(_StaticWebAssetUpToDateCheckInput.FullPath)" Condition="Exists('%(_StaticWebAssetUpToDateCheckInput.FullPath)')" /> + <_NonExistingStaticWebAssetUpToDateCheckInput Include="%(_StaticWebAssetUpToDateCheckInput.FullPath)" Condition="!Exists('%(_StaticWebAssetUpToDateCheckInput.FullPath)')" /> + + + + + + + + + + + + + + + + + + + + + diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.References.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.References.targets index b7a893e50656..b4fbaaf70cc7 100644 --- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.References.targets +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.References.targets @@ -46,6 +46,16 @@ Copyright (c) .NET Foundation. All rights reserved. + + <_ReferenceManifestPath Include="@(_ReferencedProjectsConfiguration->'%(BuildManifestPath)')" Condition="'%(_ReferencedProjectsConfiguration.BuildManifestPath)' != ''" /> + + + + @@ -85,6 +95,8 @@ Copyright (c) .NET Foundation. All rights reserved. $(StaticWebAssetsGetPublishAssetsTargets) $(StaticWebAssetsAdditionalPublishProperties) $(StaticWebAssetsAdditionalPublishPropertiesToRemove) + + $([System.IO.Path]::GetFullPath('$(StaticWebAssetBuildManifestPath)')) diff --git a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets index 9ea12ab053d9..3b10c6badc3f 100644 --- a/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets +++ b/src/StaticWebAssetsSdk/Targets/Microsoft.NET.Sdk.StaticWebAssets.targets @@ -101,7 +101,7 @@ Copyright (c) .NET Foundation. All rights reserved. TaskName="Microsoft.AspNetCore.StaticWebAssets.Tasks.FilterStaticWebAssetEndpoints" AssemblyFile="$(StaticWebAssetsSdkBuildTasksAssembly)" Condition="'$(StaticWebAssetsSdkBuildTasksAssembly)' != ''" /> - + StaticWebAssetsPrepareForRun;$(PrepareForRunDependsOn) - $(StaticWebAssetsPrepareForRunDependsOn);ResolveBuildStaticWebAssets;GenerateStaticWebAssetsManifest;CopyStaticWebAssetsToOutputDirectory + $(StaticWebAssetsPrepareForRunDependsOn);ResolveBuildStaticWebAssets;GenerateStaticWebAssetsManifest;CopyStaticWebAssetsToOutputDirectory; + $(StaticWebAssetsPrepareForRunDependsOn);WriteStaticWebAssetsUpToDateCheck; ResolveCoreStaticWebAssets;$(GenerateComputedBuildStaticWebAssetsDependsOn) @@ -457,6 +458,11 @@ Copyright (c) .NET Foundation. All rights reserved. <_StaticWebAssetsGeneratedBuildPropsFileImportPath>..\build\$(PackageId).props <_StaticWebAssetsGeneratedBuildMultiTargetingPropsFileImportPath>..\buildMultiTargeting\$(PackageId).props + + $(_StaticWebAssetsManifestBase)staticwebassets.upToDateCheck.txt + $(_StaticWebAssetsManifestBase)staticwebassets.references.upToDateCheck.txt + $(_StaticWebAssetsManifestBase)staticwebassets.removed.txt + + + + + <_UpToDateCheckStaticWebAssetCandidate Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" /> + + + + + + + + <_UpToDateCheckStaticWebAssetResolvedCandidate Include="@(_UpToDateCheckStaticWebAssetResolved->'%(OriginalItemSpec)')" /> + <_UpToDateCheckStaticWebAsset Include="@(_UpToDateCheckStaticWebAssetResolvedCandidate->Distinct())" /> + + + + + + + + + - @@ -656,7 +694,7 @@ Copyright (c) .NET Foundation. All rights reserved. - + @@ -672,6 +710,8 @@ Copyright (c) .NET Foundation. All rights reserved. + + diff --git a/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetEndpointsManifest.cs b/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetEndpointsManifest.cs index ebeef52a70cc..b10176d13a43 100644 --- a/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetEndpointsManifest.cs +++ b/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetEndpointsManifest.cs @@ -7,5 +7,7 @@ public class StaticWebAssetEndpointsManifest() { public int Version { get; set; } + public string ManifestType { get; set; } + public StaticWebAssetEndpoint[] Endpoints { get; set; } } diff --git a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs index 7fb33248e37f..9dce83a50997 100644 --- a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs +++ b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs @@ -56,6 +56,7 @@ public override bool Execute() var manifest = new StaticWebAssetEndpointsManifest() { Version = 1, + ManifestType = ManifestType, Endpoints = [.. filteredEndpoints] }; diff --git a/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsDesignTimeTest.cs b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsDesignTimeTest.cs new file mode 100644 index 000000000000..a6ec0a905bf1 --- /dev/null +++ b/test/Microsoft.NET.Sdk.Razor.Tests/StaticWebAssetsDesignTimeTest.cs @@ -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(""" + + + <_StaticWebAssetsUTDCInput Include="@(UpToDateCheckInput)" Condition="'%(UpToDateCheckInput.Set)' == 'StaticWebAssets'" /> + <_StaticWebAssetsUTDCOutput Include="@(UpToDateCheckOutput)" Condition="'%(UpToDateCheckOutput.Set)' == 'StaticWebAssets'" /> + + + + + + + """ + )); + }); + } +}