From 0874ea601af3e42404dfe874d2fbc9998fe48761 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 28 Oct 2020 17:05:43 -0700 Subject: [PATCH] Use source generators to compile .razor files --- AspNetCore.sln | 15 + eng/ProjectReferences.props | 1 + eng/Versions.props | 8 +- eng/Workarounds.targets | 7 - .../Microsoft.AspNetCore.Identity.UI.csproj | 103 +----- ...iewComponentTagHelperDescriptorProvider.cs | 19 +- .../CodeGeneration/DefaultDocumentWriter.cs | 14 +- ...efaultRazorCodeGenerationOptionsBuilder.cs | 5 +- .../src/DefaultRazorDiagnostic.cs | 2 +- .../src/Properties/AssemblyInfo.cs | 1 + .../src/PublicAPI.Unshipped.txt | 7 +- .../src/RazorCodeGenerationOptions.cs | 6 + .../src/RazorCodeGenerationOptionsBuilder.cs | 5 + .../src/RazorDiagnostic.cs | 2 +- .../src/RazorProjectItem.cs | 6 +- .../src/RazorSourceDocument.cs | 7 +- .../src/TagHelperDescriptorProviderContext.cs | 4 +- .../src/TagHelperDiscoveryMode.cs | 15 + .../src/BindTagHelperDescriptorProvider.cs | 5 + .../ComponentTagHelperDescriptorProvider.cs | 20 +- .../src/DefaultTagHelperDescriptorProvider.cs | 19 +- ...EventHandlerTagHelperDescriptorProvider.cs | 24 +- .../src/KeyTagHelperDescriptorProvider.cs | 5 + .../src/Properties/AssemblyInfo.cs | 1 + .../src/RefTagHelperDescriptorProvider.cs | 5 + .../src/SplatTagHelperDescriptorProvider.cs | 5 + .../BuildIncrementalismTest.cs | 146 +-------- .../integrationtests/BuildIntegrationTest.cs | 84 +++-- .../BuildIntrospectionTest.cs | 27 ++ .../integrationtests/BuildPerformanceTest.cs | 110 ------- .../BuildServerIntegrationTest.cs | 274 ---------------- .../DesignTimeBuildIntegrationTest.cs | 2 +- .../InitializeTestProjectAttribute.cs | 2 +- .../MSBuildIntegrationTestBase.cs | 6 +- .../PublishIntegrationTest.cs | 4 +- .../RazorCompileIntegrationTest.cs | 115 ------- .../RazorGenerateIntegrationTest.cs | 119 ++----- .../ScopedCssIntegrationTests.cs | 10 +- .../src/Microsoft.NET.Sdk.Razor.csproj | 16 +- ...osoft.NET.Sdk.Razor.CodeGeneration.targets | 8 - ...icrosoft.NET.Sdk.Razor.Compilation.targets | 11 +- ...Microsoft.NET.Sdk.Razor.DesignTime.targets | 9 +- ...oft.NET.Sdk.Razor.SourceGenerators.targets | 70 ++++ .../Sdk.Razor.CurrentVersion.targets | 51 +-- src/Razor/Razor.slnf | 1 + .../ConfigureRazorCodeGenerationOptions.cs | 22 ++ .../SourceGenerator/src/RazorDiagnostics.cs | 48 +++ .../SourceGenerator/src/RazorInputItem.cs | 37 +++ .../src/RazorSourceGenerationContext.cs | 183 +++++++++++ .../src/RazorSourceGenerator.cs | 300 ++++++++++++++++++ .../src/RazorSourceGenerators.csproj | 34 ++ .../src/SourceGeneratorProjectItem.cs | 46 +++ .../src/SourceTextRazorSourceDocument.cs | 70 ++++ .../src/StaticCompilationTagHelperFeature.cs | 55 ++++ .../src/StaticTagHelperFeature.cs | 17 + .../src/TagHelperSerializer.cs | 40 +++ .../{#FileName.cshtml => $FileName.cshtml} | 0 src/Shared/MSBuild.Testing/Assert.cs | 17 +- 58 files changed, 1277 insertions(+), 968 deletions(-) create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDiscoveryMode.cs delete mode 100644 src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildPerformanceTest.cs delete mode 100644 src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildServerIntegrationTest.cs delete mode 100644 src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/RazorCompileIntegrationTest.cs create mode 100644 src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.SourceGenerators.targets create mode 100644 src/Razor/SourceGenerator/src/ConfigureRazorCodeGenerationOptions.cs create mode 100644 src/Razor/SourceGenerator/src/RazorDiagnostics.cs create mode 100644 src/Razor/SourceGenerator/src/RazorInputItem.cs create mode 100644 src/Razor/SourceGenerator/src/RazorSourceGenerationContext.cs create mode 100644 src/Razor/SourceGenerator/src/RazorSourceGenerator.cs create mode 100644 src/Razor/SourceGenerator/src/RazorSourceGenerators.csproj create mode 100644 src/Razor/SourceGenerator/src/SourceGeneratorProjectItem.cs create mode 100644 src/Razor/SourceGenerator/src/SourceTextRazorSourceDocument.cs create mode 100644 src/Razor/SourceGenerator/src/StaticCompilationTagHelperFeature.cs create mode 100644 src/Razor/SourceGenerator/src/StaticTagHelperFeature.cs create mode 100644 src/Razor/SourceGenerator/src/TagHelperSerializer.cs rename src/Razor/test/testassets/MvcWithComponents/Views/Home/{#FileName.cshtml => $FileName.cshtml} (100%) diff --git a/AspNetCore.sln b/AspNetCore.sln index 1954cda59cd9..e27f44a76e56 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1547,6 +1547,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Signal EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{BB3D6EDD-AE71-4D25-B61B-7EBF7A1BA1D1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RazorSourceGenerators", "src\Razor\SourceGenerator\src\RazorSourceGenerators.csproj", "{1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Analyzer.Testing", "src\Analyzers\Microsoft.AspNetCore.Analyzer.Testing\src\Microsoft.AspNetCore.Analyzer.Testing.csproj", "{399EF81E-C3B5-4D86-8BF1-DC7926252A63}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Caching", "Caching", "{0F39820F-F4A5-41C6-9809-D79B68F032EF}" @@ -7447,6 +7449,18 @@ Global {399EF81E-C3B5-4D86-8BF1-DC7926252A63}.Release|x64.Build.0 = Release|Any CPU {399EF81E-C3B5-4D86-8BF1-DC7926252A63}.Release|x86.ActiveCfg = Release|Any CPU {399EF81E-C3B5-4D86-8BF1-DC7926252A63}.Release|x86.Build.0 = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|x64.ActiveCfg = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|x64.Build.0 = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|x86.ActiveCfg = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Debug|x86.Build.0 = Debug|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|Any CPU.Build.0 = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|x64.ActiveCfg = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|x64.Build.0 = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|x86.ActiveCfg = Release|Any CPU + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36}.Release|x86.Build.0 = Release|Any CPU {4FB95E16-918B-49C1-9F65-49D07CDE072C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FB95E16-918B-49C1-9F65-49D07CDE072C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FB95E16-918B-49C1-9F65-49D07CDE072C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -8305,6 +8319,7 @@ Global {C1CDD339-B51B-42BE-99F2-F39A4EC0D404} = {BB3D6EDD-AE71-4D25-B61B-7EBF7A1BA1D1} {BB3D6EDD-AE71-4D25-B61B-7EBF7A1BA1D1} = {1A304CA0-7795-4684-88E5-E66402966927} {399EF81E-C3B5-4D86-8BF1-DC7926252A63} = {05A169C7-4F20-4516-B10A-B13C5649D346} + {1822B2E7-2B15-44DA-BD6C-CF3DF66F3E36} = {DA9E1AB0-0094-4777-BF3F-BC5596C3CDA9} {0F39820F-F4A5-41C6-9809-D79B68F032EF} = {017429CC-C5FB-48B4-9C46-034E29EE2F06} {8DFA596B-BD00-404E-9445-BCFAC65BDC34} = {017429CC-C5FB-48B4-9C46-034E29EE2F06} {4FB95E16-918B-49C1-9F65-49D07CDE072C} = {8DFA596B-BD00-404E-9445-BCFAC65BDC34} diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 5ad1aa1d9ca5..97c2cd8d811c 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -106,6 +106,7 @@ + diff --git a/eng/Versions.props b/eng/Versions.props index 9a94921908b6..16f1f8c96b52 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -191,7 +191,7 @@ - 3.8.0-5.20519.18 + 3.9.0-2.20521.12 5.0.0-preview.4.20180.4 @@ -220,9 +220,9 @@ 15.8.166 1.2.6 15.8.166 - 3.7.0 - 3.7.0 - 3.7.0 + 3.8.0 + 3.8.0 + 3.8.0 3.3.0 3.0.0 1.0.0-20200708.1 diff --git a/eng/Workarounds.targets b/eng/Workarounds.targets index ce40da6d759a..ed58aca9cd17 100644 --- a/eng/Workarounds.targets +++ b/eng/Workarounds.targets @@ -21,13 +21,6 @@ '$(GenerateRazorAssemblyInfo)' == 'true'" /> - - - - diff --git a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj index 0512647672c8..ceebc2861397 100644 --- a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj +++ b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj @@ -7,11 +7,9 @@ true aspnetcore;identity;membership;razorpages Microsoft.AspNetCore.Mvc.ApplicationParts.NullApplicationPartFactory, Microsoft.AspNetCore.Mvc.Core - false - false false - true true + Microsoft.AspNetCore.Identity.UI.Views.V4 true true @@ -21,9 +19,7 @@ _UpdatedIdentityUIStaticWebAssets - Bootstrap4 $(MSBuildThisFileDirectory)THIRD-PARTY-NOTICES.TXT - @@ -47,105 +43,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Microsoft.AspNetCore.Identity.UI.Views.$(UIFrameworkVersion) - - - + - <_RazorGenerate Include="Areas\Identity\Pages\$(UIFrameworkVersion)\**\*.cshtml" /> + <_RazorGenerate Include="Areas\Identity\Pages\V4\**\*.cshtml" /> - - - - - - <_GeneratedRazorViews Include="$(TargetDir)$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).dll" /> - <_GeneratedRazorViews Include="$(TargetDir)$(TargetName).Views.%(UIFrameworkVersionMoniker.Identity).pdb" /> - - - %(FileName)%(Extension) - PreserveNewest - - - - - - - - - - - - - - - - - - - - - - - - - @@ -153,7 +58,7 @@ <_V4Content Include="wwwroot\V4\**" /> - + Microsoft.AspNetCore.Identity.UI $([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)wwwroot/V4')) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/ViewComponentTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/ViewComponentTagHelperDescriptorProvider.cs index d0d9805f6b4b..f9360795e179 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/ViewComponentTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/ViewComponentTagHelperDescriptorProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -38,16 +38,21 @@ public void Execute(TagHelperDescriptorProviderContext context) var types = new List(); var visitor = new ViewComponentTypeVisitor(vcAttribute, nonVCAttribute, types); - // We always visit the global namespace. - visitor.Visit(compilation.Assembly.GlobalNamespace); + if ((context.DiscoveryMode & TagHelperDiscoveryMode.CurrentAssembly) == TagHelperDiscoveryMode.CurrentAssembly) + { + visitor.Visit(compilation.Assembly.GlobalNamespace); + } - foreach (var reference in compilation.References) + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) == TagHelperDiscoveryMode.References) { - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + foreach (var reference in compilation.References) { - if (IsTagHelperAssembly(assembly)) + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { - visitor.Visit(assembly.GlobalNamespace); + if (IsTagHelperAssembly(assembly)) + { + visitor.Visit(assembly.GlobalNamespace); + } } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs index c5fb4de90e25..058112def09c 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -124,6 +124,18 @@ public override void VisitDocument(DocumentIntermediateNode node) .WriteLine("// ") .WriteLine("#pragma warning disable 1591"); + if (Context.Options.GenerateDesignerIfDefs) + { + if (Context.Options.DesignTime) + { + Context.CodeWriter.WriteLine("#ifdef RAZOR_SOURCEGENERATED_DESIGNTIME"); + } + else + { + Context.CodeWriter.WriteLine("#ifdef RAZOR_SOURCEGENERATED_RUNTIME"); + } + } + VisitDefault(node); Context.CodeWriter.WriteLine("#pragma warning restore 1591"); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs index 1422b410ff41..d57925133f54 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorCodeGenerationOptionsBuilder.cs @@ -52,7 +52,10 @@ public override RazorCodeGenerationOptions Build() SuppressMetadataAttributes, SuppressPrimaryMethodBody, SuppressNullabilityEnforcement, - OmitMinimizedComponentAttributeValues); + OmitMinimizedComponentAttributeValues) + { + GenerateDesignerIfDefs = GenerateDesignerIfDefs, + }; } public override void SetDesignTime(bool designTime) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorDiagnostic.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorDiagnostic.cs index 40f558df2679..6e1d2be21490 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorDiagnostic.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorDiagnostic.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Properties/AssemblyInfo.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Properties/AssemblyInfo.cs index feb088c7424d..eb475eb879fa 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Properties/AssemblyInfo.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Properties/AssemblyInfo.cs @@ -5,6 +5,7 @@ [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("rzc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("RazorSourceGenerators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.GenerateTool, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..65856dbb1ab4 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/PublicAPI.Unshipped.txt @@ -1 +1,6 @@ -#nullable enable +Microsoft.AspNetCore.Razor.Language.TagHelperDescriptorProviderContext.DiscoveryMode.get -> Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode +Microsoft.AspNetCore.Razor.Language.TagHelperDescriptorProviderContext.DiscoveryMode.set -> void +Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode +Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode.All = Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode.CurrentAssembly | Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode.References -> Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode +Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode.CurrentAssembly = 1 -> Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode +Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode.References = 2 -> Microsoft.AspNetCore.Razor.Language.TagHelperDiscoveryMode diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs index b481dfeb07a6..3323e17912e9 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptions.cs @@ -121,5 +121,11 @@ public static RazorCodeGenerationOptions CreateDesignTime(Action public virtual bool OmitMinimizedComponentAttributeValues { get; } + + /// + /// Gets or sets a value that determines if special ifdefs are added to the generated code to support + /// features such as Edit & Continue in the IDE. + /// + internal bool GenerateDesignerIfDefs { get; set; } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs index 48d725b6145f..fdec37f0ac78 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorCodeGenerationOptionsBuilder.cs @@ -64,6 +64,11 @@ public abstract class RazorCodeGenerationOptionsBuilder /// public virtual bool OmitMinimizedComponentAttributeValues { get; set; } + /// + /// . + /// + internal bool GenerateDesignerIfDefs { get; set; } + public abstract RazorCodeGenerationOptions Build(); public virtual void SetDesignTime(bool designTime) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnostic.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnostic.cs index a4bd3c2b5e49..a33257cdf501 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnostic.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnostic.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectItem.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectItem.cs index 6c245de89398..e0f0709d7dca 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectItem.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorProjectItem.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Diagnostics; @@ -130,9 +130,11 @@ public string FilePathWithoutExtension } } + internal RazorSourceDocument RazorSourceDocument { get; set; } + private string DebuggerToString() { return CombinedPath; } } -} \ No newline at end of file +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorSourceDocument.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorSourceDocument.cs index 31acb7dfd69c..6362f1708f08 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorSourceDocument.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorSourceDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -193,6 +193,11 @@ public static RazorSourceDocument ReadFrom(RazorProjectItem projectItem) filePath = projectItem.FilePath; } + if (projectItem.RazorSourceDocument is not null) + { + return projectItem.RazorSourceDocument; + } + using (var stream = projectItem.Read()) { // Autodetect the encoding. diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDescriptorProviderContext.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDescriptorProviderContext.cs index 4bf792c62da6..814bd04f969c 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDescriptorProviderContext.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDescriptorProviderContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -16,6 +16,8 @@ public abstract class TagHelperDescriptorProviderContext public abstract ICollection Results { get; } + public TagHelperDiscoveryMode DiscoveryMode { get; set; } = TagHelperDiscoveryMode.All; + public static TagHelperDescriptorProviderContext Create() { return new DefaultContext(new List()); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDiscoveryMode.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDiscoveryMode.cs new file mode 100644 index 000000000000..0a20a59fb2f6 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/TagHelperDiscoveryMode.cs @@ -0,0 +1,15 @@ +// 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.Razor.Language +{ + [Flags] + public enum TagHelperDiscoveryMode + { + CurrentAssembly = 1, + References = 2, + All = CurrentAssembly | References, + } +} diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs index 22eeb425b070..4488255e657f 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs @@ -101,6 +101,11 @@ public void Execute(TagHelperDescriptorProviderContext context) return; } + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) != TagHelperDiscoveryMode.References) + { + return; + } + // Tag Helper defintion for case #1. This is the most general case. context.Results.Add(CreateFallbackBindTagHelper()); diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs index 8c7a8ddd2dac..181306d50885 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/ComponentTagHelperDescriptorProvider.cs @@ -40,15 +40,21 @@ public void Execute(TagHelperDescriptorProviderContext context) var types = new List(); var visitor = new ComponentTypeVisitor(symbols, types); - // Visit the primary output of this compilation, as well as all references. - visitor.Visit(compilation.Assembly); - foreach (var reference in compilation.References) + if ((context.DiscoveryMode & TagHelperDiscoveryMode.CurrentAssembly) == TagHelperDiscoveryMode.CurrentAssembly) { - // We ignore .netmodules here - there really isn't a case where they are used by user code - // even though the Roslyn APIs all support them. - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + visitor.Visit(compilation.Assembly); + } + + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) == TagHelperDiscoveryMode.References) + { + foreach (var reference in compilation.References) { - visitor.Visit(assembly); + // We ignore .netmodules here - there really isn't a case where they are used by user code + // even though the Roslyn APIs all support them. + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + { + visitor.Visit(assembly.GlobalNamespace); + } } } diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs index 074ad68527e7..593c79eb873f 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/DefaultTagHelperDescriptorProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -35,16 +35,21 @@ public void Execute(TagHelperDescriptorProviderContext context) var types = new List(); var visitor = new TagHelperTypeVisitor(iTagHelper, types); - // We always visit the global namespace. - visitor.Visit(compilation.Assembly.GlobalNamespace); + if ((context.DiscoveryMode & TagHelperDiscoveryMode.CurrentAssembly) == TagHelperDiscoveryMode.CurrentAssembly) + { + visitor.Visit(compilation.Assembly.GlobalNamespace); + } - foreach (var reference in compilation.References) + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) == TagHelperDiscoveryMode.References) { - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + foreach (var reference in compilation.References) { - if (IsTagHelperAssembly(assembly)) + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { - visitor.Visit(assembly.GlobalNamespace); + if (IsTagHelperAssembly(assembly)) + { + visitor.Visit(assembly.GlobalNamespace); + } } } } diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/EventHandlerTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/EventHandlerTagHelperDescriptorProvider.cs index 25bc587b47c0..5271da7e0d06 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/EventHandlerTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/EventHandlerTagHelperDescriptorProvider.cs @@ -36,7 +36,7 @@ public void Execute(TagHelperDescriptorProviderContext context) return; } - var eventHandlerData = GetEventHandlerData(compilation); + var eventHandlerData = GetEventHandlerData(compilation, context.DiscoveryMode); foreach (var tagHelper in CreateEventHandlerTagHelpers(eventHandlerData)) { @@ -44,7 +44,7 @@ public void Execute(TagHelperDescriptorProviderContext context) } } - private List GetEventHandlerData(Compilation compilation) + private List GetEventHandlerData(Compilation compilation, TagHelperDiscoveryMode discoveryMode) { var eventHandlerAttribute = compilation.GetTypeByMetadataName(ComponentsApi.EventHandlerAttribute.FullTypeName); if (eventHandlerAttribute == null) @@ -56,15 +56,21 @@ private List GetEventHandlerData(Compilation compilation) var types = new List(); var visitor = new EventHandlerDataVisitor(types); - // Visit the primary output of this compilation, as well as all references. - visitor.Visit(compilation.Assembly); - foreach (var reference in compilation.References) + if ((discoveryMode & TagHelperDiscoveryMode.CurrentAssembly) == TagHelperDiscoveryMode.CurrentAssembly) { - // We ignore .netmodules here - there really isn't a case where they are used by user code - // even though the Roslyn APIs all support them. - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + visitor.Visit(compilation.Assembly.GlobalNamespace); + } + + if ((discoveryMode & TagHelperDiscoveryMode.References) == TagHelperDiscoveryMode.References) + { + foreach (var reference in compilation.References) { - visitor.Visit(assembly); + // We ignore .netmodules here - there really isn't a case where they are used by user code + // even though the Roslyn APIs all support them. + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + { + visitor.Visit(assembly.GlobalNamespace); + } } } diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs index 789abca3183a..8d6c15c785a9 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/KeyTagHelperDescriptorProvider.cs @@ -27,6 +27,11 @@ public void Execute(TagHelperDescriptorProviderContext context) return; } + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) != TagHelperDiscoveryMode.References) + { + return; + } + var renderTreeBuilderType = compilation.GetTypeByMetadataName(ComponentsApi.RenderTreeBuilder.FullTypeName); if (renderTreeBuilderType == null) { diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/Properties/AssemblyInfo.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/Properties/AssemblyInfo.cs index c83b18dbc584..0f7b58646633 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/Properties/AssemblyInfo.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/Properties/AssemblyInfo.cs @@ -5,6 +5,7 @@ [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("rzc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("RazorSourceGenerators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/RefTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/RefTagHelperDescriptorProvider.cs index 1eafca4181e8..4068cfdd41e7 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/RefTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/RefTagHelperDescriptorProvider.cs @@ -27,6 +27,11 @@ public void Execute(TagHelperDescriptorProviderContext context) return; } + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) != TagHelperDiscoveryMode.References) + { + return; + } + var elementReference = compilation.GetTypeByMetadataName(ComponentsApi.ElementReference.FullTypeName); if (elementReference == null) { diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/SplatTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/SplatTagHelperDescriptorProvider.cs index 3d6421442726..7e3656b36dc0 100644 --- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/SplatTagHelperDescriptorProvider.cs +++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/SplatTagHelperDescriptorProvider.cs @@ -27,6 +27,11 @@ public void Execute(TagHelperDescriptorProviderContext context) return; } + if ((context.DiscoveryMode & TagHelperDiscoveryMode.References) != TagHelperDiscoveryMode.References) + { + return; + } + var renderTreeBuilder = compilation.GetTypeByMetadataName(ComponentsApi.RenderTreeBuilder.FullTypeName); if (renderTreeBuilder == null) { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIncrementalismTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIncrementalismTest.cs index 700c19038513..4345be90f203 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIncrementalismTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIncrementalismTest.cs @@ -66,35 +66,7 @@ public async Task BuildIncremental_SimpleMvc_PersistsTargetInputFile() } } - [Fact] - [InitializeTestProject("SimpleMvc")] - public async Task RazorGenerate_RegeneratesTagHelperInputs_IfFileChanges() - { - // Act - 1 - var expectedTagHelperCacheContent = @"""Name"":""SimpleMvc.SimpleTagHelper"""; - var result = await DotnetMSBuild("Build"); - var file = Path.Combine(Project.DirectoryPath, "SimpleTagHelper.cs"); - var tagHelperOutputCache = Path.Combine(IntermediateOutputPath, "SimpleMvc.TagHelpers.output.cache"); - var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cshtml.g.cs"); - - // Assert - 1 - Assert.BuildPassed(result); - Assert.FileContains(result, tagHelperOutputCache, expectedTagHelperCacheContent); - var fileThumbPrint = GetThumbPrint(generatedFile); - - // Act - 2 - // Update the source content and build. We should expect the outputs to be regenerated. - ReplaceContent(string.Empty, file); - result = await DotnetMSBuild("Build"); - - // Assert - 2 - Assert.BuildPassed(result); - Assert.FileDoesNotContain(result, tagHelperOutputCache, @"""Name"":""SimpleMvc.SimpleTagHelper"""); - var newThumbPrint = GetThumbPrint(generatedFile); - Assert.NotEqual(fileThumbPrint, newThumbPrint); - } - - [Fact] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")] [InitializeTestProject("SimpleMvc")] public async Task Build_ErrorInGeneratedCode_ReportsMSBuildError_OnIncrementalBuild() { @@ -171,7 +143,7 @@ async Task VerifyError() } } - [Fact] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")] [InitializeTestProject("MvcWithComponents")] public async Task BuildComponents_DoesNotRegenerateComponentDefinition_WhenDefinitionIsUnchanged() { @@ -220,118 +192,6 @@ public async Task BuildComponents_DoesNotRegenerateComponentDefinition_WhenDefin Assert.Equal(definitionThumbprint, GetThumbPrint(tagHelperOutputCache)); } - [Fact] - [InitializeTestProject("MvcWithComponents")] - public async Task BuildComponents_RegeneratesComponentDefinition_WhenFilesChange() - { - // Act - 1 - var updatedContent = "@code { [Parameter] public string AParameter { get; set; } }"; - var tagHelperOutputCache = Path.Combine(IntermediateOutputPath, "MvcWithComponents.TagHelpers.output.cache"); - - var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs"); - var generatedDefinitionFile = Path.Combine(RazorComponentIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs"); - - // Assert - 1 - var result = await DotnetMSBuild("Build"); - - Assert.BuildPassed(result); - var outputFile = Path.Combine(OutputPath, "MvcWithComponents.dll"); - Assert.FileExists(result, OutputPath, "MvcWithComponents.dll"); - var outputAssemblyThumbprint = GetThumbPrint(outputFile); - - Assert.FileExists(result, generatedDefinitionFile); - var generatedDefinitionThumbprint = GetThumbPrint(generatedDefinitionFile); - Assert.FileExists(result, generatedFile); - var generatedFileThumbprint = GetThumbPrint(generatedFile); - - Assert.FileExists(result, tagHelperOutputCache); - Assert.FileContains( - result, - tagHelperOutputCache, - @"""Name"":""MvcWithComponents.Views.Shared.NavMenu"""); - - var definitionThumbprint = GetThumbPrint(tagHelperOutputCache); - - // Act - 2 - ReplaceContent(updatedContent, "Views", "Shared", "NavMenu.razor"); - result = await DotnetMSBuild("Build"); - - // Assert - 2 - Assert.FileExists(result, OutputPath, "MvcWithComponents.dll"); - Assert.NotEqual(outputAssemblyThumbprint, GetThumbPrint(outputFile)); - - Assert.FileExists(result, generatedDefinitionFile); - Assert.NotEqual(generatedDefinitionThumbprint, GetThumbPrint(generatedDefinitionFile)); - Assert.FileExists(result, generatedFile); - Assert.NotEqual(generatedFileThumbprint, GetThumbPrint(generatedFile)); - - Assert.FileExists(result, tagHelperOutputCache); - Assert.FileContains( - result, - tagHelperOutputCache, - @"""Name"":""MvcWithComponents.Views.Shared.NavMenu"""); - - Assert.FileContains( - result, - tagHelperOutputCache, - "AParameter"); - - Assert.NotEqual(definitionThumbprint, GetThumbPrint(tagHelperOutputCache)); - } - - [Fact] - [InitializeTestProject("MvcWithComponents")] - public async Task BuildComponents_DoesNotModifyFiles_IfFilesDoNotChange() - { - // Act - 1 - var tagHelperOutputCache = Path.Combine(IntermediateOutputPath, "MvcWithComponents.TagHelpers.output.cache"); - - var file = Path.Combine(Project.DirectoryPath, "Views", "Shared", "NavMenu.razor.g.cs"); - var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs"); - var generatedDefinitionFile = Path.Combine(RazorComponentIntermediateOutputPath, "Views", "Shared", "NavMenu.razor.g.cs"); - - // Assert - 1 - var result = await DotnetMSBuild("Build"); - - Assert.BuildPassed(result); - var outputFile = Path.Combine(OutputPath, "MvcWithComponents.dll"); - Assert.FileExists(result, OutputPath, "MvcWithComponents.dll"); - var outputAssemblyThumbprint = GetThumbPrint(outputFile); - - Assert.FileExists(result, generatedDefinitionFile); - var generatedDefinitionThumbprint = GetThumbPrint(generatedDefinitionFile); - Assert.FileExists(result, generatedFile); - var generatedFileThumbprint = GetThumbPrint(generatedFile); - - Assert.FileExists(result, tagHelperOutputCache); - Assert.FileContains( - result, - tagHelperOutputCache, - @"""Name"":""MvcWithComponents.Views.Shared.NavMenu"""); - - var definitionThumbprint = GetThumbPrint(tagHelperOutputCache); - - // Act - 2 - result = await DotnetMSBuild("Build"); - - // Assert - 2 - Assert.FileExists(result, OutputPath, "MvcWithComponents.dll"); - Assert.Equal(outputAssemblyThumbprint, GetThumbPrint(outputFile)); - - Assert.FileExists(result, generatedDefinitionFile); - Assert.Equal(generatedDefinitionThumbprint, GetThumbPrint(generatedDefinitionFile)); - Assert.FileExists(result, generatedFile); - Assert.Equal(generatedFileThumbprint, GetThumbPrint(generatedFile)); - - Assert.FileExists(result, tagHelperOutputCache); - Assert.FileContains( - result, - tagHelperOutputCache, - @"""Name"":""MvcWithComponents.Views.Shared.NavMenu"""); - - Assert.Equal(definitionThumbprint, GetThumbPrint(tagHelperOutputCache)); - } - [Fact] [InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")] public async Task IncrementalBuild_WithP2P_WorksWhenBuildProjectReferencesIsDisabled() @@ -369,7 +229,7 @@ public async Task IncrementalBuild_WithP2P_WorksWhenBuildProjectReferencesIsDisa Assert.FileExists(result, OutputPath, "ClassLibrary.Views.pdb"); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")] [InitializeTestProject("ClassLibrary")] public async Task Build_TouchesUpToDateMarkerFile() { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs index 7f7e829b06f3..04102d7f2e4f 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntegrationTest.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; @@ -35,7 +36,7 @@ private async Task Build_SimpleMvc_WithoutBuildServer_CanBuildSuccessfully(MSBui { var result = await DotnetMSBuild("Build", "/p:UseRazorBuildServer=false", - suppressBuildServer: true, + suppressTestSpecificBuildServer: true, msBuildProcessKind: msBuildProcessKind); Assert.BuildPassed(result); @@ -217,7 +218,7 @@ public async Task Build_WithP2P_CopiesRazorAssembly() Assert.FileExists(result, OutputPath, "ClassLibrary.Views.pdb"); } - [Fact] + [Fact(Skip = "Blocked until https://github.com/dotnet/roslyn/issues/50090 is resolved.")] [InitializeTestProject("SimplePages", additionalProjects: "LinkedDir")] public async Task Build_SetsUpEmbeddedResourcesWithLogicalName() { @@ -478,25 +479,6 @@ public async Task Build_GeneratesHostingAttributes_WhenGenerateRazorHostingAssem Assert.FileDoesNotContain(result, razorAssemblyInfo, "Microsoft.AspNetCore.Razor.Hosting.RazorConfigurationNameAttribute"); } - [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/13303")] - [InitializeTestProject("SimpleMvcFSharp", language: "F#")] - public async Task Build_SimpleMvcFSharp_NoopsWithoutFailing() - { - var result = await DotnetMSBuild("Build"); - - Assert.BuildPassed(result); - - Assert.FileExists(result, OutputPath, "SimpleMvcFSharp.dll"); - Assert.FileExists(result, OutputPath, "SimpleMvcFSharp.pdb"); - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvcFSharp.dll"); - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvcFSharp.pdb"); - - Assert.FileDoesNotExist(result, OutputPath, "SimpleMvcFSharp.Views.dll"); - Assert.FileDoesNotExist(result, OutputPath, "SimpleMvcFSharp.Views.pdb"); - Assert.FileDoesNotExist(result, IntermediateOutputPath, "SimpleMvcFSharp.RazorAssemblyInfo.cs"); - Assert.FileDoesNotExist(result, IntermediateOutputPath, "SimpleMvcFSharp.RazorAssemblyInfo.fs"); - } - [Fact] [InitializeTestProject("SimpleMvc")] public async Task Build_WithGenerateRazorAssemblyInfo_False_DoesNotGenerateAssemblyInfo() @@ -568,6 +550,7 @@ public async Task Build_WithP2P_WorksWhenBuildProjectReferencesIsDisabled() [InitializeTestProject("AppWithP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibraryMvc21" })] public async Task Build_WithP2P_Referencing21Project_Works() { + // Verifies building with different versions of Razor.Tasks works. Loosely modeled after the repro // scenario listed in https://github.com/Microsoft/msbuild/issues/3572 var additionalProjectContent = @" @@ -577,7 +560,7 @@ public async Task Build_WithP2P_Referencing21Project_Works() "; AddProjectFileContent(additionalProjectContent); - var result = await DotnetMSBuild(target: default); + var result = await DotnetMSBuild(target: default, suppressTestSpecificBuildServer: true); Assert.BuildPassed(result); @@ -628,21 +611,18 @@ public async Task Build_WithDeterministicFlagSet_OutputsDeterministicViewsAssemb [Fact] [InitializeTestProject("SimpleMvc")] - public async Task Build_WithoutServer_ErrorDuringBuild_DisplaysErrorInMsBuildOutput() + public async Task Build_ErrorDuringBuild_DisplaysErrorInMsBuildOutput() { var result = await DotnetMSBuild( "Build", "/p:UseRazorBuildServer=false /p:RazorLangVersion=99.0", - suppressBuildServer: true); + suppressTestSpecificBuildServer: true); Assert.BuildFailed(result); - Assert.BuildOutputContainsLine( + Assert.BuildError( result, - $"Invalid option 99.0 for Razor language version --version; must be Latest or a valid version in range 1.0 to 5.0."); - - // Compilation failed without creating the views assembly - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.dll"); - Assert.FileDoesNotExist(result, IntermediateOutputPath, "SimpleMvc.Views.dll"); + "RZ3600", + message: "Invalid value 99.0 for RazorLangVersion. Valid values include 'Latest' or a valid version in range 1.0 to 5.0."); } [Fact] @@ -652,7 +632,7 @@ public async Task Build_ImplicitCSharp8_NullableEnforcement_WarningsDuringBuild_ var result = await DotnetMSBuild( "Build", "/p:Nullable=enable", - suppressBuildServer: true); + suppressTestSpecificBuildServer: true); var indexFilePath = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cshtml.g.cs"); Assert.BuildPassed(result, allowWarnings: true); @@ -668,7 +648,7 @@ public async Task Build_ExplicitCSharp73_NullableEnforcement_Disabled_NoNullable var result = await DotnetMSBuild( "Build", "/p:LangVersion=7.3", - suppressBuildServer: true); + suppressTestSpecificBuildServer: true); var indexFilePath = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cshtml.g.cs"); Assert.BuildPassed(result, allowWarnings: false); @@ -698,7 +678,7 @@ public async Task Build_CSharp8_NullableEnforcement_WarningsDuringBuild_NoBuildS var result = await DotnetMSBuild( "Build", "/p:LangVersion=8.0 /p:Nullable=enable", - suppressBuildServer: true); + suppressTestSpecificBuildServer: true); var indexFilePath = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cshtml.g.cs"); Assert.BuildPassed(result, allowWarnings: true); @@ -714,7 +694,7 @@ public async Task Build_Mvc_WithoutAddRazorSupportForMvc() var result = await DotnetMSBuild( "Build", "/p:AddRazorSupportForMvc=false", - suppressBuildServer: true); + suppressTestSpecificBuildServer: true); Assert.BuildWarning(result, "RAZORSDK1004"); } @@ -733,6 +713,42 @@ public async Task Build_WithNoResolvedRazorConfiguration() Assert.BuildWarning(result, "RAZORSDK1000"); } + [Fact] + [InitializeTestProject("SimpleMvc")] + public async Task Build_EmbedRazorGenerateSources_EmbedsCshtmlFiles() + { + var result = await DotnetMSBuild("Build", "/p:EmbedRazorGenerateSources=true"); + + Assert.BuildPassed(result); + + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.Views.dll"); + + var assembly = LoadAssemblyFromBytes(result.Project.DirectoryPath, IntermediateOutputPath, "SimpleMvc.Views.dll"); + var resources = assembly.GetManifestResourceNames(); + + Assert.Equal(new string[] + { + "/Views/Home/About.cshtml", + "/Views/Home/Contact.cshtml", + "/Views/Home/Index.cshtml", + "/Views/Shared/Error.cshtml", + "/Views/Shared/_Layout.cshtml", + "/Views/Shared/_ValidationScriptsPartial.cshtml", + "/Views/_ViewImports.cshtml", + "/Views/_ViewStart.cshtml", + }, + resources.OrderBy(r => r, StringComparer.Ordinal)); + } + + private Assembly LoadAssemblyFromBytes(params string[] paths) + { + // We need to load the assembly from bytes to load it without locking the file - and yes, we need to + // load the pdb too, or else the CLR will load/lock it based on the path specified in the assembly. + var assemblyBytes = File.ReadAllBytes(Path.Combine(paths)); + var symbolBytes = File.ReadAllBytes(Path.ChangeExtension(Path.Combine(paths), ".pdb")); + return Assembly.Load(assemblyBytes, symbolBytes); + } + private static DependencyContext ReadDependencyContext(string depsFilePath) { var reader = new DependencyContextJsonReader(); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntrospectionTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntrospectionTest.cs index 041b59a9a5cc..d1e05b93f3fc 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntrospectionTest.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildIntrospectionTest.cs @@ -287,5 +287,32 @@ public async Task IntrospectRazorSdkWatchItems() Assert.BuildOutputContainsLine(result, "Watch: Index.razor"); Assert.BuildOutputContainsLine(result, "Watch: Index.razor.css"); } + + [Fact(Skip = "Blocked until https://github.com/dotnet/roslyn/issues/50090 is resolved.")] + [InitializeTestProject("SimpleMvc", additionalProjects: "LinkedDir")] + public async Task Build_WorksWithLinkedFiles() + { + // Arrange + var projectContent = @" + + + + + +"; + AddProjectFileContent(projectContent); + + var result = await DotnetMSBuild("ResolveRazorGenerateInputs", "/t:_IntrospectRazorGenerateWithTargetPath"); + + Assert.BuildPassed(result); + + Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFile.cshtml.g.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFile2.cshtml.g.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFileWithRename.cshtml.g.cs"); + + Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile.cshtml")} LinkedFile.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFile.cshtml.g.cs")}"); + Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile2.cshtml")} LinkedFileOut\LinkedFile2.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFile2.cshtml.g.cs")}"); + Assert.BuildOutputContainsLine(result, $@"RazorGenerateWithTargetPath: {Path.Combine("..", "LinkedDir", "LinkedFile3.cshtml")} LinkedFileOut\LinkedFileWithRename.cshtml {Path.Combine(RazorIntermediateOutputPath, "LinkedFileOut", "LinkedFileWithRename.cshtml.g.cs")}"); + } } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildPerformanceTest.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildPerformanceTest.cs deleted file mode 100644 index 05b2ab08bbd4..000000000000 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildPerformanceTest.cs +++ /dev/null @@ -1,110 +0,0 @@ -// 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.Globalization; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests -{ - public class BuildPerformanceTest : MSBuildIntegrationTestBase, IClassFixture - { - public BuildPerformanceTest(BuildServerTestFixture buildServer) - : base(buildServer) - { - } - - [Fact] - [InitializeTestProject("SimpleMvc")] - public async Task BuildMvcApp() - { - var result = await DotnetMSBuild(target: default, args: "/clp:PerformanceSummary"); - - Assert.BuildPassed(result); - var summary = ParseTaskPerformanceSummary(result.Output); - - Assert.Equal(1, summary.First(f => f.Name == "RazorGenerate").Calls); - Assert.Equal(1, summary.First(f => f.Name == "RazorTagHelper").Calls); - - // Incremental builds - for (var i = 0; i < 2; i++) - { - result = await DotnetMSBuild(target: default, args: "/clp:PerformanceSummary"); - - Assert.BuildPassed(result); - summary = ParseTaskPerformanceSummary(result.Output); - - Assert.DoesNotContain(summary, item => item.Name == "RazorGenerate"); - Assert.DoesNotContain(summary, item => item.Name == "RazorTagHelper"); - } - } - - [Fact] - [InitializeTestProject("MvcWithComponents")] - public async Task BuildMvcAppWithComponents() - { - var result = await DotnetMSBuild(target: default, args: "/clp:PerformanceSummary"); - - Assert.BuildPassed(result); - var summary = ParseTaskPerformanceSummary(result.Output); - - // One for declaration build, one for the "real" code gen - Assert.Equal(2, summary.First(f => f.Name == "RazorGenerate").Calls); - Assert.Equal(1, summary.First(f => f.Name == "RazorTagHelper").Calls); - - // Incremental builds - for (var i = 0; i < 2; i++) - { - result = await DotnetMSBuild(target: default, args: "/clp:PerformanceSummary"); - - Assert.BuildPassed(result); - summary = ParseTaskPerformanceSummary(result.Output); - - Assert.DoesNotContain(summary, item => item.Name == "RazorGenerate"); - Assert.DoesNotContain(summary, item => item.Name == "RazorTagHelper"); - } - } - - private List ParseTaskPerformanceSummary(string output) - { - const string Header = "Task Performance Summary:"; - var lines = output.Split(Environment.NewLine); - var taskSection = Array.LastIndexOf(lines, Header); - Assert.True(taskSection != -1, $"Could not find line ${Header} in {output}"); - - var entries = new List(); - // 6 ms FindAppConfigFile 4 calls - var matcher = new Regex(@"\s+(?"); - var result = await DotnetMSBuild(RazorGenerateTarget, "/t:_IntrospectRazorGenerateWithTargetPath"); + var result = await DotnetMSBuild("Build", "/t:_IntrospectRazorGenerateWithTargetPath"); Assert.BuildPassed(result); - // RazorGenerate should compile the assembly, but not the views. - Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.dll"); - Assert.FileDoesNotExist(result, IntermediateOutputPath, "SimpleMvc.Views.dll"); - Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "_ViewImports.cshtml.g.cs"); Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "_ViewStart.cshtml.g.cs"); Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "About.cshtml.g.cs"); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs index a98f599281d3..6ab6d800e39c 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs @@ -64,7 +64,7 @@ public async Task CanOverrideScopeIdentifiers() "); - var result = await DotnetMSBuild("Build", "/p:EnableDefaultScopedCssItems=false"); + var result = await DotnetMSBuild("Build", "/p:EnableDefaultScopedCssItems=false /p:_RazorSourceGeneratorWriteGeneratedOutput=true"); Assert.BuildPassed(result); var scoped = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Styles", "Pages", "Counter.rz.scp.css"); @@ -169,7 +169,7 @@ public async Task Publish_Publishes_IndividualScopedCssFiles_WhenNoBundlingIsEna [InitializeTestProject("ComponentApp", language: "C#")] public async Task Build_GeneratedComponentContainsScope() { - var result = await DotnetMSBuild("Build"); + var result = await DotnetMSBuild("Build", "/p:_RazorSourceGeneratorWriteGeneratedOutput=true"); Assert.BuildPassed(result); var generatedCounter = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css"); @@ -188,7 +188,7 @@ public async Task Build_GeneratedComponentContainsScope() [InitializeTestProject("ComponentApp", language: "C#")] public async Task Build_RemovingScopedCssAndBuilding_UpdatesGeneratedCodeAndBundle() { - var result = await DotnetMSBuild("Build"); + var result = await DotnetMSBuild("Build", "/p:_RazorSourceGeneratorWriteGeneratedOutput=true"); Assert.BuildPassed(result); Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css"); @@ -201,7 +201,7 @@ public async Task Build_RemovingScopedCssAndBuilding_UpdatesGeneratedCodeAndBund File.Delete(Path.Combine(Project.DirectoryPath, "Components", "Pages", "Counter.razor.css")); - result = await DotnetMSBuild("Build"); + result = await DotnetMSBuild("Build", "/p:_RazorSourceGeneratorWriteGeneratedOutput=true"); Assert.BuildPassed(result); Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css"); @@ -237,7 +237,7 @@ public async Task Build_ScopedCssTransformation_AndBundling_IsIncremental() var thumbprintLookup = new Dictionary(); // Act 1 - var result = await DotnetMSBuild("Build"); + var result = await DotnetMSBuild("Build", "/p:_RazorSourceGeneratorWriteGeneratedOutput=true"); var directoryPath = Path.Combine(result.Project.DirectoryPath, IntermediateOutputPath, "scopedcss"); diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj index d7ea394a9c6c..d95539f072c2 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/Microsoft.NET.Sdk.Razor.csproj @@ -1,4 +1,4 @@ - + Razor is a markup syntax for adding server-side logic to web pages. This package contains MSBuild support for Razor. $(DefaultNetCoreTargetFramework);net46 @@ -36,6 +36,11 @@ ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" UndefineProperties="TargetFramework;TargetFrameworks" /> + + @@ -72,6 +77,7 @@ + @@ -79,6 +85,10 @@ Text="rzc outputs were not found in $(ArtifactsBinDir)rzc\$(Configuration)\$(DefaultNetCoreTargetFramework)\publish" Condition="'@(RazorToolsOutput->Count())' == '0'" /> + + @@ -88,6 +98,10 @@ + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.CodeGeneration.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.CodeGeneration.targets index 76c4bba7c972..24461361ace1 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.CodeGeneration.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.CodeGeneration.targets @@ -174,12 +174,4 @@ Copyright (c) .NET Foundation. All rights reserved. $(ResolveRazorCompileInputsDependsOn);_ResolveGeneratedRazorCompileInputs - - - - - - diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Compilation.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Compilation.targets index c512c7ee49f2..cb3e20b8509e 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Compilation.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.Compilation.targets @@ -66,8 +66,10 @@ Copyright (c) .NET Foundation. All rights reserved. + + + + + true @@ -156,7 +163,7 @@ Copyright (c) .NET Foundation. All rights reserved. Prefer32Bit="$(Prefer32Bit)" PreferredUILang="$(PreferredUILang)" ProvideCommandLineArgs="$(ProvideCommandLineArgs)" - References="@(RazorReferencePath)" + References="@(ReferencePath);@(IntermediateAssembly)" ReportAnalyzer="$(ReportAnalyzer)" Resources="@(_RazorCoreCompileResourceInputs);@(CompiledLicenseFile)" ResponseFiles="$(CompilerResponseFile)" diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.DesignTime.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.DesignTime.targets index 4d8e0b5c4d8e..a5fdd32589ee 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.DesignTime.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.DesignTime.targets @@ -89,7 +89,8 @@ Copyright (c) .NET Foundation. All rights reserved. + Returns="@(RazorGenerateWithTargetPath)" + Condition="'$(_UseRazorSourceGenerator)' != 'true'"> + DependsOnTargets="$(_RazorGenerateComponentDeclarationDesignTimeDependsOn)" + Condition="'$(_UseRazorSourceGenerator)' != 'true'"> diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.SourceGenerators.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.SourceGenerators.targets new file mode 100644 index 000000000000..606a2f350d0b --- /dev/null +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.SourceGenerators.targets @@ -0,0 +1,70 @@ + + + + + + + + <_RazorSdkSourceGeneratorDirectoryRoot>$(RazorSdkDirectoryRoot)source-generators\ + <_RazorReferenceAssemblyTagHelpersOutputPath>$([System.IO.Path]::GetFullPath($(IntermediateOutputPath)))RazorTagHelper.refs.out.cache + + + + + <_RazorAnalyzer Include="$(_RazorSdkSourceGeneratorDirectoryRoot)Newtonsoft.Json.dll" /> + <_RazorAnalyzer Include="$(_RazorSdkSourceGeneratorDirectoryRoot)Microsoft.AspNetCore.Mvc.Razor.Extensions.dll" /> + <_RazorAnalyzer Include="$(_RazorSdkSourceGeneratorDirectoryRoot)Microsoft.AspNetCore.Razor.Language.dll" /> + <_RazorAnalyzer Include="$(_RazorSdkSourceGeneratorDirectoryRoot)Microsoft.CodeAnalysis.Razor.dll" /> + <_RazorAnalyzer Include="$(_RazorSdkSourceGeneratorDirectoryRoot)RazorSourceGenerators.dll" /> + + + + + + + + + + + + + + + + + + + + + + + + + <_RazorAdditionalFile Include="@(RazorComponentWithTargetPath)" /> + + <_RazorAdditionalFile Include="@(RazorGenerateWithTargetPath)" Condition="'$(RazorCompileOnBuild)' != 'false'" /> + + + + + + + + + + + diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets index ed4cd57af019..41c71b1bbd88 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets @@ -1,4 +1,4 @@ - + <_TargetingNET60OrLater Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND + $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0')) ">true + + <_UseRazorSourceGenerator Condition="'$(_TargetingNET60OrLater)' == 'true'">true + @@ -97,9 +105,14 @@ Copyright (c) .NET Foundation. All rights reserved. ResolveRazorConfiguration; ResolveRazorGenerateInputs; AssignRazorGenerateTargetPaths; + _ResolveGeneratedRazorCompileInputs; + _CheckForIncorrectMvcConfiguration; + + + + $(PrepareForRazorGenerateDependsOn); ResolveAssemblyReferenceRazorGenerateInputs; _CheckForMissingRazorCompiler; - _CheckForIncorrectMvcConfiguration; ResolveTagHelperRazorGenerateInputs @@ -112,6 +125,10 @@ Copyright (c) .NET Foundation. All rights reserved. PrepareForRazorGenerate; + + + + $(RazorGenerateDependsOn); _CheckForMissingRazorCompiler; RazorCoreGenerate @@ -128,7 +145,6 @@ Copyright (c) .NET Foundation. All rights reserved. ResolveRazorEmbeddedResources; - _FixupRazorReferencePathForRazorCompile; @@ -146,12 +162,12 @@ Copyright (c) .NET Foundation. All rights reserved. _RazorAddDebugSymbolsProjectOutputGroupOutput - + RazorComponentGenerate; $(CoreCompileDependsOn) - + RazorGenerateComponentDeclarationDesignTime; $(CoreCompileDependsOn) @@ -354,9 +370,11 @@ Copyright (c) .NET Foundation. All rights reserved. - + + + - + @@ -597,17 +615,6 @@ Copyright (c) .NET Foundation. All rights reserved. - - - - - - - - + false + false + false + + + false + + + false + + + + + + + + + + + + + + + + + + diff --git a/src/Razor/SourceGenerator/src/SourceGeneratorProjectItem.cs b/src/Razor/SourceGenerator/src/SourceGeneratorProjectItem.cs new file mode 100644 index 000000000000..0a73fa227c91 --- /dev/null +++ b/src/Razor/SourceGenerator/src/SourceGeneratorProjectItem.cs @@ -0,0 +1,46 @@ +// 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.IO; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.CodeAnalysis.Razor +{ + internal class SourceGeneratorProjectItem : RazorProjectItem + { + private readonly string _fileKind; + + public SourceGeneratorProjectItem(string basePath, string filePath, string relativePhysicalPath, string fileKind, AdditionalText additionalText, string cssScope) + { + BasePath = basePath; + FilePath = filePath; + RelativePhysicalPath = relativePhysicalPath; + _fileKind = fileKind; + AdditionalText = additionalText; + CssScope = cssScope; + var text = AdditionalText.GetText(); + RazorSourceDocument = new SourceTextRazorSourceDocument(AdditionalText.Path, relativePhysicalPath, text); + } + + public AdditionalText AdditionalText { get; } + + public override string BasePath { get; } + + public override string FilePath { get; } + + public override bool Exists => true; + + public override string PhysicalPath => AdditionalText.Path; + + public override string RelativePhysicalPath { get; } + + public override string FileKind => _fileKind ?? base.FileKind; + + public override string CssScope { get; } + + public override Stream Read() + => throw new NotSupportedException("This API should not be invoked. We should instead be relying on " + + "the RazorSourceDocument instead associated with this item instead."); + } +} diff --git a/src/Razor/SourceGenerator/src/SourceTextRazorSourceDocument.cs b/src/Razor/SourceGenerator/src/SourceTextRazorSourceDocument.cs new file mode 100644 index 000000000000..c391c5ee3388 --- /dev/null +++ b/src/Razor/SourceGenerator/src/SourceTextRazorSourceDocument.cs @@ -0,0 +1,70 @@ +// 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.Linq; +using System.Text; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Razor +{ + internal class SourceTextRazorSourceDocument : RazorSourceDocument + { + private readonly SourceText _sourceText; + + public SourceTextRazorSourceDocument(string filePath, string relativePath, SourceText sourceText) + { + FilePath = filePath; + RelativePath = relativePath; + _sourceText = sourceText; + Lines = new SourceTextSourceLineCollection(filePath, sourceText.Lines); + } + + public override char this[int position] => _sourceText[position]; + + public override Encoding Encoding => _sourceText.Encoding; + + public override string FilePath { get; } + + public override int Length => _sourceText.Length; + + public override string RelativePath { get; } + + public override RazorSourceLineCollection Lines { get; } + + public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) + { + _sourceText.CopyTo(sourceIndex, destination, destinationIndex, count); + } + + public override byte[] GetChecksum() => _sourceText.GetChecksum().ToArray(); + + public override string GetChecksumAlgorithm() => _sourceText.ChecksumAlgorithm.ToString().ToUpperInvariant(); + + private class SourceTextSourceLineCollection : RazorSourceLineCollection + { + private readonly string _filePath; + private readonly TextLineCollection _textLines; + + public SourceTextSourceLineCollection(string filePath, TextLineCollection textLines) + { + _filePath = filePath; + _textLines = textLines; + } + + public override int Count => _textLines.Count; + + public override int GetLineLength(int index) + { + var line = _textLines[index]; + return line.EndIncludingLineBreak - line.Start; + } + + internal override SourceLocation GetLocation(int position) + { + var line = _textLines.GetLineFromPosition(position); + return new SourceLocation(_filePath, position, line.LineNumber, position); + } + } + } +} diff --git a/src/Razor/SourceGenerator/src/StaticCompilationTagHelperFeature.cs b/src/Razor/SourceGenerator/src/StaticCompilationTagHelperFeature.cs new file mode 100644 index 000000000000..f65b6cf226c4 --- /dev/null +++ b/src/Razor/SourceGenerator/src/StaticCompilationTagHelperFeature.cs @@ -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; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.CodeAnalysis.Razor +{ + internal sealed class StaticCompilationTagHelperFeature : RazorEngineFeatureBase, ITagHelperFeature + { + private ITagHelperDescriptorProvider[] _providers; + + public IReadOnlyList GetDescriptors() + { + if (Compilation is null) + { + return Array.Empty(); + } + + var results = new List(); + + + var context = TagHelperDescriptorProviderContext.Create(results); + context.SetCompilation(Compilation); + context.DiscoveryMode = DiscoveryMode; + + for (var i = 0; i < _providers.Length; i++) + { + _providers[i].Execute(context); + } + + return results; + } + + public Compilation Compilation { get; set; } + + public TagHelperDiscoveryMode DiscoveryMode { get; set; } + + protected override void OnInitialized() + { + _providers = Engine.Features.OfType().OrderBy(f => f.Order).ToArray(); + } + + internal static bool IsValidCompilation(Compilation compilation) + { + var @string = compilation.GetSpecialType(SpecialType.System_String); + + // Do some minimal tests to verify the compilation is valid. If symbols for System.String + // is missing or errored, the compilation may be missing references. + return @string != null && @string.TypeKind != TypeKind.Error; + } + } +} diff --git a/src/Razor/SourceGenerator/src/StaticTagHelperFeature.cs b/src/Razor/SourceGenerator/src/StaticTagHelperFeature.cs new file mode 100644 index 000000000000..6fc6a52ca057 --- /dev/null +++ b/src/Razor/SourceGenerator/src/StaticTagHelperFeature.cs @@ -0,0 +1,17 @@ +// 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 Microsoft.AspNetCore.Razor.Language; + +namespace Microsoft.CodeAnalysis.Razor +{ + internal sealed class StaticTagHelperFeature : ITagHelperFeature + { + public RazorEngine Engine { get; set; } + + public IReadOnlyList TagHelpers { get; set; } + + public IReadOnlyList GetDescriptors() => TagHelpers; + } +} diff --git a/src/Razor/SourceGenerator/src/TagHelperSerializer.cs b/src/Razor/SourceGenerator/src/TagHelperSerializer.cs new file mode 100644 index 000000000000..6f3d97dc279e --- /dev/null +++ b/src/Razor/SourceGenerator/src/TagHelperSerializer.cs @@ -0,0 +1,40 @@ +// 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 System.Text; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Razor.Serialization; +using Newtonsoft.Json; + +namespace RazorSourceGenerators +{ + internal class TagHelperSerializer + { + private static readonly JsonSerializer Serializer = new JsonSerializer + { + Converters = + { + new TagHelperDescriptorJsonConverter(), + new RazorDiagnosticJsonConverter(), + } + }; + + public static void Serialize(string manifestFilePath, IReadOnlyList tagHelpers) + { + using var stream = File.OpenWrite(manifestFilePath); + using var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 4096, leaveOpen: true); + + Serializer.Serialize(writer, tagHelpers); + } + + public static IReadOnlyList Deserialize(string manifestFilePath) + { + using var stream = File.OpenRead(manifestFilePath); + using var reader = new JsonTextReader(new StreamReader(stream)); + + return Serializer.Deserialize>(reader); + } + } +} diff --git a/src/Razor/test/testassets/MvcWithComponents/Views/Home/#FileName.cshtml b/src/Razor/test/testassets/MvcWithComponents/Views/Home/$FileName.cshtml similarity index 100% rename from src/Razor/test/testassets/MvcWithComponents/Views/Home/#FileName.cshtml rename to src/Razor/test/testassets/MvcWithComponents/Views/Home/$FileName.cshtml diff --git a/src/Shared/MSBuild.Testing/Assert.cs b/src/Shared/MSBuild.Testing/Assert.cs index 8262db43dd72..ff5e3b9b39c9 100644 --- a/src/Shared/MSBuild.Testing/Assert.cs +++ b/src/Shared/MSBuild.Testing/Assert.cs @@ -20,8 +20,8 @@ internal class Assert : Xunit.Assert { // Matches `{filename}: error {code}: {message} [{project}] // See https://stackoverflow.com/questions/3441452/msbuild-and-ignorestandarderrorwarningformat/5180353#5180353 - private static readonly Regex ErrorRegex = new Regex(@"^(?'location'.+): error (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); - private static readonly Regex WarningRegex = new Regex(@"^(?'location'.+): warning (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); + private static readonly Regex ErrorRegex = new Regex(@"^((?'location'.+): )?error (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); + private static readonly Regex WarningRegex = new Regex(@"^((?'location'.+): )?warning (?'errorcode'[A-Z0-9]+): (?'message'.+) \[(?'project'.+)\]$"); private static readonly string[] AllowedBuildWarnings = new[] { "MSB3491" , // The process cannot access the file. As long as the build succeeds, we're ok. @@ -50,7 +50,7 @@ public static void BuildPassed(MSBuildResult result, bool allowWarnings = false) } } - public static void BuildError(MSBuildResult result, string errorCode, string location = null) + public static void BuildError(MSBuildResult result, string errorCode, string location = null, string message = null) { if (result == null) { @@ -75,6 +75,11 @@ public static void BuildError(MSBuildResult result, string errorCode, string loc continue; } + if (message != null && match.Groups["message"].Value.Trim() != message) + { + continue; + } + // This is a match return; } @@ -516,7 +521,11 @@ public static void AssemblyContainsType(MSBuildResult result, string assemblyPat assemblyPath = Path.Combine(result.Project.DirectoryPath, Path.Combine(assemblyPath)); var typeNames = GetDeclaredTypeNames(assemblyPath); - Assert.Contains(fullTypeName, typeNames); + + if (!typeNames.Contains(fullTypeName, StringComparer.Ordinal)) + { + throw new MSBuildXunitException(result, $"{fullTypeName} was not found in {string.Join(", ", typeNames)}"); + } } public static void AssemblyDoesNotContainType(MSBuildResult result, string assemblyPath, string fullTypeName)