Add support for gzip compression during build and publish (#23611)
* Add support for gzip compression during build and publish 3.2 shipped with gzip compression during build and publish. During the port to 5.0, the build and publish pipeline was different and ended up only during brotli compression during publish. However, during build the app size is now up to 20MB. Statically compressing runtime assets during build reduces the payload size to about 8.5 MB. This should help with faster initial boot ups and perception. * Quarantine test * More quarantine
This commit is contained in:
parent
8768cab874
commit
499a3bcdc1
|
|
@ -319,6 +319,22 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string DirectoryExists(MSBuildResult result, params string[] paths)
|
||||||
|
{
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths));
|
||||||
|
if (!Directory.Exists(filePath))
|
||||||
|
{
|
||||||
|
throw new DirectoryMissingException(result, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
|
public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern, SearchOption searchOption = SearchOption.AllDirectories)
|
||||||
{
|
{
|
||||||
if (result == null)
|
if (result == null)
|
||||||
|
|
@ -820,6 +836,19 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
protected override string Heading => $"File: '{FilePath}' was not found.";
|
protected override string Heading => $"File: '{FilePath}' was not found.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DirectoryMissingException : MSBuildXunitException
|
||||||
|
{
|
||||||
|
public DirectoryMissingException(MSBuildResult result, string directoryPath)
|
||||||
|
: base(result)
|
||||||
|
{
|
||||||
|
DirectoryPath = directoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DirectoryPath { get; }
|
||||||
|
|
||||||
|
protected override string Heading => $"Directory: '{DirectoryPath}' was not found.";
|
||||||
|
}
|
||||||
|
|
||||||
private class FileCountException : MSBuildXunitException
|
private class FileCountException : MSBuildXunitException
|
||||||
{
|
{
|
||||||
public FileCountException(MSBuildResult result, int expected, string directoryPath, string searchPattern, string[] files)
|
public FileCountException(MSBuildResult result, int expected, string directoryPath, string searchPattern, string[] files)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
public class WasmBuildIncrementalismTest
|
public class WasmBuildIncrementalismTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Build_WithLinker_IsIncremental()
|
public async Task Build_IsIncremental()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
|
using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
|
||||||
|
|
@ -40,6 +40,37 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Build_GzipCompression_IsIncremental()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" });
|
||||||
|
var result = await MSBuildProcessManager.DotnetMSBuild(project);
|
||||||
|
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var gzipCompressionDirectory = Path.Combine(project.IntermediateOutputDirectory, "build-gz");
|
||||||
|
|
||||||
|
Assert.DirectoryExists(result, gzipCompressionDirectory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, gzipCompressionDirectory);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
result = await MSBuildProcessManager.DotnetMSBuild(project);
|
||||||
|
Assert.BuildPassed(result);
|
||||||
|
|
||||||
|
var newThumbPrint = FileThumbPrint.CreateFolderThumbprint(project, gzipCompressionDirectory);
|
||||||
|
Assert.Equal(thumbPrint.Count, newThumbPrint.Count);
|
||||||
|
for (var j = 0; j < thumbPrint.Count; j++)
|
||||||
|
{
|
||||||
|
Assert.Equal(thumbPrint[j], newThumbPrint[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Build_SatelliteAssembliesFileIsPreserved()
|
public async Task Build_SatelliteAssembliesFileIsPreserved()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,14 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm.gz");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll.gz"); // Verify dependencies are part of the output.
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.dll");
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.dll.gz");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
|
||||||
|
|
||||||
|
|
@ -55,10 +59,14 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm");
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm.gz");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName);
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.Text.Json.dll.gz");
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.dll");
|
||||||
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "System.dll.gz");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm.pdb");
|
||||||
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
|
Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "RazorClassLibrary.pdb");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
var buildOutputDirectory = project.BuildOutputDirectory;
|
var buildOutputDirectory = project.BuildOutputDirectory;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli");
|
var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "compress");
|
||||||
var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
|
var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
var buildOutputDirectory = project.BuildOutputDirectory;
|
var buildOutputDirectory = project.BuildOutputDirectory;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "brotli");
|
var compressedFilesFolder = Path.Combine("..", "blazorwasm", project.IntermediateOutputDirectory, "compress");
|
||||||
var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
|
var thumbPrint = FileThumbPrint.CreateFolderThumbprint(project, compressedFilesFolder);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -157,6 +157,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
var extension = Path.GetExtension(file);
|
var extension = Path.GetExtension(file);
|
||||||
if (extension != ".br" && extension != ".gz")
|
if (extension != ".br" && extension != ".gz")
|
||||||
{
|
{
|
||||||
|
Assert.FileExists(result, file + ".gz");
|
||||||
Assert.FileExists(result, file + ".br");
|
Assert.FileExists(result, file + ".br");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
|
|
||||||
// Verify compression works
|
// Verify compression works
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br");
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); //
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); //
|
||||||
|
|
||||||
// Verify static assets are in the publish directory
|
// Verify static assets are in the publish directory
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "index.html");
|
Assert.FileExists(result, blazorPublishDirectory, "index.html");
|
||||||
|
|
@ -311,6 +311,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
||||||
|
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz");
|
||||||
|
|
||||||
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
|
|
@ -352,6 +357,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[QuarantinedTest]
|
||||||
public async Task Publish_HostedApp_WithSatelliteAssemblies()
|
public async Task Publish_HostedApp_WithSatelliteAssemblies()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -444,6 +450,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
||||||
|
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz");
|
||||||
|
|
||||||
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
|
|
@ -539,6 +550,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
// Regression test to verify satellite assemblies from the blazor app are copied to the published app's wwwroot output directory as
|
// Regression test to verify satellite assemblies from the blazor app are copied to the published app's wwwroot output directory as
|
||||||
// part of publishing in VS
|
// part of publishing in VS
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[QuarantinedTest]
|
||||||
public async Task Publish_HostedApp_VisualStudio_WithSatelliteAssemblies()
|
public async Task Publish_HostedApp_VisualStudio_WithSatelliteAssemblies()
|
||||||
{
|
{
|
||||||
// Simulates publishing the same way VS does by setting BuildProjectReferences=false.
|
// Simulates publishing the same way VS does by setting BuildProjectReferences=false.
|
||||||
|
|
@ -643,6 +655,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br");
|
||||||
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br");
|
||||||
|
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz");
|
||||||
|
Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz");
|
||||||
|
|
||||||
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
VerifyServiceWorkerFiles(result, blazorPublishDirectory,
|
||||||
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
|
||||||
serviceWorkerContent: "// This is the production service worker",
|
serviceWorkerContent: "// This is the production service worker",
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
var input = FilesToCompress[i];
|
var input = FilesToCompress[i];
|
||||||
var inputFullPath = input.GetMetadata("FullPath");
|
var inputFullPath = input.GetMetadata("FullPath");
|
||||||
var relativePath = input.GetMetadata("RelativePath");
|
var relativePath = input.GetMetadata("RelativePath");
|
||||||
var outputRelativePath = Path.Combine(OutputDirectory, CalculateTargetPath(relativePath));
|
var outputRelativePath = Path.Combine(OutputDirectory, CalculateTargetPath(relativePath, ".br"));
|
||||||
|
|
||||||
var outputItem = new TaskItem(outputRelativePath);
|
var outputItem = new TaskItem(outputRelativePath);
|
||||||
input.CopyMetadataTo(outputItem);
|
input.CopyMetadataTo(outputItem);
|
||||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CalculateTargetPath(string relativePath)
|
internal static string CalculateTargetPath(string relativePath, string extension)
|
||||||
{
|
{
|
||||||
// RelativePath can be long and if used as-is to write the output, might result in long path issues on Windows.
|
// RelativePath can be long and if used as-is to write the output, might result in long path issues on Windows.
|
||||||
// Instead we'll calculate a fixed length path by hashing the input file name. This uses SHA1 similar to the Hash task in MSBuild
|
// Instead we'll calculate a fixed length path by hashing the input file name. This uses SHA1 similar to the Hash task in MSBuild
|
||||||
|
|
@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
builder.Append(InvalidPathChars.Contains(c) ? '+' : c);
|
builder.Append(InvalidPathChars.Contains(c) ? '+' : c);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Append(".br");
|
builder.Append(extension);
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
// 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.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
using Microsoft.Build.Utilities;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||||
|
{
|
||||||
|
public class GZipCompress : Task
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public ITaskItem[] FilesToCompress { get; set; }
|
||||||
|
|
||||||
|
[Output]
|
||||||
|
public ITaskItem[] CompressedFiles { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string OutputDirectory { get; set; }
|
||||||
|
|
||||||
|
public override bool Execute()
|
||||||
|
{
|
||||||
|
CompressedFiles = new ITaskItem[FilesToCompress.Length];
|
||||||
|
|
||||||
|
Directory.CreateDirectory(OutputDirectory);
|
||||||
|
|
||||||
|
System.Threading.Tasks.Parallel.For(0, FilesToCompress.Length, i =>
|
||||||
|
{
|
||||||
|
var file = FilesToCompress[i];
|
||||||
|
var inputPath = file.ItemSpec;
|
||||||
|
var relativePath = file.GetMetadata("RelativePath");
|
||||||
|
var outputRelativePath = Path.Combine(
|
||||||
|
OutputDirectory,
|
||||||
|
BrotliCompress.CalculateTargetPath(relativePath, ".gz"));
|
||||||
|
|
||||||
|
var outputItem = new TaskItem(outputRelativePath);
|
||||||
|
outputItem.SetMetadata("RelativePath", relativePath + ".gz");
|
||||||
|
CompressedFiles[i] = outputItem;
|
||||||
|
|
||||||
|
if (File.Exists(outputRelativePath) && File.GetLastWriteTimeUtc(inputPath) < File.GetLastWriteTimeUtc(outputRelativePath))
|
||||||
|
{
|
||||||
|
// Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing.
|
||||||
|
Log.LogMessage(MessageImportance.Low, $"Skipping '{inputPath}' because '{outputRelativePath}' is newer than '{inputPath}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var sourceStream = File.OpenRead(inputPath);
|
||||||
|
using var fileStream = File.Create(outputRelativePath);
|
||||||
|
using var stream = new GZipStream(fileStream, CompressionLevel.Optimal);
|
||||||
|
|
||||||
|
sourceStream.CopyTo(stream);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.LogErrorFromException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return !Log.HasLoggedErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BlazorWriteSatelliteAssemblyFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BlazorWriteSatelliteAssemblyFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BlazorReadSatelliteAssemblyFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BlazorReadSatelliteAssemblyFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BrotliCompress" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.BrotliCompress" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.GzipCompress" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
@ -151,6 +152,30 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="_ProcessBlazorWasmOutputs" DependsOnTargets="_ResolveBlazorWasmOutputs">
|
<Target Name="_ProcessBlazorWasmOutputs" DependsOnTargets="_ResolveBlazorWasmOutputs">
|
||||||
|
<PropertyGroup>
|
||||||
|
<_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\</_BlazorBuildGZipCompressDirectory>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Compress referenced binaries using GZip during build. This skips files such as the project's assemblies
|
||||||
|
that change from build to build. Runtime assets contribute to the bulk of the download size. Compressing it
|
||||||
|
has the most benefit while avoiding any ongoing costs to the dev inner loop.
|
||||||
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<_GzipFileToCompressForBuild
|
||||||
|
Include="@(ReferenceCopyLocalPaths)"
|
||||||
|
RelativePath="$(_BlazorOutputPath)%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)"
|
||||||
|
Condition="'%(Extension)' == '.dll' or '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<GZipCompress
|
||||||
|
FilesToCompress="@(_GzipFileToCompressForBuild)"
|
||||||
|
OutputDirectory="$(_BlazorBuildGZipCompressDirectory)">
|
||||||
|
|
||||||
|
<Output TaskParameter="CompressedFiles" ItemName="_BlazorBuildGZipCompressedFile" />
|
||||||
|
<Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
|
||||||
|
</GZipCompress>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" />
|
<_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" />
|
||||||
|
|
||||||
|
|
@ -238,6 +263,15 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||||
</_BlazorWebAssemblyStaticWebAsset>
|
</_BlazorWebAssemblyStaticWebAsset>
|
||||||
|
|
||||||
|
<_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile)">
|
||||||
|
<SourceId>$(PackageId)</SourceId>
|
||||||
|
<SourceType></SourceType>
|
||||||
|
<ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot>
|
||||||
|
<BasePath>$(StaticWebAssetBasePath)</BasePath>
|
||||||
|
<RelativePath>$([System.String]::Copy('%(_BlazorBuildGZipCompressedFile.RelativePath)').Replace('\','/').Substring(8))</RelativePath>
|
||||||
|
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||||
|
</_BlazorWebAssemblyStaticWebAsset>
|
||||||
|
|
||||||
<StaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" />
|
<StaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" />
|
||||||
<_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" />
|
<_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -305,6 +339,20 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
|
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
|
||||||
</Copy>
|
</Copy>
|
||||||
|
|
||||||
|
<Copy
|
||||||
|
SourceFiles="@(_BlazorBuildGZipCompressedFile)"
|
||||||
|
DestinationFiles="@(_BlazorBuildGZipCompressedFile->'$(OutDir)%(RelativePath)')"
|
||||||
|
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
||||||
|
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
||||||
|
Retries="$(CopyRetryCount)"
|
||||||
|
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
|
||||||
|
UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
|
||||||
|
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)"
|
||||||
|
ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)">
|
||||||
|
|
||||||
|
<Output TaskParameter="DestinationFiles" ItemName="FileWrites"/>
|
||||||
|
</Copy>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink">
|
<Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink">
|
||||||
|
|
@ -411,12 +459,12 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
<Target Name="_BlazorCompressPublishFiles" AfterTargets="_ProcessPublishFilesForBlazor" Condition="'$(BlazorEnableCompression)' != 'false'">
|
<Target Name="_BlazorCompressPublishFiles" AfterTargets="_ProcessPublishFilesForBlazor" Condition="'$(BlazorEnableCompression)' != 'false'">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<_CompressedFileOutputPath>$(IntermediateOutputPath)brotli\</_CompressedFileOutputPath>
|
<_CompressedFileOutputPath>$(IntermediateOutputPath)compress\</_CompressedFileOutputPath>
|
||||||
<_BlazorWebAssemblyBrotliIncremental>true</_BlazorWebAssemblyBrotliIncremental>
|
<_BlazorWebAssemblyBrotliIncremental>true</_BlazorWebAssemblyBrotliIncremental>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_BrotliFileToCompress
|
<_FileToCompress
|
||||||
Include="@(ResolvedFileToPublish)"
|
Include="@(ResolvedFileToPublish)"
|
||||||
Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" />
|
Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -427,7 +475,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
<BrotliCompress
|
<BrotliCompress
|
||||||
OutputDirectory="$(_CompressedFileOutputPath)"
|
OutputDirectory="$(_CompressedFileOutputPath)"
|
||||||
FilesToCompress="@(_BrotliFileToCompress)"
|
FilesToCompress="@(_FileToCompress)"
|
||||||
CompressionLevel="$(_BlazorBrotliCompressionLevel)"
|
CompressionLevel="$(_BlazorBrotliCompressionLevel)"
|
||||||
SkipIfOutputIsNewer="$(_BlazorWebAssemblyBrotliIncremental)"
|
SkipIfOutputIsNewer="$(_BlazorWebAssemblyBrotliIncremental)"
|
||||||
ToolAssembly="$(_RazorSdkToolAssembly)">
|
ToolAssembly="$(_RazorSdkToolAssembly)">
|
||||||
|
|
@ -436,8 +484,17 @@ Copyright (c) .NET Foundation. All rights reserved.
|
||||||
<Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
|
<Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
|
||||||
</BrotliCompress>
|
</BrotliCompress>
|
||||||
|
|
||||||
|
<GZipCompress
|
||||||
|
OutputDirectory="$(_CompressedFileOutputPath)"
|
||||||
|
FilesToCompress="@(_FileToCompress)">
|
||||||
|
|
||||||
|
<Output TaskParameter="CompressedFiles" ItemName="_BlazorPublishGZipCompressedFile" />
|
||||||
|
<Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
|
||||||
|
</GZipCompress>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResolvedFileToPublish Include="@(_BrotliCompressedFile)" />
|
<ResolvedFileToPublish Include="@(_BrotliCompressedFile)" />
|
||||||
|
<ResolvedFileToPublish Include="@(_BlazorPublishGZipCompressedFile)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue