Merge pull request dotnet/aspnetcore-tooling#1490 from dotnet-maestro-bot/merge/release/3.1-to-master

[automated] Merge branch 'release/3.1' => 'master'\n\nCommit migrated from 2b4debb7e6
This commit is contained in:
Pranav K 2020-01-17 14:58:49 -08:00 committed by GitHub
commit 61cd85b436
13 changed files with 515 additions and 29 deletions

View File

@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
{
private const string ContentRoot = "ContentRoot";
private const string BasePath = "BasePath";
private const string SourceId = "SourceId";
[Required]
public string TargetManifestPath { get; set; }
@ -76,13 +77,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
// so it needs to always be '/'.
var normalizedBasePath = basePath.Replace("\\", "/");
// contentRoot can have forward and trailing slashes and sometimes consecutive directory
// separators. To be more flexible we will normalize the content root so that it contains a
// single trailing separator.
var normalizedContentRoot = $"{contentRoot.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)}{Path.DirectorySeparatorChar}";
// At this point we already know that there are no elements with different base paths and same content roots
// or viceversa. Here we simply skip additional items that have the same base path and same content root.
if (!nodes.Exists(e => e.Attribute(BasePath).Value.Equals(normalizedBasePath, StringComparison.OrdinalIgnoreCase)))
if (!nodes.Exists(e => e.Attribute("BasePath").Value.Equals(normalizedBasePath, StringComparison.OrdinalIgnoreCase) &&
e.Attribute("Path").Value.Equals(normalizedContentRoot, StringComparison.OrdinalIgnoreCase)))
{
nodes.Add(new XElement("ContentRoot",
new XAttribute("BasePath", normalizedBasePath),
new XAttribute("Path", contentRoot)));
new XAttribute("Path", normalizedContentRoot)));
}
}
@ -102,7 +109,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
{
var contentRootDefinition = ContentRootDefinitions[i];
if (!EnsureRequiredMetadata(contentRootDefinition, BasePath) ||
!EnsureRequiredMetadata(contentRootDefinition, ContentRoot))
!EnsureRequiredMetadata(contentRootDefinition, ContentRoot) ||
!EnsureRequiredMetadata(contentRootDefinition, SourceId))
{
return false;
}
@ -126,23 +134,35 @@ namespace Microsoft.AspNetCore.Razor.Tasks
var contentRootDefinition = ContentRootDefinitions[i];
var basePath = contentRootDefinition.GetMetadata(BasePath);
var contentRoot = contentRootDefinition.GetMetadata(ContentRoot);
var sourceId = contentRootDefinition.GetMetadata(SourceId);
if (basePaths.TryGetValue(basePath, out var existingBasePath))
{
var existingBasePathContentRoot = existingBasePath.GetMetadata(ContentRoot);
if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase))
var existingSourceId = existingBasePath.GetMetadata(SourceId);
if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase) &&
// We want to check this case to allow for client-side blazor projects to have multiple different content
// root sources exposed under the same base path while still requiring unique base paths/content roots across
// project/package boundaries.
!string.Equals(sourceId, existingSourceId, StringComparison.OrdinalIgnoreCase))
{
// Case:
// Item1: /_content/Library, /package/aspnetContent1
// Item2: /_content/Library, /package/aspnetContent2
// Item2: /_content/Library, project:/project/aspnetContent2
// Item1: /_content/Library, package:/package/aspnetContent1
Log.LogError($"Duplicate base paths '{basePath}' for content root paths '{contentRoot}' and '{existingBasePathContentRoot}'. " +
$"('{contentRootDefinition.ItemSpec}', '{existingBasePath.ItemSpec}')");
return false;
}
// It was a duplicate, so we skip it.
// Case:
// Item1: /_content/Library, /package/aspnetContent
// Item2: /_content/Library, /package/aspnetContent
// Item1: /_content/Library, project:/project/aspnetContent
// Item2: /_content/Library, project:/project/aspnetContent
// It was a separate content root exposed from the same project/package, so we skip it.
// Case:
// Item1: /_content/Library, project:/project/aspnetContent/bin/debug/netstandard2.1/dist
// Item2: /_content/Library, project:/project/wwwroot
}
else
{

View File

@ -0,0 +1,86 @@
// 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.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.AspNetCore.Razor.Tasks
{
public class ValidateStaticWebAssetsUniquePaths : Task
{
private const string BasePath = "BasePath";
private const string RelativePath = "RelativePath";
private const string TargetPath = "TargetPath";
[Required]
public ITaskItem[] StaticWebAssets { get; set; }
[Required]
public ITaskItem[] WebRootFiles { get; set; }
public override bool Execute()
{
var assetsByWebRootPaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < StaticWebAssets.Length; i++)
{
var contentRootDefinition = StaticWebAssets[i];
if (!EnsureRequiredMetadata(contentRootDefinition, BasePath) ||
!EnsureRequiredMetadata(contentRootDefinition, RelativePath))
{
return false;
}
else
{
var webRootPath = GetWebRootPath(
contentRootDefinition.GetMetadata(BasePath),
contentRootDefinition.GetMetadata(RelativePath));
if (assetsByWebRootPaths.TryGetValue(webRootPath, out var existingWebRootPath))
{
if (!string.Equals(contentRootDefinition.ItemSpec, existingWebRootPath.ItemSpec, StringComparison.OrdinalIgnoreCase))
{
Log.LogError($"Conflicting assets with the same path '{webRootPath}' for content root paths '{contentRootDefinition.ItemSpec}' and '{existingWebRootPath.ItemSpec}'.");
return false;
}
}
else
{
assetsByWebRootPaths.Add(webRootPath, contentRootDefinition);
}
}
}
for (var i = 0; i < WebRootFiles.Length; i++)
{
var webRootFile = WebRootFiles[i];
var relativePath = webRootFile.GetMetadata(TargetPath);
var webRootFileWebRootPath = GetWebRootPath("/", relativePath);
if (assetsByWebRootPaths.TryGetValue(webRootFileWebRootPath, out var existingAsset))
{
Log.LogError($"The static web asset '{existingAsset.ItemSpec}' has a conflicting web root path '{webRootFileWebRootPath}' with the project file '{webRootFile.ItemSpec}'.");
return false;
}
}
return true;
}
// Normalizes /base/relative \base\relative\ base\relative and so on to /base/relative
private string GetWebRootPath(string basePath, string relativePath) => $"/{Path.Combine(basePath, relativePath.TrimStart('.').TrimStart('/')).Replace("\\", "/").Trim('/')}";
private bool EnsureRequiredMetadata(ITaskItem item, string metadataName)
{
var value = item.GetMetadata(metadataName);
if (string.IsNullOrEmpty(value))
{
Log.LogError($"Missing required metadata '{metadataName}' for '{item.ItemSpec}'.");
return false;
}
return true;
}
}
}

View File

@ -32,6 +32,11 @@ Copyright (c) .NET Foundation. All rights reserved.
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
<UsingTask
TaskName="Microsoft.AspNetCore.Razor.Tasks.ValidateStaticWebAssetsUniquePaths"
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
<UsingTask
TaskName="Microsoft.AspNetCore.Razor.Tasks.GenerateStaticWebAsssetsPropsFile"
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
@ -54,23 +59,19 @@ Copyright (c) .NET Foundation. All rights reserved.
$(GetCurrentProjectStaticWebAssetsDependsOn)
</GetCurrentProjectStaticWebAssetsDependsOn>
<AssignTargetPathsDependsOn>
<GetCopyToOutputDirectoryItemsDependsOn>
$(GetCopyToOutputDirectoryItemsDependsOn);
GenerateStaticWebAssetsManifest;
$(AssignTargetPathsDependsOn)
</AssignTargetPathsDependsOn>
</GetCopyToOutputDirectoryItemsDependsOn>
<ResolveStaticWebAssetsInputsDependsOn>
ResolveCurrentProjectStaticWebAssetsInputs;
$(ResolveStaticWebAssetsInputsDependsOn)
</ResolveStaticWebAssetsInputsDependsOn>
<ResolveStaticWebAssetsInputsDependsOn Condition="$(NoBuild) != 'true'">
ResolveReferencedProjectsStaticWebAssets;
$(ResolveStaticWebAssetsInputsDependsOn)
</ResolveStaticWebAssetsInputsDependsOn>
<ResolveReferencedProjectsStaticWebAssetsDependsOn>
ResolveReferences;
PrepareProjectReferences;
$(ResolveReferencedProjectsStaticWebAssetsDependsOn)
</ResolveReferencedProjectsStaticWebAssetsDependsOn>
@ -145,11 +146,12 @@ Copyright (c) .NET Foundation. All rights reserved.
Condition="'%(SourceType)' != ''">
<BasePath>%(StaticWebAsset.BasePath)</BasePath>
<ContentRoot>%(StaticWebAsset.ContentRoot)</ContentRoot>
<SourceId>%(StaticWebAsset.SourceId)</SourceId>
</_ExternalStaticWebAsset>
</ItemGroup>
<!-- We need a transform here to make sure we hash the metadata -->
<Hash ItemsToHash="@(_ExternalStaticWebAsset->'%(Identity)%(BasePath)%(ContentRoot)')">
<Hash ItemsToHash="@(_ExternalStaticWebAsset->'%(Identity)%(SourceId)%(BasePath)%(ContentRoot)')">
<Output TaskParameter="HashResult" PropertyName="_StaticWebAssetsCacheHash" />
</Hash>
@ -181,20 +183,29 @@ Copyright (c) .NET Foundation. All rights reserved.
Outputs="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
DependsOnTargets="$(GenerateStaticWebAssetsManifestDependsOn)">
<ItemGroup>
<_WebRootFiles Include="@(ContentWithTargetPath)" Condition="$([System.String]::Copy('%(TargetPath)').Replace('\','/').StartsWith('wwwroot/'))" />
<_ReferencedStaticWebAssets Include="@(StaticWebAsset)" Condition="'%(SourceType)' != ''" />
</ItemGroup>
<ValidateStaticWebAssetsUniquePaths
StaticWebAssets="@(_ReferencedStaticWebAssets)"
WebRootFiles="@(_WebRootFiles)" />
<GenerateStaticWebAssetsManifest
ContentRootDefinitions="@(_ExternalStaticWebAsset)"
TargetManifestPath="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
<!-- This is the list of inputs that will be used for generating the manifest used during development. -->
<ItemGroup>
<Content
<ContentWithTargetPath
Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
Condition="'@(_ExternalStaticWebAsset->Count())' != '0'"
Link="$(TargetName).StaticWebAssets.xml">
Condition="'@(_ExternalStaticWebAsset->Count())' != '0'">
<TargetPath>$(TargetName).StaticWebAssets.xml</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
@ -273,7 +284,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<_ThisProjectStaticWebAsset
Include="@(Content)"
Condition="$([System.String]::Copy('%(Identity)').StartsWith('wwwroot'))">
Condition="$([System.String]::Copy('%(Identity)').Replace('\','/').StartsWith('wwwroot/'))">
<!-- Remove the wwwroot\ prefix -->
<RelativePath>$([System.String]::Copy('%(Identity)').Substring(8))</RelativePath>
@ -506,14 +517,15 @@ Copyright (c) .NET Foundation. All rights reserved.
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))'))</RelativePath>
</_ExternalPublishStaticWebAsset>
<!-- Remove any existing external static web asset that might have been added as part of the
regular publish pipeline. -->
<ResolvedFileToPublish Remove="@(_ExternalPublishStaticWebAsset)" />
<ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)" />
<ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>

View File

@ -89,12 +89,14 @@ namespace Microsoft.AspNetCore.Razor.Tasks
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
{
["BasePath"] = "MyLibrary",
["ContentRoot"] = Path.Combine("nuget","MyLibrary")
["ContentRoot"] = Path.Combine("nuget", "MyLibrary"),
["SourceId"] = "MyLibrary"
}),
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
{
["BasePath"] = "MyLibrary",
["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary")
["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary"),
["SourceId"] = "MyOtherLibrary"
})
}
};
@ -111,6 +113,58 @@ namespace Microsoft.AspNetCore.Razor.Tasks
message);
}
[Fact]
public void AllowsMultipleContentRootsWithSameBasePath_ForTheSameSourceId()
{
// Arrange
var file = Path.GetTempFileName();
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
<ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", $"Blazor.Client{Path.DirectorySeparatorChar}")}"" />
<ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", "bin", "debug", $"netstandard2.1{Path.DirectorySeparatorChar}")}"" />
</StaticWebAssets>";
var buildEngine = new Mock<IBuildEngine>();
var task = new GenerateStaticWebAssetsManifest
{
BuildEngine = buildEngine.Object,
ContentRootDefinitions = new TaskItem[]
{
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
{
["BasePath"] = "Blazor.Client",
["ContentRoot"] = Path.Combine(".", "nuget","Blazor.Client"),
["SourceId"] = "Blazor.Client"
}),
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
{
["BasePath"] = "Blazor.Client",
["ContentRoot"] = Path.Combine(".", "nuget", "bin","debug","netstandard2.1"),
["SourceId"] = "Blazor.Client"
})
},
TargetManifestPath = file
};
try
{
// Act
var result = task.Execute();
// Assert
Assert.True(result);
var document = File.ReadAllText(file);
Assert.Equal(expectedDocument, document);
}
finally
{
if (File.Exists(file))
{
File.Delete(file);
}
}
}
[Fact]
public void ReturnsError_ForDuplicateContentRoots()
{
@ -128,11 +182,13 @@ namespace Microsoft.AspNetCore.Razor.Tasks
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
{
["BasePath"] = "MyLibrary",
["SourceId"] = "MyLibrary",
["ContentRoot"] = Path.Combine(".", "MyLibrary")
}),
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
{
["BasePath"] = "MyOtherLibrary",
["SourceId"] = "MyOtherLibrary",
["ContentRoot"] = Path.Combine(".", "MyLibrary")
})
}
@ -191,7 +247,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
// Arrange
var file = Path.GetTempFileName();
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
<ContentRoot BasePath=""MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
<ContentRoot BasePath=""MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", $"razorContent{Path.DirectorySeparatorChar}")}"" />
</StaticWebAssets>";
try
@ -206,7 +262,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
{
["BasePath"] = "MyLibrary",
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent"),
["SourceId"] = "MyLibrary"
}),
},
TargetManifestPath = file
@ -235,7 +292,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
// Arrange
var file = Path.GetTempFileName();
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
<ContentRoot BasePath=""Base/MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
<ContentRoot BasePath=""Base/MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", $"razorContent{Path.DirectorySeparatorChar}")}"" />
</StaticWebAssets>";
try
@ -251,12 +308,14 @@ namespace Microsoft.AspNetCore.Razor.Tasks
{
// Base path needs to be normalized to '/' as it goes in the url
["BasePath"] = "Base\\MyLibrary",
["SourceId"] = "MyLibrary",
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
}),
// Comparisons are case insensitive
CreateItem(Path.Combine("wwwroot, site.css"), new Dictionary<string,string>
{
["BasePath"] = "Base\\MyLIBRARY",
["SourceId"] = "MyLibrary",
["ContentRoot"] = Path.Combine(".", "nuget", "MyLIBRARY", "razorContent")
}),
},

View File

@ -2,7 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
@ -221,5 +224,49 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.BuildOutputContainsLine(result, "Content: appsettings.json CopyToOutputDirectory= CopyToPublishDirectory=PreserveNewest ExcludeFromSingleFile=true");
Assert.BuildOutputContainsLine(result, "Content: appsettings.Development.json CopyToOutputDirectory= CopyToPublishDirectory=PreserveNewest ExcludeFromSingleFile=true");
}
[Fact]
[InitializeTestProject("SimpleMvc")]
public async Task IntrospectRazorTasksDllPath()
{
// Regression test for https://github.com/aspnet/AspNetCore/issues/17308
var solutionRoot = GetType().Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
.First(a => a.Key == "Testing.RepoRoot")
.Value;
var tfm =
#if NETCOREAPP5_0
"netcoreapp5.0";
#else
#error Target framework needs to be updated.
#endif
var expected = Path.Combine(solutionRoot, "artifacts", "bin", "Microsoft.NET.Sdk.Razor", Configuration, "sdk-output", "tasks", tfm, "Microsoft.NET.Sdk.Razor.Tasks.dll");
// Verifies the fix for https://github.com/aspnet/AspNetCore/issues/17308
var result = await DotnetMSBuild("_IntrospectRazorTasks");
Assert.BuildPassed(result);
Assert.BuildOutputContainsLine(result, $"RazorTasksPath: {expected}");
}
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
[InitializeTestProject("SimpleMvc")]
public async Task IntrospectRazorTasksDllPath_DesktopMsBuild()
{
var solutionRoot = GetType().Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
.First(a => a.Key == "Testing.RepoRoot")
.Value;
var tfm = "net46";
var expected = Path.Combine(solutionRoot, "artifacts", "bin", "Microsoft.NET.Sdk.Razor", Configuration, "sdk-output", "tasks", tfm, "Microsoft.NET.Sdk.Razor.Tasks.dll");
// Verifies the fix for https://github.com/aspnet/AspNetCore/issues/17308
var result = await DotnetMSBuild("_IntrospectRazorTasks", msBuildProcessKind: MSBuildProcessKind.Desktop);
Assert.BuildPassed(result);
Assert.BuildOutputContainsLine(result, $"RazorTasksPath: {expected}");
}
}
}

View File

@ -40,6 +40,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
protected string PublishOutputPath => Path.Combine(OutputPath, "publish");
protected string GetRidSpecificPublishOutputPath(string rid) => Path.Combine(OutputPath, rid, "publish");
// Used by the test framework to set the project that we're working with
internal static ProjectDirectory Project
{

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.CommandLineUtils;
using Xunit;
using Xunit.Abstractions;
@ -83,6 +84,31 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileDoesNotExist(result, PublishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
}
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
[InitializeTestProject("AppWithPackageAndP2PReferenceAndRID", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
public async Task Publish_CopiesStaticWebAssetsToDestinationFolder_PublishSingleFile()
{
var result = await DotnetMSBuild("Publish", $"/restore /p:PublishSingleFile=true /p:ReferenceLocallyBuiltPackages=true");
Assert.BuildPassed(result);
var publishOutputPath = GetRidSpecificPublishOutputPath("win-x64");
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
// Validate that static web assets don't get published as content too on their regular path
Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.v4.js"));
// Validate that the manifest never gets copied
Assert.FileDoesNotExist(result, publishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
}
[Fact]
[InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
public async Task Publish_WithBuildReferencesDisabled_CopiesStaticWebAssetsToDestinationFolder()
@ -104,6 +130,26 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
}
[Fact]
[InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
public async Task Publish_NoBuild_CopiesStaticWebAssetsToDestinationFolder()
{
var build = await DotnetMSBuild("Build", "/restore");
Assert.BuildPassed(build);
var publish = await DotnetMSBuild("Publish", "/p:NoBuild=true");
Assert.BuildPassed(publish);
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
}
[Fact]
[InitializeTestProject("SimpleMvc")]

View File

@ -53,6 +53,11 @@
<_Parameter2>$(ProcDumpPath)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>Testing.RepoRoot</_Parameter1>
<_Parameter2>$(RepoRoot)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
<!-- The test projects rely on these binaries being available -->

View File

@ -0,0 +1,141 @@
// 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.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Tasks
{
public class ValidateStaticWebAssetsUniquePathsTest
{
[Fact]
public void ReturnsError_WhenStaticWebAssetsWebRootPathMatchesExistingContentItemPath()
{
// Arrange
var errorMessages = new List<string>();
var buildEngine = new Mock<IBuildEngine>();
buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
.Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
var task = new ValidateStaticWebAssetsUniquePaths
{
BuildEngine = buildEngine.Object,
StaticWebAssets = new TaskItem[]
{
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["BasePath"] = "/",
["RelativePath"] = "/sample.js",
})
},
WebRootFiles = new TaskItem[]
{
CreateItem(Path.Combine(".", "App", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["TargetPath"] = "/SAMPLE.js",
})
}
};
// Act
var result = task.Execute();
// Assert
Assert.False(result);
var message = Assert.Single(errorMessages);
Assert.Equal($"The static web asset '{Path.Combine(".", "Library", "wwwroot", "sample.js")}' has a conflicting web root path '/SAMPLE.js' with the project file '{Path.Combine(".", "App", "wwwroot", "sample.js")}'.", message);
}
[Fact]
public void ReturnsError_WhenMultipleStaticWebAssetsHaveTheSameWebRootPath()
{
// Arrange
var errorMessages = new List<string>();
var buildEngine = new Mock<IBuildEngine>();
buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
.Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
var task = new ValidateStaticWebAssetsUniquePaths
{
BuildEngine = buildEngine.Object,
StaticWebAssets = new TaskItem[]
{
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["BasePath"] = "/",
["RelativePath"] = "/sample.js",
}),
CreateItem(Path.Combine(".", "Library", "bin", "dist", "sample.js"), new Dictionary<string,string>
{
["BasePath"] = "/",
["RelativePath"] = "/sample.js",
})
}
};
// Act
var result = task.Execute();
// Assert
Assert.False(result);
var message = Assert.Single(errorMessages);
Assert.Equal($"Conflicting assets with the same path '/sample.js' for content root paths '{Path.Combine(".", "Library", "bin", "dist", "sample.js")}' and '{Path.Combine(".", "Library", "wwwroot", "sample.js")}'.", message);
}
[Fact]
public void ReturnsSuccess_WhenStaticWebAssetsDontConflictWithApplicationContentItems()
{
// Arrange
var errorMessages = new List<string>();
var buildEngine = new Mock<IBuildEngine>();
var task = new ValidateStaticWebAssetsUniquePaths
{
BuildEngine = buildEngine.Object,
StaticWebAssets = new TaskItem[]
{
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["BasePath"] = "/_library",
["RelativePath"] = "/sample.js",
}),
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["BasePath"] = "/_library",
["RelativePath"] = "/sample.js",
})
},
WebRootFiles = new TaskItem[]
{
CreateItem(Path.Combine(".", "App", "wwwroot", "sample.js"), new Dictionary<string,string>
{
["TargetPath"] = "/SAMPLE.js",
})
}
};
// Act
var result = task.Execute();
// Assert
Assert.True(result);
}
private static TaskItem CreateItem(
string spec,
IDictionary<string, string> metadata)
{
var result = new TaskItem(spec);
foreach (var (key, value) in metadata)
{
result.SetMetadata(key, value);
}
return result;
}
}
}

View File

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<RazorSdkDirectoryRoot>$(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\</RazorSdkDirectoryRoot>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<RuntimeAdditionalRestoreSources Condition="'$(RuntimeAdditionalRestoreSources)'=='' AND EXISTS('$(MSBuildThisFileDirectory)..\TestPackageRestoreSource\')">$(MSBuildThisFileDirectory)..\TestPackageRestoreSource\</RuntimeAdditionalRestoreSources>
<RestoreAdditionalProjectSources>$(RestoreAdditionalProjectSources);$(RuntimeAdditionalRestoreSources)</RestoreAdditionalProjectSources>
</PropertyGroup>
<ItemGroup>
<!-- Avoid referencing the AspNetCore framework since we want the ability to target work-in-progress shims -->
<FrameworkReference Remove="Microsoft.AspNetCore.App" />
<!-- RID specific installs attempts to restore runtime packs which cause cyclical dependencies .-->
<KnownFrameworkReference Remove="Microsoft.AspNetCore.App" />
<KnownFrameworkReference Remove="Microsoft.WindowsDesktop.App" />
<KnownFrameworkReference Remove="Microsoft.WindowsDesktop.App.WPF" />
<KnownFrameworkReference Remove="Microsoft.WindowsDesktop.App.WindowsForms" />
</ItemGroup>
<PropertyGroup Condition="'$(RunningAsTest)' == ''">
<!-- We don't want to run build server when not running as tests. -->
<UseRazorBuildServer>false</UseRazorBuildServer>
</PropertyGroup>
<!-- Test Placeholder -->
<ItemGroup>
<PackageReference Include="PackageLibraryDirectDependency" Version="1.0.0" Condition="'$(ReferenceLocallyBuiltPackages)' == 'true'" />
<ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(BinariesRoot)'==''">
<!-- In test scenarios $(BinariesRoot) is defined in a generated Directory.Build.props file -->
<BinariesRoot>$(RepositoryRoot)artifacts\bin\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\$(Configuration)\netstandard2.0\</BinariesRoot>
</PropertyGroup>
<ItemGroup Condition="'$(BinariesRoot)'!=''">
<Reference Include="$(BinariesRoot)\Microsoft.AspNetCore.Razor.Test.ComponentShim.dll" />
<Reference Include="$(BinariesRoot)\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.dll" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@

namespace AppWithP2PReference
{
public class Program
{
public static void Main(string[] args)
{
// Just make sure we have a reference to the MvcShim
var t = typeof(Microsoft.AspNetCore.Mvc.IActionResult);
System.Console.WriteLine(t.FullName);
}
}
}

View File

@ -46,4 +46,11 @@
<Target Name="_IntrospectContentItems">
<Message Text="Content: %(Content.Identity) CopyToOutputDirectory=%(Content.CopyToOutputDirectory) CopyToPublishDirectory=%(Content.CopyToPublishDirectory) ExcludeFromSingleFile=%(Content.ExcludeFromSingleFile)" Importance="High" />
</Target>
<Target Name="_IntrospectRazorTasks">
<PropertyGroup>
<_SdkTaskPath>$([System.IO.Path]::GetFullPath('$(RazorSdkBuildTasksAssembly)'))</_SdkTaskPath>
</PropertyGroup>
<Message Text="RazorTasksPath: $(_SdkTaskPath)" Importance="High" />
</Target>
</Project>

View File

@ -10,6 +10,7 @@
<ProjectReference Include="..\ClassLibraryMvc21\ClassLibraryMvc21.csproj" />
<ProjectReference Include="..\AppWithP2PReference\AppWithP2PReference.csproj" />
<ProjectReference Include="..\AppWithPackageAndP2PReferenceAndRID\AppWithPackageAndP2PReferenceAndRID.csproj" />
<ProjectReference Include="..\ClassLibrary\ClassLibrary.csproj" />
<ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" />
<ProjectReference Include="..\ClassLibraryMvc21\ClassLibraryMvc21.csproj" />