Skip to content

Commit

Permalink
[Static web assets] Several fixes for additional scenarios
Browse files Browse the repository at this point in the history
* Fixes an issue with projects not working with reference assemblies
  using HintPath.
* Fixes an issue where files without an extension where being included
  inside an additional folder with the file name on nuget packages.
* Adds additional error logging for the brotli compression tool.
* Adds additional login for the gzip compress task.
* Adds editor config files in the blazor wasm and razor sdk folders
  to handle 'var' usage preferences in ASP.NET Core projects.
* Performs a more selective filtering for static web assets candidates
  from the build candidates in blazor webassembly applications.
* Fixes an issue with AppendTargetFramework=false causing issues

dotnet/aspnetcore#35349
dotnet/aspnetcore#32744
dotnet/aspnetcore#29561
dotnet/aspnetcore#35561
  • Loading branch information
javiercn authored Aug 20, 2021
1 parent 962de9e commit 843f0f9
Show file tree
Hide file tree
Showing 26 changed files with 5,649 additions and 305 deletions.
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",
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

0 comments on commit 843f0f9

Please sign in to comment.