Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Static web assets] Account for HintPath when we compute the build assets #19873

Merged
merged 8 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/BlazorWasmSdk/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# C# files
[*.cs]

#### Core EditorConfig Options ####

# var preferences
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BrotliCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GzipCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorFilesToCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorBuildAssets" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
<UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorPublishAssets" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />

Expand Down Expand Up @@ -260,14 +261,10 @@ Copyright (c) .NET Foundation. All rights reserved.
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="@(_BlazorStaticWebAsset->'%(OriginalItemSpec)')" >
<RelatedAsset>%(Identity)</RelatedAsset>
<AssetRole>Alternative</AssetRole>
<AssetTraitName>Content-Encoding</AssetTraitName>
<AssetTraitValue>gzip</AssetTraitValue>
</_GzipFileToCompressForBuild>
</ItemGroup>

<ComputeBlazorFilesToCompress Assets="@(_BlazorStaticWebAsset)">
<Output TaskParameter="AssetsToCompress" ItemName="_GzipFileToCompressForBuild" />
</ComputeBlazorFilesToCompress>

<GZipCompress
FilesToCompress="@(_GzipFileToCompressForBuild)"
Expand Down
13 changes: 11 additions & 2 deletions src/BlazorWasmSdk/Tasks/BrotliCompress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,20 @@ protected override string GenerateResponseFileCommands()
outputItem.SetMetadata("OriginalItemSpec", file.ItemSpec);
CompressedFiles[i] = outputItem;

if (SkipIfOutputIsNewer && File.Exists(outputFullPath) && File.GetLastWriteTimeUtc(inputFullPath) < File.GetLastWriteTimeUtc(outputFullPath))
if (!File.Exists(outputRelativePath))
{
Log.LogMessage(MessageImportance.Low, $"Skipping compression for '{file.ItemSpec}' because '{outputRelativePath}' is newer than '{file.ItemSpec}'.");
Log.LogMessage(MessageImportance.Low, "Compressing '{0}' because compressed file '{1}' does not exist.", file.ItemSpec, outputRelativePath);
}
else if (File.GetLastWriteTimeUtc(inputFullPath) < 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 '{0}' because '{1}' is newer than '{2}'.", file.ItemSpec, outputRelativePath, file.ItemSpec);
continue;
}
else
{
Log.LogMessage(MessageImportance.Low, "Compressing '{0}' because file is newer than '{1}'.", inputFullPath, outputRelativePath);
}

builder.AppendLine("-s");
builder.AppendLine(inputFullPath);
Expand Down
24 changes: 18 additions & 6 deletions src/BlazorWasmSdk/Tasks/ComputeBlazorBuildAssets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ private static void ApplyUniqueMetadataProperties(ITaskItem candidate)
candidate.SetMetadata("AssetTraitName", "BlazorWebAssemblyResource");
candidate.SetMetadata("AssetTraitValue", "runtime");
}
if (string.Equals(candidate.GetMetadata("ResolvedFrom"), "{HintPathFromItem}", StringComparison.Ordinal))
{
candidate.RemoveMetadata("OriginalItemSpec");
}
break;
case ".wasm":
case ".blat":
Expand Down Expand Up @@ -227,17 +231,25 @@ public static bool ShouldFilterCandidate(
var extension = candidate.GetMetadata("Extension");
var fileName = candidate.GetMetadata("FileName");
var assetType = candidate.GetMetadata("AssetType");
var fromMonoPackage = string.Equals(
candidate.GetMetadata("NuGetPackageId"),
"Microsoft.NETCore.App.Runtime.Mono.browser-wasm",
StringComparison.Ordinal);

reason = extension switch
{
".a" => "extension is .a is not supported.",
".c" => "extension is .c is not supported.",
".h" => "extension is .h is not supported.",
".a" when fromMonoPackage => "extension is .a is not supported.",
".c" when fromMonoPackage => "extension is .c is not supported.",
".h" when fromMonoPackage => "extension is .h is not supported.",
// It is safe to filter out all XML files since we are not interested in any XML file from the list
// of ResolvedFilesToPublish to become a static web asset. Things like this include XML doc files and
// so on.
".xml" => "it is a documentation file",
".rsp" => "extension is .rsp is not supported.",
".props" => "extension is .props is not supported.",
".rsp" when fromMonoPackage => "extension is .rsp is not supported.",
".props" when fromMonoPackage => "extension is .props is not supported.",
".blat" when !timezoneSupport => "timezone support is not enabled.",
".dat" when invariantGlobalization && fileName.StartsWith("icudt") => "invariant globalization is enabled",
".json" when fileName == "emcc-props" => $"{fileName}{extension} is not used by Blazor",
".json" when fromMonoPackage && fileName == "emcc-props" => $"{fileName}{extension} is not used by Blazor",
".js" when fileName == "dotnet" => "dotnet.js is already processed by Blazor",
".js" when assetType == "native" => $"{fileName}{extension} is not used by Blazor",
".pdb" when !copySymbols => "copying symbols is disabled",
Comment on lines +234 to 255
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand Down
76 changes: 76 additions & 0 deletions src/BlazorWasmSdk/Tasks/ComputeBlazorFilesToCompress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Microsoft.NET.Sdk.BlazorWebAssembly
{
// During the blazor build process some assets might not be at their final location by the time we try to compress them.
// For that reason we need to determine the path to use to compress the file, which is what this task deals with.
// We first check on the OriginalItemSpec of the asset and use that if the asset exists there.
// In case it does not, we rely use the ItemSpec, which in case OriginalItemSpec does not exist, should point to an existing file on disk.
// If neither the ItemSpec nor the OriginalItemSpec exist, we issue an error, since it indicates that the asset is not correctly
// defined.
// We can't just use the ItemSpec because for some assets that points to the output folder and causes issues with incrementalism.
public class ComputeBlazorFilesToCompress : Task
{
[Required] public ITaskItem[] Assets { get; set; }

[Output] public ITaskItem[] AssetsToCompress { get; set; }

public override bool Execute()
{
var result = new List<ITaskItem>();

for (var i = 0; i < Assets.Length; i++)
{
var asset = Assets[i];
var originalItemSpec = asset.GetMetadata("OriginalItemSpec");
if (File.Exists(originalItemSpec))
{
Log.LogMessage("Asset '{0}' found at OriginalItemSpec '{1}' and will be used for compressing the asset",
asset.ItemSpec,
originalItemSpec);

result.Add(CreateGzipAsset(asset, originalItemSpec));
}
else if (File.Exists(asset.ItemSpec))
{
Log.LogMessage("Asset '{0}' found at '{1}' and will be used for compressing the asset",
asset.ItemSpec,
asset.ItemSpec);

result.Add(CreateGzipAsset(asset, asset.ItemSpec));
}
else
{
Log.LogError("The asset '{0}' can not be found at any of the searched locations '{1}' and '{2}'",
asset.ItemSpec,
asset.ItemSpec,
originalItemSpec);
break;
}
}

AssetsToCompress = result.ToArray();

return !Log.HasLoggedErrors;

static TaskItem CreateGzipAsset(ITaskItem asset, string gzipSpec)
{
var result = new TaskItem(gzipSpec, asset.CloneCustomMetadata());

result.SetMetadata("RelatedAsset", asset.ItemSpec);
result.SetMetadata("AssetRole", "Alternative");
result.SetMetadata("AssetTraitName", "Content-Encoding");
result.SetMetadata("AssetTraitValue", "gzip");

return result;
}
}
}
}
12 changes: 10 additions & 2 deletions src/BlazorWasmSdk/Tasks/GZipCompress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,20 @@ public override bool Execute()
outputItem.SetMetadata("OriginalItemSpec", file.ItemSpec);
CompressedFiles[i] = outputItem;

if (File.Exists(outputRelativePath) && File.GetLastWriteTimeUtc(inputFullPath) < File.GetLastWriteTimeUtc(outputRelativePath))
if (!File.Exists(outputRelativePath))
{
Log.LogMessage(MessageImportance.Low, "Compressing '{0}' because compressed file '{1}' does not exist.", file.ItemSpec, outputRelativePath);
}
else if (File.GetLastWriteTimeUtc(inputFullPath) < 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 '{file.ItemSpec}' because '{outputRelativePath}' is newer than '{file.ItemSpec}'.");
Log.LogMessage(MessageImportance.Low, "Skipping '{0}' because '{1}' is newer than '{2}'.", file.ItemSpec, outputRelativePath, file.ItemSpec);
return;
}
else
{
Log.LogMessage(MessageImportance.Low, "Compressing '{0}' because file is newer than '{1}'.", inputFullPath, outputRelativePath);
}

try
{
Expand Down
43 changes: 28 additions & 15 deletions src/BlazorWasmSdk/Tool/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// 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.
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using System;
using System.Collections.Generic;
Expand All @@ -24,10 +25,16 @@ public static int Main(string[] args)
description: "System.IO.Compression.CompressionLevel for the Brotli compression algorithm.");
var sourcesOption = new Option<List<string>>(
"-s",
description: "A list of files to compress.") { AllowMultipleArgumentsPerToken = false };
description: "A list of files to compress.")
{
AllowMultipleArgumentsPerToken = false
};
var outputsOption = new Option<List<string>>(
"-o",
"The filenames to output the compressed file to.") { AllowMultipleArgumentsPerToken = false };
"The filenames to output the compressed file to.")
{
AllowMultipleArgumentsPerToken = false
};

brotli.Add(compressionLevelOption);
brotli.Add(sourcesOption);
Expand All @@ -37,18 +44,24 @@ public static int Main(string[] args)

brotli.Handler = CommandHandler.Create<CompressionLevel, List<string>, List<string>>((c, s, o) =>
{
Parallel.For(0, s.Count, i =>
{
var source = s[i];
var output = o[i];

using var sourceStream = File.OpenRead(source);
using var fileStream = new FileStream(output, FileMode.Create);

using var stream = new BrotliStream(fileStream, c);
Parallel.For(0, s.Count, i =>
{
var source = s[i];
var output = o[i];
try
{
using var sourceStream = File.OpenRead(source);
using var fileStream = new FileStream(output, FileMode.Create);

sourceStream.CopyTo(stream);
});
using var stream = new BrotliStream(fileStream, c);
sourceStream.CopyTo(stream);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error compressing '{source}' into '{output}'");
Console.Error.WriteLine(ex.ToString());
}
});
});

return rootCommand.InvokeAsync(args).Result;
Expand Down
9 changes: 9 additions & 0 deletions src/RazorSdk/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# C# files
[*.cs]

#### Core EditorConfig Options ####

# var preferences
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
Original file line number Diff line number Diff line change
Expand Up @@ -281,12 +281,12 @@ Copyright (c) .NET Foundation. All rights reserved.

<!-- TMP: Pack -->
<GenerateStaticWebAssetsPackTargetsDependsOn>
ResolveStaticWebAssetsConfiguration;
LoadStaticWebAssetsBuildManifest;
$(GenerateStaticWebAssetsPackTargetsDependsOn);
</GenerateStaticWebAssetsPackTargetsDependsOn>

<TargetsForTfmSpecificContentInPackage>
ResolveStaticWebAssetsConfiguration;
GenerateStaticWebAssetsPackTargets;
$(TargetsForTfmSpecificContentInPackage)
</TargetsForTfmSpecificContentInPackage>
Expand All @@ -295,7 +295,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<Target Name="ResolveStaticWebAssetsConfiguration">
<PropertyGroup>
<_StaticWebAssetsManifestBase>$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework.ToLowerInvariant())\</_StaticWebAssetsManifestBase>
<_StaticWebAssetsManifestBase>$(BaseIntermediateOutputPath)$(Configuration)\</_StaticWebAssetsManifestBase>
<_StaticWebAssetsManifestBase Condition="'$(AppendTargetFrameworkToOutputPath)' != 'false'">$(_StaticWebAssetsManifestBase)$(TargetFramework.ToLowerInvariant())\</_StaticWebAssetsManifestBase>
<StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">_content/$(PackageId)</StaticWebAssetBasePath>
<StaticWebAssetProjectMode Condition="'$(StaticWebAssetProjectMode)' == ''">Default</StaticWebAssetProjectMode>
<StaticWebAssetBuildManifestPath>$(_StaticWebAssetsManifestBase)staticwebassets.build.json</StaticWebAssetBuildManifestPath>
Expand Down Expand Up @@ -937,6 +938,12 @@ Copyright (c) .NET Foundation. All rights reserved.
<FileWrites Include="$(_GeneratedBuildTransitivePropsFile)" />
</ItemGroup>

<!-- We need to adjust the path for files without extension (LICENSE) for example. Otherwise, when they get packed, nuget creates an
additional folder for the file. -->
<ComputeStaticWebAssetsTargetPaths Assets="@(_CurrentProjectStaticWebAsset)" PathPrefix="staticwebassets" AdjustPathsForPack="true">
<Output TaskParameter="AssetsWithTargetPath" ItemName="_PackStaticWebAssetWithTargetPath" />
</ComputeStaticWebAssetsTargetPaths>

<!-- All files that go into the nuget package -->
<ItemGroup Condition="'$(_CurrentProjectHasStaticWebAssets)' == 'true'">

Expand Down Expand Up @@ -968,8 +975,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<!-- Project file contents -->

<TfmSpecificPackageFile Include="%(_CurrentProjectStaticWebAsset.Identity)">
<PackagePath>staticwebassets\%(_CurrentProjectStaticWebAsset.RelativePath)</PackagePath>
<TfmSpecificPackageFile Include="%(_PackStaticWebAssetWithTargetPath.Identity)">
<PackagePath>%(_PackStaticWebAssetWithTargetPath.TargetPath)</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override bool Execute()
var normalizedOutputPath = StaticWebAsset.NormalizeContentRootPath(Path.GetFullPath(OutputPath));
try
{
foreach (var asset in Assets.Select(a => StaticWebAsset.FromTaskItem(a)))
foreach (var asset in Assets.Select(StaticWebAsset.FromTaskItem))
{
string fileOutputPath = null;
if (!(asset.IsDiscovered() || asset.IsComputed()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class ComputeStaticWebAssetsTargetPaths : Task

public bool UseAlternatePathDirectorySeparator { get; set; }

public bool AdjustPathsForPack { get; set; } = false;

[Output]
public ITaskItem[] AssetsWithTargetPath { get; set; }

Expand All @@ -35,6 +37,11 @@ public override bool Execute()
PathPrefix,
UseAlternatePathDirectorySeparator ? Path.AltDirectorySeparatorChar : Path.DirectorySeparatorChar);

if (AdjustPathsForPack && string.IsNullOrEmpty(Path.GetExtension(targetPath)))
{
targetPath = Path.GetDirectoryName(targetPath);
}

result.SetMetadata("TargetPath", targetPath);

AssetsWithTargetPath[i] = result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# C# files
[*.cs]

#### Core EditorConfig Options ####

# var preferences
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
Loading