diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets index 43a1fbc8db..5f1182be43 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets @@ -67,7 +67,16 @@ Copyright (c) .NET Foundation. All rights reserved. OutputFile="$(_MvcApplicationPartAttributeGeneratedFile)" Condition="'@(_ApplicationPartAssemblyNames->Count())' != '0'" /> - + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/ApplicationPartDiscoveryIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/ApplicationPartDiscoveryIntegrationTest.cs index a3e9e59160..d0eb4978c6 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/ApplicationPartDiscoveryIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/ApplicationPartDiscoveryIntegrationTest.cs @@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.FileExists(result, IntermediateOutputPath, "AppWithP2PReference.MvcApplicationPartsAssemblyInfo.cs"); Assert.FileContains(result, Path.Combine(IntermediateOutputPath, "AppWithP2PReference.MvcApplicationPartsAssemblyInfo.cs"), "[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute(\"ClassLibrary\")]"); + Assert.AssemblyHasAttribute(result, Path.Combine(OutputPath, "AppWithP2PReference.dll"), "Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute"); } [Fact] @@ -69,6 +70,33 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.FileExists(result, generatedAttributeFile); Assert.Equal(thumbPrint, GetThumbPrint(generatedAttributeFile)); + Assert.AssemblyHasAttribute(result, Path.Combine(OutputPath, "AppWithP2PReference.dll"), "Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute"); + } + + // Regression test for https://github.com/aspnet/AspNetCore/issues/11315 + [Fact] + [InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")] + public async Task BuildIncrementalism_CausingRecompilation_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); + + // Touch a file in the main app which should call recompilation, but not the Mvc discovery tasks to re-run. + File.AppendAllText(Path.Combine(Project.DirectoryPath, "Program.cs"), " "); + result = await DotnetMSBuild("Build"); + + Assert.BuildPassed(result); + + Assert.FileExists(result, generatedAttributeFile); + Assert.Equal(thumbPrint, GetThumbPrint(generatedAttributeFile)); + Assert.AssemblyHasAttribute(result, Path.Combine(OutputPath, "AppWithP2PReference.dll"), "Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute"); } [Fact] @@ -88,6 +116,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests Assert.FileExists(result, IntermediateOutputPath, "SimpleMvcFSharp.MvcApplicationPartsAssemblyInfo.fs"); Assert.FileContains(result, Path.Combine(IntermediateOutputPath, "SimpleMvcFSharp.MvcApplicationPartsAssemblyInfo.fs"), ""); + Assert.AssemblyHasAttribute(result, Path.Combine(OutputPath, "SimpleMvcFSharp.dll"), "Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute"); } [Fact] diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs index 9f4b6bfe30..23ab8172e8 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/test/IntegrationTests/Assert.cs @@ -507,6 +507,36 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } + public static void AssemblyHasAttribute(MSBuildResult result, string assemblyPath, string fullTypeName) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); + + var typeNames = GetAssemblyAttributes(assemblyPath); + Assert.Contains(fullTypeName, typeNames); + } + + private static IEnumerable GetAssemblyAttributes(string assemblyPath) + { + using (var file = File.OpenRead(assemblyPath)) + { + var peReader = new PEReader(file); + var metadataReader = peReader.GetMetadataReader(); + return metadataReader.CustomAttributes.Where(t => !t.IsNil).Select(t => + { + var attribute = metadataReader.GetCustomAttribute(t); + var constructor = metadataReader.GetMemberReference((MemberReferenceHandle)attribute.Constructor); + var type = metadataReader.GetTypeReference((TypeReferenceHandle)constructor.Parent); + + return metadataReader.GetString(type.Namespace) + "." + metadataReader.GetString(type.Name); + }).ToArray(); + } + } + private abstract class MSBuildXunitException : Xunit.Sdk.XunitException { protected MSBuildXunitException(MSBuildResult result)