From ad725861a9f4a2f36549556faf6efdfac22d90ad Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Wed, 15 Apr 2020 19:26:00 +0200 Subject: [PATCH] [Blazor][Wasm] Build fixes for incrementalism and PWA (#20861) * Fixes incrementalism issue caused by a wrong up to date check * Fixes PWA template on hosted scenario by removing the 'ServiceWorker' from the content itemgroup in all scenarios. --- .../GzipCompressBlazorApplicationFiles.cs | 18 +-- .../src/targets/Blazor.MonoRuntime.targets | 8 +- .../ServiceWorkerAssetsManifest.targets | 8 +- .../BuildCompressionTests.cs | 139 ++++++++++++++++++ .../WebAssembly/Compression/src/Program.cs | 18 +-- 5 files changed, 163 insertions(+), 28 deletions(-) diff --git a/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs b/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs index 65aeb409f5..53d26328a3 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs +++ b/src/Components/WebAssembly/Build/src/Tasks/GzipCompressBlazorApplicationFiles.cs @@ -32,18 +32,16 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build var inputSource = file.InputSource; var targetCompressionPath = file.Target; - if (!File.Exists(inputSource) || - (File.Exists(targetCompressionPath) && File.GetLastWriteTime(inputSource) > File.GetLastWriteTime(targetCompressionPath))) + if (!File.Exists(inputSource)) + { + Log.LogMessage($"Skipping '{inputPath}' because '{inputSource}' does not exist."); + return; + } + + if (File.Exists(targetCompressionPath) && File.GetLastWriteTimeUtc(inputSource) < File.GetLastWriteTimeUtc(targetCompressionPath)) { // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. - if (!File.Exists(inputSource)) - { - Log.LogMessage($"Skipping '{inputPath}' because '{inputSource}' does not exist."); - } - else - { - Log.LogMessage($"Skipping '{inputPath}' because '{inputSource}' is newer than '{targetCompressionPath}'."); - } + Log.LogMessage($"Skipping '{inputPath}' because '{targetCompressionPath}' is newer than '{inputSource}'."); return; } diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets index ebddf882f0..ddb7556f0b 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets @@ -150,9 +150,9 @@ - <_ExistingBlazorOutputWithTargetPath Include="@(_BlazorOutputWithTargetPath)" Condition="Exists('%(FullPath)')" /> + <_ExistingBlazorOutputWithTargetPath Include="@(_BlazorOutputWithTargetPath)" Condition="Exists('%(FullPath)')" /> - + @@ -160,7 +160,7 @@ <_BlazorOutputWithIntegrity Include="@(_BlazorOutputWithHash)"> %(_BlazorOutputWithHash.FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash' + $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash <_BlazorOutputWithTargetPath Remove="@(_BlazorOutputWithIntegrity)" /> @@ -413,7 +413,7 @@ <_BlazorBootJsonWithIntegrity Include="@(_BlazorBootJsonWithHash)"> %(FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash' + $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash <_BlazorOutputWithTargetPath Include="@(_BlazorBootJsonWithIntegrity)" RemoveMetadata="FileHash;FileHashAlgorithm"> diff --git a/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets b/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets index fcf1b38503..74654a8d33 100644 --- a/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets +++ b/src/Components/WebAssembly/Build/src/targets/ServiceWorkerAssetsManifest.targets @@ -55,7 +55,7 @@ - + @@ -64,7 +64,7 @@ <_ServiceWorkerManifestWithIntegrity Include="@(_ServiceWorkerManifestWithHash)"> %(FileHash) - $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash' + $(IntermediateOutputPath)integrity\$([System.String]::Copy('%(FileHash)').Replace('/','-').Replace('+','_')).hash @@ -74,7 +74,7 @@ - <_ServiceWorkerManifestIntegrityFile>$(IntermediateOutputPath)integrity\$([System.String]::Copy('%(_ServiceWorkerManifestWithIntegrity.FileHash)').Replace('/','-').Replace('+','_')).hash' + <_ServiceWorkerManifestIntegrityFile>$(IntermediateOutputPath)integrity\$([System.String]::Copy('%(_ServiceWorkerManifestWithIntegrity.FileHash)').Replace('/','-').Replace('+','_')).hash @@ -178,7 +178,7 @@ + BeforeTargets="AssignTargetPaths;ResolveCurrentProjectStaticWebAssets"> diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs index 97f26ca308..476035b503 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildCompressionTests.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -40,6 +41,45 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } } + [Fact] + public async Task Build_WithLinkerAndCompression_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project); + + Assert.BuildPassed(result); + + var mainAppDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.gz"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var blazorBootJson = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.gz"); + var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + // Act + var programFile = Path.Combine(project.DirectoryPath, "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + result = await MSBuildProcessManager.DotnetMSBuild(project); + + // Assert + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + + Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); + Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); + } + [Fact] public async Task Build_WithoutLinkerAndCompression_IsIncremental() { @@ -70,6 +110,34 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build } } + [Fact] + public async Task Build_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("standalone", additionalProjects: new[] { "razorclasslibrary" }); + var result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); + + // Act + var mainAppDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.BuildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.gz"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var programFile = Path.Combine(project.DirectoryPath, "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, args: "/p:BlazorWebAssemblyEnableLinking=false"); + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + } + [Fact] public async Task Build_CompressesAllFrameworkFiles() { @@ -147,6 +215,77 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.False(Directory.Exists(compressedFilesPath)); } + [Fact] + public async Task Publish_WithLinkerAndCompression_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); + project.TargetFramework = "netcoreapp3.1"; + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + + Assert.BuildPassed(result); + + // Act + var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.br"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var blazorBootJson = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); + var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.br"); + var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + var programFile = Path.Combine(project.DirectoryPath, "..", "standalone", "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish"); + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); + var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + + Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); + Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); + } + + [Fact] + public async Task Publish_WithoutLinkerAndCompression_UpdatesFilesWhenSourcesChange() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "standalone", "razorclasslibrary" }); + project.TargetFramework = "netcoreapp3.1"; + var result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); + + Assert.BuildPassed(result); + + // Act + var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); + var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + + var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll.br"); + var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + var programFile = Path.Combine(project.DirectoryPath, "..", "standalone", "Program.cs"); + var programFileContents = File.ReadAllText(programFile); + File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); + + // Assert + result = await MSBuildProcessManager.DotnetMSBuild(project, target: "publish", args: "/p:BlazorWebAssemblyEnableLinking=false"); + Assert.BuildPassed(result); + var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); + var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); + + Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); + Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); + } + [Fact] public async Task Publish_WithLinkerAndCompression_IsIncremental() { diff --git a/src/Components/WebAssembly/Compression/src/Program.cs b/src/Components/WebAssembly/Compression/src/Program.cs index 093b2159ef..3391e96036 100644 --- a/src/Components/WebAssembly/Compression/src/Program.cs +++ b/src/Components/WebAssembly/Compression/src/Program.cs @@ -35,18 +35,16 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build.BrotliCompression var inputSource = file.InputSource; var targetCompressionPath = file.Target; - if (!File.Exists(inputSource) || - (File.Exists(targetCompressionPath) && File.GetLastWriteTime(inputSource) > File.GetLastWriteTime(targetCompressionPath))) + if (!File.Exists(inputSource)) + { + Console.WriteLine($"Skipping '{inputPath}' because '{inputSource}' does not exist."); + return; + } + + if (File.Exists(targetCompressionPath) && File.GetLastWriteTimeUtc(inputSource) < File.GetLastWriteTimeUtc(targetCompressionPath)) { // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. - if (!File.Exists(inputSource)) - { - Console.WriteLine($"Skipping '{inputPath}' because '{inputSource}' does not exist."); - } - else - { - Console.WriteLine($"Skipping '{inputPath}' because '{inputSource}' is newer than '{targetCompressionPath}'."); - } + Console.WriteLine($"Skipping '{inputPath}' because '{targetCompressionPath}' is newer than '{inputSource}'."); return; }