зеркало из https://github.com/dotnet/razor.git
Do build time discovery of MVC ApplicationParts (#598)
* Do build time discovery of MVC ApplicationParts
This commit is contained in:
Родитель
17af3efccd
Коммит
e79d5600f7
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче