Initial support for static assets in Razor Class Libraries (dotnet/aspnetcore-tooling#580)
* Imports static assets from packages containing custom msbuild
targets defining a StaticWebAsset item group.
* Generates an embeds a manifest into the application assembly
that contains a list of paths to the content roots of the
assets defined in the packages custom msbuild targets.\n\nCommit migrated from 8e70013b70
This commit is contained in:
parent
111e26dd03
commit
13397dd4d6
|
|
@ -0,0 +1,184 @@
|
|||
// 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 System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
public class GenerateStaticWebAssetsManifest : Task
|
||||
{
|
||||
private const string ContentRoot = "ContentRoot";
|
||||
private const string BasePath = "BasePath";
|
||||
|
||||
[Required]
|
||||
public string TargetManifestPath { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] ContentRootDefinitions { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
if (!ValidateArguments())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ExecuteCore();
|
||||
}
|
||||
|
||||
private bool ExecuteCore()
|
||||
{
|
||||
var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||
var root = new XElement(
|
||||
"StaticWebAssets",
|
||||
new XAttribute("Version", "1.0"),
|
||||
CreateNodes());
|
||||
|
||||
document.Add(root);
|
||||
|
||||
var settings = new XmlWriterSettings
|
||||
{
|
||||
Encoding = Encoding.UTF8,
|
||||
CloseOutput = true,
|
||||
OmitXmlDeclaration = true,
|
||||
Indent = true,
|
||||
NewLineOnAttributes = false,
|
||||
Async = true
|
||||
};
|
||||
|
||||
using (var xmlWriter = GetXmlWriter(settings))
|
||||
{
|
||||
document.WriteTo(xmlWriter);
|
||||
}
|
||||
|
||||
return !Log.HasLoggedErrors;
|
||||
}
|
||||
|
||||
private IEnumerable<XElement> CreateNodes()
|
||||
{
|
||||
var nodes = new List<XElement>();
|
||||
for (var i = 0; i < ContentRootDefinitions.Length; i++)
|
||||
{
|
||||
var contentRootDefinition = ContentRootDefinitions[i];
|
||||
var basePath = contentRootDefinition.GetMetadata(BasePath);
|
||||
var contentRoot = contentRootDefinition.GetMetadata(ContentRoot);
|
||||
|
||||
// basePath is meant to be a prefix for the files under contentRoot. MSbuild
|
||||
// normalizes '\' according to the OS, but this is going to be part of the url
|
||||
// so it needs to always be '/'.
|
||||
var normalizedBasePath = basePath.Replace("\\", "/");
|
||||
|
||||
// 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)))
|
||||
{
|
||||
nodes.Add(new XElement("ContentRoot",
|
||||
new XAttribute("BasePath", normalizedBasePath),
|
||||
new XAttribute("Path", contentRoot)));
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private XmlWriter GetXmlWriter(XmlWriterSettings settings)
|
||||
{
|
||||
var fileStream = new FileStream(TargetManifestPath, FileMode.Create);
|
||||
return XmlWriter.Create(fileStream, settings);
|
||||
}
|
||||
|
||||
private bool ValidateArguments()
|
||||
{
|
||||
for (var i = 0; i < ContentRootDefinitions.Length; i++)
|
||||
{
|
||||
var contentRootDefinition = ContentRootDefinitions[i];
|
||||
if (!EnsureRequiredMetadata(contentRootDefinition, BasePath) ||
|
||||
!EnsureRequiredMetadata(contentRootDefinition, ContentRoot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We want to validate that there are no different item groups that share either the same base path
|
||||
// but different content roots or that share the same content root but different base paths.
|
||||
// We pass in all the static web assets that we discovered to this task without making any distinction for
|
||||
// duplicates, so here we skip elements for which we are already tracking an element with the same
|
||||
// content root path and same base path.
|
||||
|
||||
// Case-sensitivity depends on the underlying OS so we are not going to do anything to enforce it here.
|
||||
// Any two items that match base path and content root in a case-insensitive way won't produce an error.
|
||||
// Any other two items will produce an error even if there is only a casing difference between either the
|
||||
// base paths or the content roots.
|
||||
var basePaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
|
||||
var contentRootPaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (var i = 0; i < ContentRootDefinitions.Length; i++)
|
||||
{
|
||||
var contentRootDefinition = ContentRootDefinitions[i];
|
||||
var basePath = contentRootDefinition.GetMetadata(BasePath);
|
||||
var contentRoot = contentRootDefinition.GetMetadata(ContentRoot);
|
||||
|
||||
if (basePaths.TryGetValue(basePath, out var existingBasePath))
|
||||
{
|
||||
var existingBasePathContentRoot = existingBasePath.GetMetadata(ContentRoot);
|
||||
if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Case:
|
||||
// Item1: /_content/Library, /package/aspnetContent1
|
||||
// Item2: /_content/Library, /package/aspnetContent2
|
||||
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
|
||||
}
|
||||
else
|
||||
{
|
||||
if (contentRootPaths.TryGetValue(contentRoot, out var existingContentRoot))
|
||||
{
|
||||
// Case:
|
||||
// Item1: /_content/Library1, /package/aspnetContent
|
||||
// Item2: /_content/Library2, /package/aspnetContent
|
||||
Log.LogError($"Duplicate content root paths '{contentRoot}' for base paths '{basePath}' and '{existingContentRoot.GetMetadata(BasePath)}' " +
|
||||
$"('{contentRootDefinition.ItemSpec}', '{existingContentRoot.ItemSpec}')");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!basePaths.ContainsKey(basePath))
|
||||
{
|
||||
basePaths.Add(basePath, contentRootDefinition);
|
||||
}
|
||||
|
||||
if (!contentRootPaths.ContainsKey(contentRoot))
|
||||
{
|
||||
contentRootPaths.Add(contentRoot, contentRootDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<!--
|
||||
***********************************************************************************************
|
||||
Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
|
||||
|
||||
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
|
||||
created a backup copy. Incorrect changes to this file will make it
|
||||
impossible to load or build your projects from the command-line or the IDE.
|
||||
|
||||
Copyright (c) .NET Foundation. All rights reserved.
|
||||
***********************************************************************************************
|
||||
-->
|
||||
|
||||
<Project ToolsVersion="14.0">
|
||||
|
||||
<!-- Targets that support static content scenarios in ASP.NET Core.
|
||||
The main targets are:
|
||||
* GenerateStaticWebAssetsManifest: Creates a manifest file to use in development with
|
||||
the paths to all the references packages and projects content roots.
|
||||
* ResolveStaticWebAssetsInputs: Collects all the static assets from different sources
|
||||
* Current project.
|
||||
* Referenced project.
|
||||
* Referenced packages.
|
||||
-->
|
||||
|
||||
<UsingTask
|
||||
TaskName="Microsoft.AspNetCore.Razor.Tasks.GenerateStaticWebAssetsManifest"
|
||||
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
|
||||
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateStaticWebAssetsManifestDependsOn>
|
||||
ResolveStaticWebAssetsInputs;
|
||||
_CreateStaticWebAssetsInputsCacheFile
|
||||
</GenerateStaticWebAssetsManifestDependsOn>
|
||||
|
||||
<AssignTargetPathsDependsOn>
|
||||
GenerateStaticWebAssetsManifest;
|
||||
$(AssignTargetPathsDependsOn)
|
||||
</AssignTargetPathsDependsOn>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_GeneratedStaticWebAssetsInputsCacheFile>$(IntermediateOutputPath)$(TargetName).StaticWebAssets.cache</_GeneratedStaticWebAssetsInputsCacheFile>
|
||||
<_GeneratedStaticWebAssetsDevelopmentManifest>$(IntermediateOutputPath)$(TargetName).StaticWebAssets.xml</_GeneratedStaticWebAssetsDevelopmentManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target
|
||||
Name="_CreateStaticWebAssetsInputsCacheFile"
|
||||
DependsOnTargets="ResolveStaticWebAssetsInputs">
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
This is the list of inputs that will be used for generating the manifest used during development.
|
||||
-->
|
||||
<_ExternalStaticWebAsset
|
||||
Include="%(StaticWebAsset.Identity)"
|
||||
Condition="'%(SourceType)' != ''">
|
||||
<BasePath>%(StaticWebAsset.BasePath)</BasePath>
|
||||
<ContentRoot>%(StaticWebAsset.ContentRoot)</ContentRoot>
|
||||
</_ExternalStaticWebAsset>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- We need a transform here to make sure we hash the metadata -->
|
||||
<Hash ItemsToHash="@(_ExternalStaticWebAsset->'%(Identity)%(BasePath)%(ContentRoot)')">
|
||||
<Output TaskParameter="HashResult" PropertyName="_StaticWebAssetsCacheHash" />
|
||||
</Hash>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_StaticWebAssetsCacheHash)"
|
||||
File="$(_GeneratedStaticWebAssetsInputsCacheFile)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_GeneratedStaticWebAssetsInputsCacheFile)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
This target generates a manifest for development time that includes information
|
||||
about the base path for the referenced package and project static web assets. The
|
||||
manifest includes the content root and the base path for each of the referenced
|
||||
packages and projects.
|
||||
|
||||
Ideally, each package/project contains a unique base path and a given content
|
||||
root, but we don't check for duplicates on either of them.
|
||||
-->
|
||||
|
||||
<Target
|
||||
Name="GenerateStaticWebAssetsManifest"
|
||||
Inputs="$(_GeneratedStaticWebAssetsInputsCacheFile)"
|
||||
Outputs="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
|
||||
DependsOnTargets="$(GenerateStaticWebAssetsManifestDependsOn)">
|
||||
|
||||
<GenerateStaticWebAssetsManifest
|
||||
ContentRootDefinitions="@(_ExternalStaticWebAsset)"
|
||||
TargetManifestPath="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
|
||||
|
||||
<!-- This is the list of inputs that will be used for generating the manifest used during development. -->
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Condition="'@(_ExternalStaticWebAsset->Count())' != '0'"
|
||||
Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
|
||||
LogicalName="Microsoft.AspNetCore.StaticWebAssets.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<!-- This target collects all the StaticWebAssets from different sources:
|
||||
* The current project StaticWebAssets that come from wwwroot\** by default.
|
||||
* Assets from referenced projects that get retrieved invoking an MSBuild target on
|
||||
the referenced projects.
|
||||
* Assets from the referenced packages. These will be implicitly included when nuget
|
||||
restores the package and includes the package props file for the package.
|
||||
-->
|
||||
<Target
|
||||
Name="ResolveStaticWebAssetsInputs">
|
||||
<PropertyGroup>
|
||||
<!-- The _SafeBasePath is used as a path segment in the urls that we will
|
||||
be exposing content from when the library is referenced as a package
|
||||
or as a project by a web application. Our convention will be to expose
|
||||
content directly on _content/<<_SafeBasePath>>
|
||||
|
||||
We simply remove the dots from the package id so that a package
|
||||
like Microsoft.AspNetCore.Identity becomes MicrosoftAspNetCoreIdentity
|
||||
|
||||
TODO: Investigate if we need to do something more sophisticated here.
|
||||
-->
|
||||
<_SafeBasePath>$(PackageId.Replace('.',''))</_SafeBasePath>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- StaticWebAssets from the current project -->
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
Should we promote 'wwwroot\**'' to a property?
|
||||
We don't want to capture any content outside the content root, that's why we don't do
|
||||
@(Content) here.
|
||||
-->
|
||||
<StaticWebAsset Include="wwwroot\**" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)">
|
||||
<!-- (PackageReference,ProjectReference,'' (CurrentProject)) -->
|
||||
<SourceType></SourceType>
|
||||
<!-- Identifier describing the source, the package id, the project name, empty for the current project. -->
|
||||
<SourceId></SourceId>
|
||||
<!--
|
||||
Full path to the content root for the item:
|
||||
* For packages it corresponds to %userprofile%/.nuget/packages/<<PackageId>>/<<PackageVersion>>/razorContent
|
||||
* For referenced projects it corresponds to <<FullProjectRefPath>>/wwwroot
|
||||
* For the current projects it corresponds to $(MSBuildThisProjectFileDirectory)wwwroot\
|
||||
-->
|
||||
<ContentRoot>$(MSBuildProjectDirectory)\wwwroot\</ContentRoot>
|
||||
<!-- Subsection (folder) from the url space where content for this library will be served. -->
|
||||
<BasePath>_content\$(_SafeBasePath)\</BasePath>
|
||||
<!-- Relative path from the content root for the file. At publish time, we combine the BasePath + Relative
|
||||
path to determine the final path for the file. -->
|
||||
<RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
|
||||
|
||||
</StaticWebAsset>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- StaticWebAssets from referenced projects. -->
|
||||
<!-- TODO: Include implementation -->
|
||||
|
||||
<!-- StaticWebAssets from packages are already available, so we don't do anything. -->
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -74,6 +74,10 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<None Remove="**\*.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableRazorSdkContent Condition=" '$(UsingMicrosoftNETSdkWeb)' == '' ">true</EnableRazorSdkContent>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import
|
||||
Project="$(MSBuildThisFileDirectory)..\..\Sdk\Sdk.Razor.StaticAssets.ProjectSystem.props"
|
||||
Condition=" '$(EnableRazorSdkContent)' == 'true' " />
|
||||
|
|
|
|||
|
|
@ -338,6 +338,8 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
|
||||
<Import Project="Microsoft.NET.Sdk.Razor.Component.targets" Condition="'$(_Targeting30OrNewerRazorLangVersion)' == 'true'" />
|
||||
|
||||
<Import Project="Microsoft.NET.Sdk.Razor.StaticWebAssets.targets" Condition="'$(_Targeting30OrNewerRazorLangVersion)' == 'true'" />
|
||||
|
||||
<Import Project="Microsoft.NET.Sdk.Razor.GenerateAssemblyInfo.targets" />
|
||||
|
||||
<Import Project="Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets" Condition="'$(_TargetingNETCoreApp30OrLater)' == 'true'" />
|
||||
|
|
@ -480,6 +482,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
'$(EnableDefaultRazorGenerateItems)'=='true'">
|
||||
|
||||
<Content Condition="'%(Content.Extension)'=='.cshtml'" Pack="$(IncludeRazorContentInPack)" />
|
||||
<Content Condition="'%(Content.Extension)'=='.razor'" Pack="$(IncludeRazorContentInPack)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,296 @@
|
|||
// 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 GenerateStaticWebAssetsManifestTest
|
||||
{
|
||||
[Fact]
|
||||
public void ReturnsError_WhenBasePathIsMissing()
|
||||
{
|
||||
// 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 GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine("wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["ContentRoot"] = "/"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal($"Missing required metadata 'BasePath' for '{Path.Combine("wwwroot", "sample.js")}'.", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsError_WhenContentRootIsMissing()
|
||||
{
|
||||
// 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 GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal($"Missing required metadata 'ContentRoot' for '{Path.Combine("wwwroot", "sample.js")}'.", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsError_ForDuplicateBasePaths()
|
||||
{
|
||||
// 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 GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine("nuget","MyLibrary")
|
||||
}),
|
||||
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary")
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal(
|
||||
$"Duplicate base paths 'MyLibrary' for content root paths '{Path.Combine("nuget", "MyOtherLibrary")}' and '{Path.Combine("nuget", "MyLibrary")}'. " +
|
||||
$"('{Path.Combine("wwwroot", "otherLib.js")}', '{Path.Combine("wwwroot", "sample.js")}')",
|
||||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsError_ForDuplicateContentRoots()
|
||||
{
|
||||
// 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 GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "MyLibrary")
|
||||
}),
|
||||
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyOtherLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "MyLibrary")
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal(
|
||||
$"Duplicate content root paths '{Path.Combine(".", "MyLibrary")}' for base paths 'MyOtherLibrary' and 'MyLibrary' " +
|
||||
$"('{Path.Combine("wwwroot", "otherLib.js")}', '{Path.Combine("wwwroot", "sample.js")}')",
|
||||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Generates_EmptyManifest_WhenNoItems_Passed()
|
||||
{
|
||||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = @"<StaticWebAssets Version=""1.0"" />";
|
||||
|
||||
try
|
||||
{
|
||||
var buildEngine = new Mock<IBuildEngine>();
|
||||
|
||||
var task = new GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[] { },
|
||||
TargetManifestPath = file
|
||||
};
|
||||
|
||||
// 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 Generates_Manifest_WhenContentRootsAvailable()
|
||||
{
|
||||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
|
||||
</StaticWebAssets>";
|
||||
|
||||
try
|
||||
{
|
||||
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"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
|
||||
}),
|
||||
},
|
||||
TargetManifestPath = file
|
||||
};
|
||||
|
||||
// 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 SkipsAdditionalElements_WithSameBasePathAndSameContentRoot()
|
||||
{
|
||||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""Base/MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
|
||||
</StaticWebAssets>";
|
||||
|
||||
try
|
||||
{
|
||||
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>
|
||||
{
|
||||
// Base path needs to be normalized to '/' as it goes in the url
|
||||
["BasePath"] = "Base\\MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
|
||||
}),
|
||||
// Comparisons are case insensitive
|
||||
CreateItem(Path.Combine("wwwroot, site.css"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "Base\\MyLIBRARY",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLIBRARY", "razorContent")
|
||||
}),
|
||||
},
|
||||
TargetManifestPath = file
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
|
|
@ -344,6 +345,58 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
}
|
||||
}
|
||||
|
||||
public static Stream ContainsEmbeddedResource(string assemblyPath, string resourceName)
|
||||
{
|
||||
var stream = ExtractEmbeddedResource(assemblyPath, resourceName);
|
||||
Assert.NotNull(stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static void DoesNotContainEmbeddedResource(string assemblyPath, string resourceName)
|
||||
{
|
||||
var stream = ExtractEmbeddedResource(assemblyPath, resourceName);
|
||||
Assert.Null(stream);
|
||||
}
|
||||
|
||||
private static Stream ExtractEmbeddedResource(string path, string expectedResourceName)
|
||||
{
|
||||
using (var peStream = File.OpenRead(path))
|
||||
{
|
||||
using (var peReader = new PEReader(peStream))
|
||||
{
|
||||
var mdReader = peReader.GetMetadataReader();
|
||||
|
||||
foreach (var resourceHandle in mdReader.ManifestResources)
|
||||
{
|
||||
var resource = mdReader.GetManifestResource(resourceHandle);
|
||||
|
||||
if (!resource.Implementation.IsNil)
|
||||
{
|
||||
continue; // resource is not embedded.
|
||||
}
|
||||
|
||||
var resourceName = mdReader.GetString(resource.Name);
|
||||
if (!string.Equals(expectedResourceName, resourceName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We are not taking resource.Offset into account here.
|
||||
// We currently only have the casuistic that we are embedding a single resource.
|
||||
// If that changes we'll have to change this test code, but as its hard we won't do it for now.
|
||||
var resourcesSection = peReader.GetSectionData(peReader.PEHeaders.CorHeader.ResourcesDirectory.RelativeVirtualAddress);
|
||||
var resourcesReader = resourcesSection.GetReader();
|
||||
var resourceSizeInBytes = resourcesReader.ReadInt32();
|
||||
var resourceBytes = resourcesReader.ReadBytes(resourceSizeInBytes);
|
||||
return new MemoryStream(resourceBytes, writable: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void NuspecContains(MSBuildResult result, string nuspecPath, string expected)
|
||||
{
|
||||
if (result == null)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -13,6 +15,9 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
{
|
||||
public abstract class MSBuildIntegrationTestBase
|
||||
{
|
||||
internal static readonly string LocalNugetPackagesCacheTempPath =
|
||||
Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()) + Path.DirectorySeparatorChar;
|
||||
|
||||
private static readonly AsyncLocal<ProjectDirectory> _project = new AsyncLocal<ProjectDirectory>();
|
||||
private static readonly AsyncLocal<string> _projectTfm = new AsyncLocal<string>();
|
||||
|
||||
|
|
@ -42,6 +47,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
set { _project.Value = value; }
|
||||
}
|
||||
|
||||
// Whether to use a local cache or not to prevent polluting the global cache
|
||||
// with test packages.
|
||||
public bool UseLocalPackageCache { get; set; }
|
||||
|
||||
protected string RazorIntermediateOutputPath => Path.Combine(IntermediateOutputPath, "Razor");
|
||||
|
||||
protected string RazorComponentIntermediateOutputPath => Path.Combine(IntermediateOutputPath, "RazorDeclaration");
|
||||
|
|
@ -64,18 +73,42 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
bool runRestoreBeforeBuildOrPublish = true)
|
||||
{
|
||||
var timeout = suppressTimeout ? (TimeSpan?)Timeout.InfiniteTimeSpan : null;
|
||||
|
||||
// Additional restore sources for packages used in testing
|
||||
var additionalRestoreSources = string.Join(
|
||||
',',
|
||||
typeof(PackageTestProjectsFixture).Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
|
||||
.Where(a => a.Key == "Testing.AdditionalRestoreSources")
|
||||
.Select(a => a.Value)
|
||||
.ToArray());
|
||||
|
||||
var buildArgumentList = new List<string>
|
||||
{
|
||||
// Disable node-reuse. We don't want msbuild processes to stick around
|
||||
// once the test is completed.
|
||||
"/nr:false",
|
||||
|
||||
// Always generate a bin log for debugging purposes
|
||||
"/bl",
|
||||
|
||||
// Let the test app know it is running as part of a test.
|
||||
"/p:RunningAsTest=true",
|
||||
|
||||
$"/p:MicrosoftNETCoreApp30PackageVersion={BuildVariables.MicrosoftNETCoreApp30PackageVersion}",
|
||||
|
||||
// Additional restore sources for projects that require built packages
|
||||
$"/p:RuntimeAdditionalRestoreSources={additionalRestoreSources}",
|
||||
};
|
||||
|
||||
if (UseLocalPackageCache)
|
||||
{
|
||||
if (!Directory.Exists(LocalNugetPackagesCacheTempPath))
|
||||
{
|
||||
// The local cache folder needs to exist so that nuget
|
||||
Directory.CreateDirectory(LocalNugetPackagesCacheTempPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppressBuildServer)
|
||||
{
|
||||
buildArgumentList.Add($@"/p:_RazorBuildServerPipeName=""{buildServerPipeName ?? BuildServer.PipeName}""");
|
||||
|
|
@ -110,7 +143,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Project,
|
||||
buildArguments,
|
||||
timeout,
|
||||
msBuildProcessKind);
|
||||
msBuildProcessKind,
|
||||
UseLocalPackageCache ? LocalNugetPackagesCacheTempPath : null);
|
||||
}
|
||||
|
||||
internal void AddProjectFileContent(string content)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -16,7 +19,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
ProjectDirectory project,
|
||||
string arguments,
|
||||
TimeSpan? timeout = null,
|
||||
MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet)
|
||||
MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet,
|
||||
string localPackageCache = null)
|
||||
{
|
||||
var processStartInfo = new ProcessStartInfo()
|
||||
{
|
||||
|
|
@ -26,6 +30,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
RedirectStandardOutput = true,
|
||||
};
|
||||
|
||||
if (localPackageCache != null)
|
||||
{
|
||||
processStartInfo.Environment.Add("NUGET_PACKAGES", localPackageCache);
|
||||
}
|
||||
|
||||
if (msBuildProcessKind == MSBuildProcessKind.Desktop)
|
||||
{
|
||||
if (string.IsNullOrEmpty(BuildVariables.MSBuildPath))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,261 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
{
|
||||
public class StaticWebAssetsIntegrationTest : MSBuildIntegrationTestBase, IClassFixture<BuildServerTestFixture>, IClassFixture<PackageTestProjectsFixture>, IAsyncLifetime
|
||||
{
|
||||
public StaticWebAssetsIntegrationTest(
|
||||
BuildServerTestFixture buildServer,
|
||||
PackageTestProjectsFixture packageTestProjects,
|
||||
ITestOutputHelper output)
|
||||
: base(buildServer)
|
||||
{
|
||||
UseLocalPackageCache = true;
|
||||
PackageTestProjects = packageTestProjects;
|
||||
Output = output;
|
||||
}
|
||||
|
||||
public PackageTestProjectsFixture PackageTestProjects { get; private set; }
|
||||
|
||||
public ITestOutputHelper Output { get; private set; }
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference")]
|
||||
public async Task Build_GeneratesStaticWebAssetsManifest_Success_CreatesManifest()
|
||||
{
|
||||
var result = await DotnetMSBuild("Build", "/restore");
|
||||
|
||||
var expectedManifest = GetExpectedManifest();
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
// GenerateStaticWebAssetsManifest should generate the manifest and the cache.
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache");
|
||||
|
||||
var path = Assert.FileExists(result, OutputPath, "AppWithPackageAndP2PReference.dll");
|
||||
var assembly = Assert.ContainsEmbeddedResource(path, "Microsoft.AspNetCore.StaticWebAssets.xml");
|
||||
using (var reader = new StreamReader(assembly))
|
||||
{
|
||||
var data = await reader.ReadToEndAsync();
|
||||
Assert.Equal(expectedManifest, data);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task Build_DoesNotEmbedManifestWhen_NoStaticResourcesAvailable()
|
||||
{
|
||||
var result = await DotnetMSBuild("Build", "/restore");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
// GenerateStaticWebAssetsManifest should generate the manifest and the cache.
|
||||
Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.StaticWebAssets.xml");
|
||||
Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.StaticWebAssets.cache");
|
||||
|
||||
var path = Assert.FileExists(result, OutputPath, "SimpleMvc.dll");
|
||||
Assert.DoesNotContainEmbeddedResource(path, "SimpleMvc.StaticWebAssets.xml");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference")]
|
||||
public async Task Clean_Success_RemovesManifestAndCache()
|
||||
{
|
||||
var result = await DotnetMSBuild("Build", "/restore");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
// GenerateStaticWebAssetsManifest should generate the manifest and the cache.
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache");
|
||||
|
||||
var cleanResult = await DotnetMSBuild("Clean");
|
||||
|
||||
Assert.BuildPassed(cleanResult);
|
||||
|
||||
// Clean should delete the manifest and the cache.
|
||||
Assert.FileDoesNotExist(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache");
|
||||
Assert.FileDoesNotExist(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference")]
|
||||
public async Task Rebuild_Success_RecreatesManifestAndCache()
|
||||
{
|
||||
// Arrange
|
||||
var result = await DotnetMSBuild("Build", "/restore");
|
||||
|
||||
var expectedManifest = GetExpectedManifest();
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
// GenerateStaticWebAssetsManifest should generate the manifest and the cache.
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache");
|
||||
|
||||
var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath);
|
||||
var thumbPrints = new Dictionary<string, FileThumbPrint>();
|
||||
var thumbPrintFiles = new[]
|
||||
{
|
||||
Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"),
|
||||
Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"),
|
||||
};
|
||||
|
||||
foreach (var file in thumbPrintFiles)
|
||||
{
|
||||
var thumbprint = GetThumbPrint(file);
|
||||
thumbPrints[file] = thumbprint;
|
||||
}
|
||||
|
||||
// Act
|
||||
var rebuild = await DotnetMSBuild("Rebuild");
|
||||
|
||||
// Assert
|
||||
Assert.BuildPassed(rebuild);
|
||||
|
||||
foreach (var file in thumbPrintFiles)
|
||||
{
|
||||
var thumbprint = GetThumbPrint(file);
|
||||
Assert.NotEqual(thumbPrints[file], thumbprint);
|
||||
}
|
||||
|
||||
var path = Assert.FileExists(result, OutputPath, "AppWithPackageAndP2PReference.dll");
|
||||
var assembly = Assert.ContainsEmbeddedResource(path, "Microsoft.AspNetCore.StaticWebAssets.xml");
|
||||
using (var reader = new StreamReader(assembly))
|
||||
{
|
||||
var data = reader.ReadToEnd();
|
||||
Assert.Equal(expectedManifest, data);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference")]
|
||||
public async Task GenerateStaticWebAssetsManifest_IncrementalBuild_ReusesManifest()
|
||||
{
|
||||
var result = await DotnetMSBuild("GenerateStaticWebAssetsManifest", "/restore");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
// GenerateStaticWebAssetsManifest should generate the manifest and the cache.
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
Assert.FileExists(result, IntermediateOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache");
|
||||
|
||||
var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath);
|
||||
var thumbPrints = new Dictionary<string, FileThumbPrint>();
|
||||
var thumbPrintFiles = new[]
|
||||
{
|
||||
Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml"),
|
||||
Path.Combine(directoryPath, "AppWithPackageAndP2PReference.StaticWebAssets.cache"),
|
||||
};
|
||||
|
||||
foreach (var file in thumbPrintFiles)
|
||||
{
|
||||
var thumbprint = GetThumbPrint(file);
|
||||
thumbPrints[file] = thumbprint;
|
||||
}
|
||||
|
||||
// Act
|
||||
var incremental = await DotnetMSBuild("GenerateStaticWebAssetsManifest");
|
||||
|
||||
// Assert
|
||||
Assert.BuildPassed(incremental);
|
||||
|
||||
foreach (var file in thumbPrintFiles)
|
||||
{
|
||||
var thumbprint = GetThumbPrint(file);
|
||||
Assert.Equal(thumbPrints[file], thumbprint);
|
||||
}
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
return PackageTestProjects.PackAsync(Output);
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private string GetExpectedManifest()
|
||||
{
|
||||
var restorePath = LocalNugetPackagesCacheTempPath;
|
||||
var projects = new[]
|
||||
{
|
||||
Path.Combine(restorePath, "packagelibrarytransitivedependency", "1.0.0", "buildTransitive", "..", "razorContent") + Path.DirectorySeparatorChar,
|
||||
Path.Combine(restorePath, "packagelibrarydirectdependency", "1.0.0", "build", "..", "razorContent") + Path.DirectorySeparatorChar
|
||||
};
|
||||
|
||||
return $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""_content/PackageLibraryTransitiveDependency"" Path=""{projects[0]}"" />
|
||||
<ContentRoot BasePath=""_content/PackageLibraryDirectDependency"" Path=""{projects[1]}"" />
|
||||
</StaticWebAssets>";
|
||||
}
|
||||
}
|
||||
|
||||
public class PackageTestProjectsFixture
|
||||
{
|
||||
private bool _packed;
|
||||
|
||||
internal async Task PackAsync(ITestOutputHelper output)
|
||||
{
|
||||
if (_packed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var projectsToPack = GetProjectsToPack();
|
||||
|
||||
foreach (var project in projectsToPack)
|
||||
{
|
||||
output.WriteLine(project);
|
||||
}
|
||||
|
||||
foreach (var project in projectsToPack)
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = DotNetMuxer.MuxerPathOrDefault(),
|
||||
#if DEBUG
|
||||
Arguments = "msbuild /t:Restore;Pack /p:Configuration=Debug",
|
||||
#else
|
||||
Arguments = "msbuild /t:Restore;Pack /p:Configuration=Release",
|
||||
#endif
|
||||
WorkingDirectory = project,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
|
||||
var result = await MSBuildProcessManager.RunProcessCoreAsync(
|
||||
psi,
|
||||
TimeSpan.FromMinutes(2));
|
||||
|
||||
output.WriteLine(result.Output);
|
||||
Assert.Equal(0, result.ExitCode);
|
||||
}
|
||||
|
||||
_packed = true;
|
||||
}
|
||||
|
||||
public static string[] GetProjectsToPack()
|
||||
{
|
||||
return typeof(PackageTestProjectsFixture).Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
|
||||
.Where(a => a.Key == "Testing.ProjectToPack")
|
||||
.Select(a => a.Value)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,11 +21,30 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(MicrosoftExtensionsDependencyModelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
|
||||
<_Parameter1>Testing.ProjectToPack</_Parameter1>
|
||||
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\PackageLibraryDirectDependency</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
|
||||
<_Parameter1>Testing.ProjectToPack</_Parameter1>
|
||||
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\PackageLibraryTransitiveDependency</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
|
||||
<_Parameter1>Testing.AdditionalRestoreSources</_Parameter1>
|
||||
<_Parameter2>$(MSBuildThisFileDirectory)..\testapps\TestPackageRestoreSource</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<!-- The test projects rely on these binaries being available -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourcePackageVersion)" />
|
||||
|
|
@ -51,11 +70,7 @@
|
|||
Inputs="$(MSBuildAllProjects)"
|
||||
Outputs="$(MSBuildLocationFileOutput)">
|
||||
|
||||
<Exec
|
||||
Condition="'$(OS)' == 'Windows_NT'"
|
||||
Command='"$(NuGetPackageRoot)vswhere\$(VSWhereVersion)\tools\vswhere.exe" -latest -prerelease -property installationPath -requires Microsoft.Component.MSBuild'
|
||||
ConsoleToMsBuild="true"
|
||||
StandardErrorImportance="high">
|
||||
<Exec Condition="'$(OS)' == 'Windows_NT'" Command=""$(NuGetPackageRoot)vswhere\$(VSWhereVersion)\tools\vswhere.exe" -latest -prerelease -property installationPath -requires Microsoft.Component.MSBuild" ConsoleToMsBuild="true" StandardErrorImportance="high">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="_VSInstallDir" />
|
||||
</Exec>
|
||||
<Error Condition="'$(OS)' == 'Windows_NT' and '$(_VSInstallDir)'==''" Text="Visual Studio not found on Windows." />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorSdkDirectoryRoot>$(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\</RazorSdkDirectoryRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<RuntimeAdditionalRestoreSources Condition="'$(RuntimeAdditionalRestoreSources)' == ''">$(MSBuildThisFileDirectory)..\TestPackageRestoreSource\</RuntimeAdditionalRestoreSources>
|
||||
<RestoreSources>
|
||||
$(RestoreSources);
|
||||
$(RuntimeAdditionalRestoreSources)
|
||||
</RestoreSources>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Avoid referencing the AspNetCore framework since we want the ability to target work-in-progress shims -->
|
||||
<FrameworkReference Remove="Microsoft.AspNetCore.App" />
|
||||
</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" />
|
||||
</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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorSdkDirectoryRoot>$(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\</RazorSdkDirectoryRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<Copyright>© Microsoft</Copyright>
|
||||
<Product>Razor Test</Product>
|
||||
<Company>Microsoft</Company>
|
||||
<Description>PackageLibraryDirectDependency Description</Description>
|
||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\TestPackageRestoreSource</PackageOutputPath>
|
||||
<DeterministicSourcePaths>false</DeterministicSourcePaths>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RunningAsTest)' == ''">
|
||||
<!-- We don't want to run build server when not running as tests. -->
|
||||
<UseRazorBuildServer>false</UseRazorBuildServer>
|
||||
</PropertyGroup>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Test Placeholder -->
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="build\**" Pack="true" PackagePath="build" />
|
||||
<!-- We will remove the line below when we do the item to support packing -->
|
||||
<Content Update="wwwroot\**" Pack="true" PackagePath="razorContent" />
|
||||
<ProjectReference Include="..\PackageLibraryTransitiveDependency\PackageLibraryTransitiveDependency.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<StaticWebAsset Include="$(MSBuildThisFileDirectory)..\razorContent\**">
|
||||
<SourceType>Package</SourceType>
|
||||
<SourceId>PackageLibraryDirectDependency</SourceId>
|
||||
<ContentRoot>$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)..\razorContent'))</ContentRoot>
|
||||
<BasePath>_content\PackageLibraryDirectDependency</BasePath>
|
||||
<RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
|
||||
</StaticWebAsset>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1 @@
|
|||
div.fluent { display: inline-block }
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(function () {
|
||||
document.getElementById('pkg-direct-dep').innerHTML = 'pkg-direct-dep';
|
||||
})()
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<RazorSdkDirectoryRoot>$(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\</RazorSdkDirectoryRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Copyright>© Microsoft</Copyright>
|
||||
<Product>Razor Test</Product>
|
||||
<Company>Microsoft</Company>
|
||||
<Description>PackageLibraryTransitiveDependency Description</Description>
|
||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\TestPackageRestoreSource</PackageOutputPath>
|
||||
<DeterministicSourcePaths>false</DeterministicSourcePaths>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(RunningAsTest)' == ''">
|
||||
<!-- We don't want to run build server when not running as tests. -->
|
||||
<UseRazorBuildServer>false</UseRazorBuildServer>
|
||||
</PropertyGroup>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- Test Placeholder -->
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="build\**" Pack="true" PackagePath="buildTransitive" />
|
||||
<!-- We will remove the line below when we do the item to support packing -->
|
||||
<Content Update="wwwroot\**" Pack="true" PackagePath="razorContent" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<StaticWebAsset Include="$(MSBuildThisFileDirectory)..\razorContent\**">
|
||||
<SourceType>Package</SourceType>
|
||||
<SourceId>PackageLibraryTransitiveDependency</SourceId>
|
||||
<ContentRoot>$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)..\razorContent'))</ContentRoot>
|
||||
<BasePath>_content\PackageLibraryTransitiveDependency</BasePath>
|
||||
<RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
|
||||
</StaticWebAsset>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
(function () {
|
||||
document.getElementById('pkg-transitive-dep').innerHTML = 'pkg-transitive-dep';
|
||||
})()
|
||||
|
|
@ -4,6 +4,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SimpleMvc11NetFx\SimpleMvc11NetFx.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc11\SimpleMvc11.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc21\SimpleMvc21.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc22\SimpleMvc22.csproj" />
|
||||
<ProjectReference Include="..\ClassLibraryMvc21\ClassLibraryMvc21.csproj" />
|
||||
|
||||
<ProjectReference Include="..\AppWithP2PReference\AppWithP2PReference.csproj" />
|
||||
<ProjectReference Include="..\ClassLibrary\ClassLibrary.csproj" />
|
||||
<ProjectReference Include="..\ClassLibrary2\ClassLibrary2.csproj" />
|
||||
|
|
@ -14,12 +20,15 @@
|
|||
<ProjectReference Include="..\MvcWithComponents\MvcWithComponents.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvcFSharp\SimpleMvcFSharp.fsproj" />
|
||||
<ProjectReference Include="..\SimpleMvc\SimpleMvc.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc11NetFx\SimpleMvc11NetFx.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc11\SimpleMvc11.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc21\SimpleMvc21.csproj" />
|
||||
<ProjectReference Include="..\SimpleMvc22\SimpleMvc22.csproj" />
|
||||
<ProjectReference Include="..\SimplePages\SimplePages.csproj" />
|
||||
|
||||
<!--
|
||||
We don't add AppWithPackageAndP2PReference, PackageLibraryDirectDependency or PackageLibraryTransitiveDependency here
|
||||
as PackageLibraryDirectDependency and PackageLibraryTransitiveDependency are test packages that
|
||||
need to be produced on the fly and AppWithPackageAndP2PReference depends on them so adding them here
|
||||
will make restore fail.
|
||||
-->
|
||||
|
||||
<ProjectReference Include="..\..\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.csproj"/>
|
||||
<ProjectReference Include="..\..\Microsoft.AspNetCore.Razor.Test.ComponentShim\Microsoft.AspNetCore.Razor.Test.ComponentShim.csproj"/>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in New Issue