Do build time discovery of MVC ApplicationParts (#598)

* Do build time discovery of MVC ApplicationParts
This commit is contained in:
Pranav K 2019-05-17 13:14:41 -07:00 коммит произвёл GitHub
Родитель 17af3efccd
Коммит e79d5600f7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 659 добавлений и 7 удалений

Просмотреть файл

@ -21,7 +21,7 @@
</PropertyGroup>
<PropertyGroup Label="Build Settings">
<LangVersion>7.3</LangVersion>
<LangVersion>8.0</LangVersion>
<StrongNameKeyId>MicrosoftAspNetCore</StrongNameKeyId>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<DebugType>portable</DebugType>

Просмотреть файл

@ -21,6 +21,10 @@
<Uri>https://github.com/dotnet/corefx</Uri>
<Sha>a28176b5ec68b6da1472934fe9493790d1665cae</Sha>
</Dependency>
<Dependency Name="System.Reflection.Metadata" Version="1.7.0-preview6.19264.9" CoherentParentDependency="Microsoft.NETCore.App">
<Uri>https://github.com/dotnet/corefx</Uri>
<Sha>a28176b5ec68b6da1472934fe9493790d1665cae</Sha>
</Dependency>
<Dependency Name="System.Text.Encodings.Web" Version="4.6.0-preview6.19264.9" CoherentParentDependency="Microsoft.NETCore.App">
<Uri>https://github.com/dotnet/corefx</Uri>
<Sha>a28176b5ec68b6da1472934fe9493790d1665cae</Sha>

Просмотреть файл

@ -63,6 +63,7 @@
<MicrosoftNETCorePlatformsPackageVersion>3.0.0-preview6.19264.9</MicrosoftNETCorePlatformsPackageVersion>
<SystemDiagnosticsDiagnosticSourcePackageVersion>4.6.0-preview6.19264.9</SystemDiagnosticsDiagnosticSourcePackageVersion>
<SystemTextEncodingsWebPackageVersion>4.6.0-preview6.19264.9</SystemTextEncodingsWebPackageVersion>
<SystemReflectionMetadataPackageVersion>1.7.0-preview6.19264.9</SystemReflectionMetadataPackageVersion>
</PropertyGroup>
<!--

Просмотреть файл

@ -0,0 +1,14 @@
// 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.
namespace Microsoft.AspNetCore.Razor.Tasks
{
public class AssemblyItem
{
public string Path { get; set; }
public bool IsSystemReference { get; set; }
public string AssemblyName { get; set; }
}
}

Просмотреть файл

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Razor is a markup syntax for adding server-side logic to web pages. This package contains MSBuild support for Razor.</Description>
<TargetFrameworks>netcoreapp3.0;netstandard2.0;net46</TargetFrameworks>
<TargetFrameworks>netcoreapp3.0;net46</TargetFrameworks>
<TargetName>Microsoft.NET.Sdk.Razor.Tasks</TargetName>
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
@ -17,8 +17,9 @@
<PackageReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkPackageVersion)" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCorePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" Condition="'$(TargetFramework)'=='net46'" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" ReferenceOutputAssembly="false" Condition="'$(TargetFramework)'=='netstandard2.0'" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" ReferenceOutputAssembly="false" Condition="'$(TargetFramework)'=='netcoreapp3.0'" />
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj" ReferenceOutputAssembly="false" Condition="'$(TargetFramework)'=='netcoreapp3.0'" />
</ItemGroup>
@ -54,7 +55,10 @@
<ItemGroup>
<ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\net46*\Microsoft.NET.Sdk.Razor.Tasks.*" />
<ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\netstandard2.0*\Microsoft.NET.Sdk.Razor.Tasks.*" />
<ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\netcoreapp3.0*\Microsoft.NET.Sdk.Razor.Tasks.*" />
<ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\net46*\System.Collections.Immutable.dll" />
<ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\net46*\System.Reflection.Metadata.dll" />
</ItemGroup>
<Copy SourceFiles="@(ProjectOutput)" DestinationFiles="$(SdkOutputPath)tasks\%(RecursiveDir)%(FileName)%(Extension)" SkipUnchangedFiles="true">

Просмотреть файл

@ -0,0 +1,169 @@
// 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.Reflection.Metadata;
using System.Reflection.PortableExecutable;
namespace Microsoft.AspNetCore.Razor.Tasks
{
/// <summary>
/// Resolves assemblies that reference one of the specified "targetAssemblies" either directly or transitively.
/// </summary>
public class ReferenceResolver
{
private readonly HashSet<string> _mvcAssemblies;
private readonly Dictionary<string, ClassifiedAssemblyItem> _lookup = new Dictionary<string, ClassifiedAssemblyItem>(StringComparer.Ordinal);
public ReferenceResolver(IReadOnlyList<string> targetAssemblies, IReadOnlyList<AssemblyItem> assemblyItems)
{
_mvcAssemblies = new HashSet<string>(targetAssemblies, StringComparer.Ordinal);
foreach (var item in assemblyItems)
{
var classifiedItem = new ClassifiedAssemblyItem(item);
_lookup[item.AssemblyName] = classifiedItem;
}
}
public IReadOnlyList<string> ResolveAssemblies()
{
var applicationParts = new List<string>();
foreach (var item in _lookup)
{
var classification = Resolve(item.Value);
if (classification == DependencyClassification.ReferencesMvc)
{
applicationParts.Add(item.Key);
}
// It's not interesting for us to know if a dependency has a classification of MvcReference.
// All applications targeting the Microsoft.AspNetCore.App will have have a reference to Mvc.
}
return applicationParts;
}
private DependencyClassification Resolve(ClassifiedAssemblyItem classifiedItem)
{
if (classifiedItem.DependencyClassification != DependencyClassification.Unknown)
{
return classifiedItem.DependencyClassification;
}
if (classifiedItem.AssemblyItem == null)
{
// We encountered a dependency that isn't part of this assembly's dependency set. We'll see if it happens to be an MVC assembly.
// This might be useful in scenarios where the app does not have a framework reference at the entry point,
// but the transitive dependency does.
classifiedItem.DependencyClassification = _mvcAssemblies.Contains(classifiedItem.Name) ?
DependencyClassification.MvcReference :
DependencyClassification.DoesNotReferenceMvc;
return classifiedItem.DependencyClassification;
}
if (classifiedItem.AssemblyItem.IsSystemReference)
{
// We do not allow transitive references to MVC via a framework reference to count.
// e.g. depending on Microsoft.AspNetCore.SomeThingNewThatDependsOnMvc would not result in an assembly being treated as
// referencing MVC.
classifiedItem.DependencyClassification = _mvcAssemblies.Contains(classifiedItem.Name) ?
DependencyClassification.MvcReference :
DependencyClassification.DoesNotReferenceMvc;
return classifiedItem.DependencyClassification;
}
if (_mvcAssemblies.Contains(classifiedItem.Name))
{
classifiedItem.DependencyClassification = DependencyClassification.MvcReference;
return classifiedItem.DependencyClassification;
}
var dependencyClassification = DependencyClassification.DoesNotReferenceMvc;
foreach (var assemblyItem in GetReferences(classifiedItem.AssemblyItem.Path))
{
var classification = Resolve(assemblyItem);
if (classification == DependencyClassification.MvcReference || classification == DependencyClassification.ReferencesMvc)
{
dependencyClassification = DependencyClassification.ReferencesMvc;
break;
}
}
classifiedItem.DependencyClassification = dependencyClassification;
return dependencyClassification;
}
protected virtual IReadOnlyList<ClassifiedAssemblyItem> GetReferences(string file)
{
try
{
using var peReader = new PEReader(File.OpenRead(file));
if (!peReader.HasMetadata)
{
return Array.Empty<ClassifiedAssemblyItem>(); // not a managed assembly
}
var metadataReader = peReader.GetMetadataReader();
var assemblyItems = new List<ClassifiedAssemblyItem>();
foreach (var handle in metadataReader.AssemblyReferences)
{
var reference = metadataReader.GetAssemblyReference(handle);
var referenceName = metadataReader.GetString(reference.Name);
if (_lookup.TryGetValue(referenceName, out var classifiedItem))
{
assemblyItems.Add(classifiedItem);
}
else
{
// A dependency references an item that isn't referenced by this project.
// We'll construct an item for so that we can calculate the classification based on it's name.
assemblyItems.Add(new ClassifiedAssemblyItem(referenceName));
}
}
return assemblyItems;
}
catch (BadImageFormatException)
{
// not a PE file, or invalid metadata
}
return Array.Empty<ClassifiedAssemblyItem>(); // not a managed assembly
}
protected enum DependencyClassification
{
Unknown,
DoesNotReferenceMvc,
ReferencesMvc,
MvcReference,
}
protected class ClassifiedAssemblyItem
{
public ClassifiedAssemblyItem(AssemblyItem classifiedItem)
: this(classifiedItem.AssemblyName)
{
AssemblyItem = classifiedItem;
}
public ClassifiedAssemblyItem(string name)
{
Name = name;
}
public string Name { get; }
public AssemblyItem AssemblyItem { get; }
public DependencyClassification DependencyClassification { get; set; }
}
}
}

Просмотреть файл

@ -0,0 +1,55 @@
// 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.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.AspNetCore.Razor.Tasks
{
public class FindAssembliesWithReferencesTo : Task
{
[Required]
public ITaskItem[] TargetAssemblyNames { get; set; }
[Required]
public ITaskItem[] Assemblies { get; set; }
[Output]
public string[] ResolvedAssemblies { get; set; }
public override bool Execute()
{
var referenceItems = new List<AssemblyItem>();
foreach (var item in Assemblies)
{
const string FusionNameKey = "FusionName";
var fusionName = item.GetMetadata(FusionNameKey);
if (string.IsNullOrEmpty(fusionName))
{
Log.LogError($"Missing required metadata '{FusionNameKey}' for '{item.ItemSpec}.");
return false;
}
var assemblyName = new AssemblyName(fusionName).Name;
referenceItems.Add(new AssemblyItem
{
AssemblyName = assemblyName,
IsSystemReference = item.GetMetadata("IsSystemReference") == "true",
Path = item.ItemSpec,
});
}
var targetAssemblyNames = TargetAssemblyNames.Select(s => s.ItemSpec).ToList();
var provider = new ReferenceResolver(targetAssemblyNames, referenceItems);
var assemblyNames = provider.ResolveAssemblies();
ResolvedAssemblies = assemblyNames.ToArray();
return !Log.HasLoggedErrors;
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
<!--
***********************************************************************************************
Microsoft.NET.Sdk.Razor.ApplicationPartsDiscovery
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">
<UsingTask
TaskName="Microsoft.AspNetCore.Razor.Tasks.FindAssembliesWithReferencesTo"
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
<PropertyGroup>
<GenerateMvcApplicationPartsAttribute Condition="'$(GenerateMvcApplicationPartsAssemblyAttributes)' == '' AND '$(OutputType)' == 'Exe'">true</GenerateMvcApplicationPartsAttribute>
<CoreCompileDependsOn Condition="'$(GenerateMvcApplicationPartsAttribute)' == 'true' AND '$(DesignTimeBuild)' != 'true'">
_DiscoverMvcApplicationParts;
$(CoreCompileDependsOn);
</CoreCompileDependsOn>
<_MvcApplicationPartAttributeGeneratedFile>$(IntermediateOutputPath)$(TargetName).MvcApplicationPartsAssemblyInfo$(DefaultLanguageSourceExtension)</_MvcApplicationPartAttributeGeneratedFile>
</PropertyGroup>
<Target
Name="_DiscoverMvcApplicationParts"
Inputs="$(ProjectAssetsFile);$(MSBuildAllProjects)"
Outputs="$(_MvcApplicationPartAttributeGeneratedFile)"
DependsOnTargets="ResolveAssemblyReferences">
<ItemGroup>
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Abstractions" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.ApiExplorer" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Core" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Cors" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.DataAnnotations" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Formatters.Json" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Formatters.Xml" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Localization" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.Razor" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.RazorPages" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.TagHelpers" />
<_MvcAssemblyName Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
</ItemGroup>
<FindAssembliesWithReferencesTo Assemblies="@(ReferencePath)" TargetAssemblyNames="@(_MvcAssemblyName)">
<Output TaskParameter="ResolvedAssemblies" ItemName="_ApplicationPartAssemblyNames"/>
</FindAssembliesWithReferencesTo>
<ItemGroup>
<_MvcApplicationPartAttribute Include="Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute">
<_Parameter1>%(_ApplicationPartAssemblyNames.Identity)</_Parameter1>
</_MvcApplicationPartAttribute>
</ItemGroup>
<!-- If we found application part assemblies, generate attributes for it and add it to compilation list -->
<WriteCodeFragment
AssemblyAttributes="@(_MvcApplicationPartAttribute)"
Language="$(Language)"
OutputFile="$(_MvcApplicationPartAttributeGeneratedFile)"
Condition="'@(_ApplicationPartAssemblyNames->Count())' != '0'" />
<ItemGroup Condition="'@(_ApplicationPartAssemblyNames->Count())' != '0'">
<Compile Remove="$(_MvcApplicationPartAttributeGeneratedFile)" Condition="'$(Language)'!='F#'" />
<Compile Include="$(_MvcApplicationPartAttributeGeneratedFile)" Condition="'$(Language)'!='F#'" />
<CompileBefore Remove="$(_MvcApplicationPartAttributeGeneratedFile)" Condition="'$(Language)'=='F#'" />
<CompileBefore Include="$(_MvcApplicationPartAttributeGeneratedFile)" Condition="'$(Language)'=='F#'" />
</ItemGroup>
<!--
If we did not find any application parts, produce an empty file which is not added to compilation.
This is required to play nicely with incremental builds.
-->
<Touch
Files="$(_MvcApplicationPartAttributeGeneratedFile)"
AlwaysCreate="true" />
<ItemGroup>
<FileWrites Include="$(_MvcApplicationPartAttributeGeneratedFile)" />
</ItemGroup>
</Target>
</Project>

Просмотреть файл

@ -25,7 +25,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<!-- Paths to tools, tasks, and extensions are calculated relative to the RazorSdkDirectoryRoot. This can be modified to test a local build. -->
<RazorSdkDirectoryRoot Condition="'$(RazorSdkDirectoryRoot)'==''">$(MSBuildThisFileDirectory)..\..\</RazorSdkDirectoryRoot>
<RazorSdkBuildTasksDirectoryRoot Condition="'$(RazorSdkBuildTasksDirectoryRoot)'==''">$(RazorSdkDirectoryRoot)tasks\</RazorSdkBuildTasksDirectoryRoot>
<_RazorSdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">netstandard2.0</_RazorSdkTasksTFM>
<_RazorSdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.0</_RazorSdkTasksTFM>
<_RazorSdkTasksTFM Condition=" '$(_RazorSdkTasksTFM)' == ''">net46</_RazorSdkTasksTFM>
<RazorSdkBuildTasksAssembly>$(RazorSdkBuildTasksDirectoryRoot)$(_RazorSdkTasksTFM)\Microsoft.NET.Sdk.Razor.Tasks.dll</RazorSdkBuildTasksAssembly>
</PropertyGroup>
@ -335,6 +335,8 @@ Copyright (c) .NET Foundation. All rights reserved.
<Import Project="Microsoft.NET.Sdk.Razor.GenerateAssemblyInfo.targets" />
<Import Project="Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets" Condition="'$(_TargetingNETCoreApp30OrLater)' == 'true'" />
<!--
These are the targets that actually do compilation using CSC, separated from the main file for ease of maintenance.

Просмотреть файл

@ -0,0 +1,13 @@
// 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;
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ApplicationPartAttribute : Attribute
{
public ApplicationPartAttribute(string assemblyName) {}
}
}

Просмотреть файл

@ -0,0 +1,115 @@
// 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.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
{
public class ApplicationPartDiscoveryIntegrationTest : MSBuildIntegrationTestBase, IClassFixture<BuildServerTestFixture>
{
public ApplicationPartDiscoveryIntegrationTest(BuildServerTestFixture buildServer)
: base(buildServer)
{
}
[Fact]
[InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")]
public Task Build_ProjectWithDependencyThatReferencesMvc_AddsAttribute_WhenBuildingUsingDotnetMsbuild()
=> Build_ProjectWithDependencyThatReferencesMvc_AddsAttribute(MSBuildProcessKind.Dotnet);
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
[InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")]
public Task Build_ProjectWithDependencyThatReferencesMvc_AddsAttribute_WhenBuildingUsingDesktopMsbuild()
=> Build_ProjectWithDependencyThatReferencesMvc_AddsAttribute(MSBuildProcessKind.Desktop);
private async Task Build_ProjectWithDependencyThatReferencesMvc_AddsAttribute(MSBuildProcessKind msBuildProcessKind)
{
var result = await DotnetMSBuild("Build", msBuildProcessKind: msBuildProcessKind);
Assert.BuildPassed(result);
Assert.FileExists(result, IntermediateOutputPath, "AppWithP2PReference.MvcApplicationPartsAssemblyInfo.cs");
Assert.FileContains(result, Path.Combine(IntermediateOutputPath, "AppWithP2PReference.MvcApplicationPartsAssemblyInfo.cs"), "[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute(\"ClassLibrary\")]");
}
[Fact]
[InitializeTestProject("SimpleMvc")]
public async Task Build_ProjectWithoutMvcReferencingDependencies_DoesNotGenerateAttribute()
{
var result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.MvcApplicationPartsAssemblyInfo.cs");
// We should produced an empty file for build incrementalism
Assert.Empty(File.ReadAllText(Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath, "SimpleMvc.MvcApplicationPartsAssemblyInfo.cs")));
}
[Fact]
[InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")]
public async Task BuildIncrementalism_WhenApplicationPartAttributeIsGenerated()
{
var result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
var generatedAttributeFile = Path.Combine(IntermediateOutputPath, "AppWithP2PReference.MvcApplicationPartsAssemblyInfo.cs");
Assert.FileExists(result, generatedAttributeFile);
Assert.FileContains(result, generatedAttributeFile, "[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute(\"ClassLibrary\")]");
var thumbPrint = GetThumbPrint(generatedAttributeFile);
result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
Assert.FileExists(result, generatedAttributeFile);
Assert.Equal(thumbPrint, GetThumbPrint(generatedAttributeFile));
}
[Fact]
[InitializeTestProject("SimpleMvcFSharp", language: "F#", additionalProjects: "ClassLibrary")]
public async Task Build_ProjectWithDependencyThatReferencesMvc_AddsAttributeToNonCSharpProjects()
{
AddProjectFileContent(
@"
<ItemGroup>
<ProjectReference Include=""..\ClassLibrary\ClassLibrary.csproj"" />
</ItemGroup>
");
var result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
Assert.FileExists(result, IntermediateOutputPath, "SimpleMvcFSharp.MvcApplicationPartsAssemblyInfo.fs");
Assert.FileContains(result, Path.Combine(IntermediateOutputPath, "SimpleMvcFSharp.MvcApplicationPartsAssemblyInfo.fs"), "<assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute(\"ClassLibrary\")>");
}
[Fact]
[InitializeTestProject("SimpleMvc")]
public async Task BuildIncrementalism_WhenApplicationPartAttributeIsNotGenerated()
{
var result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
var generatedAttributeFile = Path.Combine(IntermediateOutputPath, "SimpleMvc.MvcApplicationPartsAssemblyInfo.cs");
Assert.FileExists(result, generatedAttributeFile);
Assert.Empty(File.ReadAllText(Path.Combine(result.Project.DirectoryPath, generatedAttributeFile)));
var thumbPrint = GetThumbPrint(generatedAttributeFile);
result = await DotnetMSBuild("Build");
Assert.BuildPassed(result);
Assert.FileExists(result, generatedAttributeFile);
Assert.Equal(thumbPrint, GetThumbPrint(generatedAttributeFile));
}
}
}

Просмотреть файл

@ -34,9 +34,9 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Tools\Microsoft.AspNetCore.Razor.Tools.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.csproj" />
<ProjectReference Include="..\..\src\Microsoft.NET.Sdk.Razor\Microsoft.NET.Sdk.Razor.csproj" />
<!-- We don't need anything in this assembly, we just want to make sure it's built -->
<ProjectReference Include="..\..\src\Microsoft.NET.Sdk.Razor\Microsoft.NET.Sdk.Razor.csproj" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Razor.Extensions\Microsoft.AspNetCore.Mvc.Razor.Extensions.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,181 @@
// 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.Linq;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Tasks
{
public class ReferenceResolverTest
{
internal static readonly string[] MvcAssemblies = new[]
{
"Microsoft.AspNetCore.Mvc",
"Microsoft.AspNetCore.Mvc.Abstractions",
"Microsoft.AspNetCore.Mvc.ApiExplorer",
"Microsoft.AspNetCore.Mvc.Core",
"Microsoft.AspNetCore.Mvc.Cors",
"Microsoft.AspNetCore.Mvc.DataAnnotations",
"Microsoft.AspNetCore.Mvc.Formatters.Json",
"Microsoft.AspNetCore.Mvc.Formatters.Xml",
"Microsoft.AspNetCore.Mvc.Localization",
"Microsoft.AspNetCore.Mvc.NewtonsoftJson",
"Microsoft.AspNetCore.Mvc.Razor",
"Microsoft.AspNetCore.Mvc.RazorPages",
"Microsoft.AspNetCore.Mvc.TagHelpers",
"Microsoft.AspNetCore.Mvc.ViewFeatures",
};
[Fact]
public void Resolve_ReturnsEmptySequence_IfNoAssemblyReferencesMvc()
{
// Arrange
var resolver = new TestReferencesToMvcResolver(new[]
{
CreateAssemblyItem("Microsoft.AspNetCore.Blazor"),
CreateAssemblyItem("Microsoft.AspNetCore.Components"),
CreateAssemblyItem("Microsoft.JSInterop"),
CreateAssemblyItem("System.Net.Http"),
CreateAssemblyItem("System.Runtime"),
});
resolver.Add("Microsoft.AspNetCore.Blazor", "Microsoft.AspNetCore.Components", "Microsoft.JSInterop");
resolver.Add("Microsoft.AspNetCore.Components", "Microsoft.JSInterop", "System.Net.Http", "System.Runtime");
resolver.Add("System.Net.Http", "System.Runtime");
// Act
var assemblies = resolver.ResolveAssemblies();
// Assert
Assert.Empty(assemblies);
}
[Fact]
public void Resolve_ReturnsEmptySequence_IfNoDependencyReferencesMvc()
{
// Arrange
var resolver = new TestReferencesToMvcResolver(new[]
{
CreateAssemblyItem("MyApp.Models"),
CreateAssemblyItem("Microsoft.AspNetCore.Mvc", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.Hosting", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.HttpAbstractions", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.KestrelHttpServer", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.StaticFiles", isSystemReference: true),
CreateAssemblyItem("Microsoft.Extensions.Primitives", isSystemReference: true),
CreateAssemblyItem("System.Net.Http", isSystemReference: true),
CreateAssemblyItem("Microsoft.EntityFrameworkCore"),
});
resolver.Add("MyApp.Models", "Microsoft.EntityFrameworkCore");
resolver.Add("Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.HttpAbstractions");
resolver.Add("Microsoft.AspNetCore.KestrelHttpServer", "Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.HttpAbstractions");
resolver.Add("Microsoft.AspNetCore.StaticFiles", "Microsoft.AspNetCore.HttpAbstractions", "Microsoft.Extensions.Primitives");
resolver.Add("Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.HttpAbstractions");
resolver.Add("Microsoft.AspNetCore.HttpAbstractions", "Microsoft.Extensions.Primitives");
// Act
var assemblies = resolver.ResolveAssemblies();
// Assert
Assert.Empty(assemblies);
}
[Fact]
public void Resolve_ReturnsReferences_ThatReferenceMvc()
{
// Arrange
var resolver = new TestReferencesToMvcResolver(new[]
{
CreateAssemblyItem("Microsoft.AspNetCore.Mvc", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.Mvc.TagHelpers", isSystemReference: true),
CreateAssemblyItem("MyTagHelpers"),
CreateAssemblyItem("MyControllers"),
CreateAssemblyItem("MyApp.Models"),
CreateAssemblyItem("Microsoft.AspNetCore.Hosting", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.HttpAbstractions", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.KestrelHttpServer", isSystemReference: true),
CreateAssemblyItem("Microsoft.AspNetCore.StaticFiles", isSystemReference: true),
CreateAssemblyItem("Microsoft.Extensions.Primitives", isSystemReference: true),
CreateAssemblyItem("Microsoft.EntityFrameworkCore"),
});
resolver.Add("MyTagHelpers", "Microsoft.AspNetCore.Mvc.TagHelpers");
resolver.Add("MyControllers", "Microsoft.AspNetCore.Mvc");
resolver.Add("MyApp.Models", "Microsoft.EntityFrameworkCore");
resolver.Add("Microsoft.AspNetCore.Mvc", "Microsoft.AspNetCore.HttpAbstractions", "Microsoft.AspNetCore.Mvc.TagHelpers");
resolver.Add("Microsoft.AspNetCore.KestrelHttpServer", "Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.HttpAbstractions");
resolver.Add("Microsoft.AspNetCore.StaticFiles", "Microsoft.AspNetCore.HttpAbstractions", "Microsoft.Extensions.Primitives");
resolver.Add("Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.HttpAbstractions");
resolver.Add("Microsoft.AspNetCore.HttpAbstractions", "Microsoft.Extensions.Primitives");
// Act
var assemblies = resolver.ResolveAssemblies();
// Assert
Assert.Equal(new[] { "MyControllers", "MyTagHelpers" }, assemblies.OrderBy(a => a));
}
[Fact]
public void Resolve_ReturnsItemsThatTransitivelyReferenceMvc()
{
// Arrange
var resolver = new TestReferencesToMvcResolver(new[]
{
CreateAssemblyItem("MyCMS"),
CreateAssemblyItem("MyCMS.Core"),
CreateAssemblyItem("Microsoft.AspNetCore.Mvc.ViewFeatures", isSystemReference: true),
});
resolver.Add("MyCMS", "MyCMS.Core");
resolver.Add("MyCMS.Core", "Microsoft.AspNetCore.Mvc.ViewFeatures");
// Act
var assemblies = resolver.ResolveAssemblies();
// Assert
Assert.Equal(new[] { "MyCMS", "MyCMS.Core" }, assemblies.OrderBy(a => a));
}
public AssemblyItem CreateAssemblyItem(string name, bool isSystemReference = false)
{
return new AssemblyItem
{
AssemblyName = name,
IsSystemReference = isSystemReference,
Path = name,
};
}
private class TestReferencesToMvcResolver : ReferenceResolver
{
private readonly Dictionary<string, List<ClassifiedAssemblyItem>> _references = new Dictionary<string, List<ClassifiedAssemblyItem>>();
private readonly Dictionary<string, ClassifiedAssemblyItem> _lookup;
public TestReferencesToMvcResolver(AssemblyItem[] referenceItems)
: base(MvcAssemblies, referenceItems)
{
_lookup = referenceItems.ToDictionary(r => r.AssemblyName, r => new ClassifiedAssemblyItem(r));
}
public void Add(string assembly, params string[] references)
{
var assemblyItems = references.Select(r => _lookup[r]).ToList();
_references[assembly] = assemblyItems;
}
protected override IReadOnlyList<ClassifiedAssemblyItem> GetReferences(string file)
{
if (_references.TryGetValue(file, out var result))
{
return result;
}
return Array.Empty<ClassifiedAssemblyItem>();
}
}
}
}

Просмотреть файл

@ -23,6 +23,11 @@
<UseRazorBuildServer>false</UseRazorBuildServer>
</PropertyGroup>
<ItemGroup>
<!-- Have the SDK treat the MvcShim as an MVC assembly -->
<_MvcAssemblyName Include="Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib" />
</ItemGroup>
<Import Project="After.Directory.Build.props" Condition="Exists('After.Directory.Build.props')" />
</Project>

Просмотреть файл

@ -10,7 +10,6 @@
-->
<PropertyGroup>
<DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
<_ReferencesAspNetCoreApp>true</_ReferencesAspNetCoreApp>
</PropertyGroup>
<ItemGroup>