From e822f5f12dcdbe14de7c49f03256f8ea92f84efe Mon Sep 17 00:00:00 2001 From: Pranav K <prkrishn@hotmail.com> Date: Mon, 20 Jul 2020 13:25:49 -0700 Subject: [PATCH] Create a Blazor WebAssembly SDK (#24044) --- AspNetCore.sln | 63 ++ eng/Build.props | 1 + eng/ProjectReferences.props | 1 + src/Components/Components.slnf | 3 + src/Components/ComponentsNoDeps.slnf | 4 + src/Components/Directory.Build.props | 8 - src/Components/Directory.Build.targets | 2 +- ....BlazorWebAssembly.IntegrationTests.csproj | 78 +++ .../integrationtests}/ServiceWorkerAssert.cs | 2 +- .../WasmBuildIncrementalismTest.cs | 2 +- .../WasmBuildIntegrationTest.cs | 2 +- .../WasmBuildLazyLoadTest.cs | 2 +- .../integrationtests}/WasmCompressionTests.cs | 0 .../WasmPublishIntegrationTest.cs | 18 +- .../integrationtests}/WasmPwaManifestTests.cs | 0 .../Sdk/integrationtests/xunit.runner.json | 5 + .../WebAssembly/Sdk/src/AssetsManifestFile.cs | 33 + .../src/BlazorReadSatelliteAssemblyFile.cs | 38 ++ .../src/BlazorWriteSatelliteAssemblyFile.cs | 53 ++ .../WebAssembly/Sdk/src/BootJsonData.cs | 85 +++ .../WebAssembly/Sdk/src/BrotliCompress.cs | 125 ++++ .../CreateBlazorTrimmerRootDescriptorFile.cs | 79 +++ .../WebAssembly/Sdk/src/GZipCompress.cs | 70 +++ .../src/GenerateBlazorWebAssemblyBootJson.cs | 155 +++++ .../GenerateServiceWorkerAssetsManifest.cs | 97 +++ ...Microsoft.NET.Sdk.BlazorWebAssembly.csproj | 89 +++ ...Microsoft.NET.Sdk.BlazorWebAssembly.nuspec | 19 + .../WebAssembly/Sdk/src/Sdk/Sdk.props | 22 + .../WebAssembly/Sdk/src/Sdk/Sdk.targets | 20 + .../WebAssembly/Sdk/src/_._} | 0 .../Microsoft.NET.Sdk.BlazorWebAssembly.props | 16 + ...icrosoft.NET.Sdk.BlazorWebAssembly.targets | 16 + .../Sdk/src/targets/BlazorWasm.web.config | 40 ++ .../Sdk/src/targets/LinkerWorkaround.xml | 15 + ...ft.NET.Sdk.BlazorWebAssembly.Current.props | 36 ++ ....NET.Sdk.BlazorWebAssembly.Current.targets | 582 ++++++++++++++++++ ...sembly.ServiceWorkerAssetsManifest.targets | 169 +++++ .../BlazorReadSatelliteAssemblyFileTest.cs | 68 ++ .../Sdk/test/GenerateBlazorBootJsonTest.cs | 195 ++++++ ...oft.NET.Sdk.BlazorWebAssembly.Tests.csproj | 12 + .../Sdk/testassets/Directory.Build.props | 50 ++ .../Sdk/testassets/Directory.Build.targets | 7 + .../LinkBaseToWebRoot/js/LinkedScript.js | 0 .../RestoreBlazorWasmTestProjects.csproj | 12 + .../Sdk/testassets/blazor.webassembly.js | 1 + .../testassets/blazorhosted-rid/Program.cs | 0 .../blazorhosted-rid/blazorhosted-rid.csproj | 0 .../Sdk}/testassets/blazorhosted/Program.cs | 0 .../blazorhosted/blazorhosted.csproj | 0 .../Sdk}/testassets/blazorwasm/App.razor | 0 .../blazorwasm/LinkToWebRoot/css/app.css | 0 .../testassets/blazorwasm/Pages/Index.razor | 0 .../Sdk}/testassets/blazorwasm/Program.cs | 0 .../blazorwasm/Resources.ja.resx.txt | 0 .../Sdk}/testassets/blazorwasm/_Imports.razor | 0 .../testassets/blazorwasm/blazorwasm.csproj | 8 +- .../blazorwasm/wwwroot/Fake-License.txt | 0 .../testassets/blazorwasm/wwwroot/css/app.css | 0 .../testassets/blazorwasm/wwwroot/index.html | 0 .../serviceworkers/my-prod-service-worker.js | 0 .../serviceworkers/my-service-worker.js | 0 .../Class1.cs | 0 .../Resources.es-ES.resx | 0 ...classlibrarywithsatelliteassemblies.csproj | 0 .../testassets/razorclasslibrary/Class1.cs | 0 .../RazorClassLibrary.csproj | 0 .../razorclasslibrary/wwwroot/styles.css | 0 .../wwwroot/wwwroot/exampleJsInterop.js | 0 .../WebAssembly/Sdk/tools/Application.cs | 98 +++ .../Sdk/tools/BrotliCompressCommand.cs | 85 +++ .../WebAssembly/Sdk/tools/DebugMode.cs | 27 + ...oft.NET.Sdk.BlazorWebAssembly.Tools.csproj | 22 + .../WebAssembly/Sdk/tools/Program.cs | 30 + .../Sdk/tools/runtimeconfig.template.json | 3 + ...soft.NET.Sdk.Razor.IntegrationTests.csproj | 48 +- src/Shared/E2ETesting/E2ETesting.targets | 9 +- .../MSBuild.Testing}/Assert.cs | 0 .../MSBuild.Testing}/BuildVariables.cs | 6 +- .../MSBuild.Testing}/FIleThumbPrint.cs | 0 .../MSBuild.Testing/MSBuild.Testing.targets | 53 ++ .../MSBuild.Testing}/MSBuildProcessKind.cs | 0 .../MSBuild.Testing}/MSBuildProcessManager.cs | 3 +- .../MSBuild.Testing}/MSBuildResult.cs | 0 .../MSBuild.Testing}/ProjectDirectory.cs | 10 +- 84 files changed, 2625 insertions(+), 72 deletions(-) create mode 100644 src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/ServiceWorkerAssert.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildIncrementalismTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildIntegrationTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmBuildLazyLoadTest.cs (99%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmCompressionTests.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmPublishIntegrationTest.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm => Components/WebAssembly/Sdk/integrationtests}/WasmPwaManifestTests.cs (100%) create mode 100644 src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json create mode 100644 src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BootJsonData.cs create mode 100644 src/Components/WebAssembly/Sdk/src/BrotliCompress.cs create mode 100644 src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GZipCompress.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs create mode 100644 src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs create mode 100644 src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj create mode 100644 src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec create mode 100644 src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props create mode 100644 src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets rename src/{Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js => Components/WebAssembly/Sdk/src/_._} (100%) create mode 100644 src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props create mode 100644 src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets create mode 100644 src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config create mode 100644 src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets create mode 100644 src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets create mode 100644 src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs create mode 100644 src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs create mode 100644 src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj create mode 100644 src/Components/WebAssembly/Sdk/testassets/Directory.Build.props create mode 100644 src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/LinkBaseToWebRoot/js/LinkedScript.js (100%) create mode 100644 src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj create mode 100644 src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted-rid/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted-rid/blazorhosted-rid.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorhosted/blazorhosted.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/App.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/LinkToWebRoot/css/app.css (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Pages/Index.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Program.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/Resources.ja.resx.txt (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/_Imports.razor (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/blazorwasm.csproj (88%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/Fake-License.txt (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/css/app.css (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/index.html (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/Class1.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/Class1.cs (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/RazorClassLibrary.csproj (100%) rename src/{Razor/test => Components/WebAssembly/Sdk}/testassets/razorclasslibrary/wwwroot/styles.css (100%) create mode 100644 src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js create mode 100644 src/Components/WebAssembly/Sdk/tools/Application.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/DebugMode.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj create mode 100644 src/Components/WebAssembly/Sdk/tools/Program.cs create mode 100644 src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/Assert.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/BuildVariables.cs (83%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/FIleThumbPrint.cs (100%) create mode 100644 src/Shared/MSBuild.Testing/MSBuild.Testing.targets rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildProcessKind.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildProcessManager.cs (98%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/MSBuildResult.cs (100%) rename src/{Razor/Microsoft.NET.Sdk.Razor/integrationtests => Shared/MSBuild.Testing}/ProjectDirectory.cs (97%) diff --git a/AspNetCore.sln b/AspNetCore.sln index ca9dfcf2818..50068277d88 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1427,6 +1427,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.Web.Extensions.Tests", "src\Components\Web.Extensions\test\Microsoft.AspNetCore.Components.Web.Extensions.Tests.csproj", "{157605CB-5170-4C1A-980F-4BAE42DB60DE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{FED4267E-E5E4-49C5-98DB-8B3F203596EE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly", "src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", "{6B2734BF-C61D-4889-ABBF-456A4075D59B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tests", "src\Components\WebAssembly\Sdk\test\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", "{83371889-9A3E-4D16-AE77-EB4F83BC6374}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests", "src\Components\WebAssembly\Sdk\integrationtests\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "{525EBCB4-A870-470B-BC90-845306C337D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.NET.Sdk.BlazorWebAssembly.Tools", "src\Components\WebAssembly\Sdk\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", "{175E5CD8-92D4-46BB-882E-3A930D3302D4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -6729,6 +6739,54 @@ Global {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x64.Build.0 = Release|Any CPU {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.ActiveCfg = Release|Any CPU {157605CB-5170-4C1A-980F-4BAE42DB60DE}.Release|x86.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x64.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Debug|x86.Build.0 = Debug|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|Any CPU.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x64.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x64.Build.0 = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x86.ActiveCfg = Release|Any CPU + {6B2734BF-C61D-4889-ABBF-456A4075D59B}.Release|x86.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x64.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x64.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x86.ActiveCfg = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Debug|x86.Build.0 = Debug|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|Any CPU.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x64.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x64.Build.0 = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x86.ActiveCfg = Release|Any CPU + {83371889-9A3E-4D16-AE77-EB4F83BC6374}.Release|x86.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x64.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Debug|x86.Build.0 = Debug|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|Any CPU.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x64.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x64.Build.0 = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x86.ActiveCfg = Release|Any CPU + {525EBCB4-A870-470B-BC90-845306C337D1}.Release|x86.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x64.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Debug|x86.Build.0 = Debug|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|Any CPU.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x64.Build.0 = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.ActiveCfg = Release|Any CPU + {175E5CD8-92D4-46BB-882E-3A930D3302D4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -7444,6 +7502,11 @@ Global {F71FE795-9923-461B-9809-BB1821A276D0} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF} {8294A74F-7DAA-4B69-BC56-7634D93C9693} = {F71FE795-9923-461B-9809-BB1821A276D0} {157605CB-5170-4C1A-980F-4BAE42DB60DE} = {F71FE795-9923-461B-9809-BB1821A276D0} + {FED4267E-E5E4-49C5-98DB-8B3F203596EE} = {562D5067-8CD8-4F19-BCBB-873204932C61} + {6B2734BF-C61D-4889-ABBF-456A4075D59B} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {83371889-9A3E-4D16-AE77-EB4F83BC6374} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {525EBCB4-A870-470B-BC90-845306C337D1} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} + {175E5CD8-92D4-46BB-882E-3A930D3302D4} = {FED4267E-E5E4-49C5-98DB-8B3F203596EE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/eng/Build.props b/eng/Build.props index 56b4917250d..3740546dd64 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -172,6 +172,7 @@ @(ProjectToBuild); @(ProjectToExclude); $(RepoRoot)src\Razor\test\testassets\**\*.*proj; + $(RepoRoot)src\Components\WebAssembly\Sdk\testassets\**\*.*proj; $(RepoRoot)**\node_modules\**\*; $(RepoRoot)**\bin\**\*; $(RepoRoot)**\obj\**\*;" diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index a0e89782cae..6dbfca4646b 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -145,6 +145,7 @@ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Web.Extensions" ProjectPath="$(RepoRoot)src\Components\Web.Extensions\src\Microsoft.AspNetCore.Components.Web.Extensions.csproj" /> <ProjectReferenceProvider Include="Microsoft.Authentication.WebAssembly.Msal" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj" /> <ProjectReferenceProvider Include="Microsoft.JSInterop.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\JSInterop\src\Microsoft.JSInterop.WebAssembly.csproj" /> + <ProjectReferenceProvider Include="Microsoft.NET.Sdk.BlazorWebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Sdk\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Server" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Server\src\Microsoft.AspNetCore.Components.WebAssembly.Server.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly.Authentication\src\Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj" /> <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.WebAssembly" ProjectPath="$(RepoRoot)src\Components\WebAssembly\WebAssembly\src\Microsoft.AspNetCore.Components.WebAssembly.csproj" /> diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf index c202f7c1c69..637ec1a3845 100644 --- a/src/Components/Components.slnf +++ b/src/Components/Components.slnf @@ -110,6 +110,9 @@ "src\\Components\\WebAssembly\\WebAssembly.Authentication\\test\\Microsoft.AspNetCore.Components.WebAssembly.Authentication.Tests.csproj", "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Client\\Wasm.Authentication.Client.csproj", "src\\Components\\WebAssembly\\testassets\\Wasm.Authentication.Shared\\Wasm.Authentication.Shared.csproj", + "src\\Components\\WebAssembly\\Sdk\\src\\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", + "src\\Components\\WebAssembly\\Sdk\\test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj" ] } diff --git a/src/Components/ComponentsNoDeps.slnf b/src/Components/ComponentsNoDeps.slnf index 6dbf031fd31..dd08970d02a 100644 --- a/src/Components/ComponentsNoDeps.slnf +++ b/src/Components/ComponentsNoDeps.slnf @@ -31,6 +31,10 @@ "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Client\\HostedInAspNet.Client.csproj", "src\\Components\\WebAssembly\\testassets\\HostedInAspNet.Server\\HostedInAspNet.Server.csproj", "src\\Components\\WebAssembly\\testassets\\StandaloneApp\\StandaloneApp.csproj", + "src\\Components\\WebAssembly\\Sdk\\src\\Microsoft.NET.Sdk.BlazorWebAssembly.csproj", + "src\\Components\\WebAssembly\\Sdk\\test\\Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj", + "src\\Components\\WebAssembly\\Sdk\\tools\\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj", + "src\\Components\\WebAssembly\\Sdk\\integrationtests\\Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj", "src\\Components\\Web\\src\\Microsoft.AspNetCore.Components.Web.csproj", "src\\Components\\Web\\test\\Microsoft.AspNetCore.Components.Web.Tests.csproj", "src\\Components\\benchmarkapps\\Wasm.Performance\\Driver\\Wasm.Performance.Driver.csproj", diff --git a/src/Components/Directory.Build.props b/src/Components/Directory.Build.props index 1704a1b070a..b1ea764edd5 100644 --- a/src/Components/Directory.Build.props +++ b/src/Components/Directory.Build.props @@ -1,14 +1,6 @@ <Project> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" /> - <PropertyGroup> - <!-- Workaround for https://github.com/dotnet/aspnetcore/issues/5486 which requires the bin and obj directory be in the project directory --> - <BaseIntermediateOutputPath /> - <IntermediateOutputPath /> - <BaseOutputPath /> - <OutputPath /> - </PropertyGroup> - <PropertyGroup> <EnableTypeScriptNuGetTarget>true</EnableTypeScriptNuGetTarget> </PropertyGroup> diff --git a/src/Components/Directory.Build.targets b/src/Components/Directory.Build.targets index 69204d61a66..f3143253af1 100644 --- a/src/Components/Directory.Build.targets +++ b/src/Components/Directory.Build.targets @@ -3,7 +3,7 @@ <BlazorWebAssemblyJSPath>$(RepoRoot)src\Components\Web.JS\dist\$(Configuration)\blazor.webassembly.js</BlazorWebAssemblyJSPath> <BlazorWebAssemblyJSMapPath>$(BlazorWebAssemblyJSPath).map</BlazorWebAssemblyJSMapPath> - <_BlazorDevServerPath>$(RepoRoot)src/Components/WebAssembly/DevServer/src/bin/$(Configuration)/$(DefaultNetCoreTargetFramework)/blazor-devserver.dll</_BlazorDevServerPath> + <_BlazorDevServerPath>$(ArtifactsDir)/bin/Microsoft.AspNetCore.Components.WebAssembly.DevServer/$(Configuration)/$(DefaultNetCoreTargetFramework)/blazor-devserver.dll</_BlazorDevServerPath> <RunCommand>dotnet</RunCommand> <RunArguments>exec "$(_BlazorDevServerPath)" serve --applicationpath "$(TargetPath)" $(AdditionalRunArguments)</RunArguments> </PropertyGroup> diff --git a/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj b/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj new file mode 100644 index 00000000000..4255ce7c73e --- /dev/null +++ b/src/Components/WebAssembly/Sdk/integrationtests/Microsoft.NET.Sdk.BlazorWebAssembly.IntegrationTests.csproj @@ -0,0 +1,78 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <!-- + There's not much value in multi-targeting here, this doesn't run much .NET code, it tests MSBuild. + + This is also a partial workaround for https://github.com/Microsoft/msbuild/issues/2661 - this project + has netcoreapp dependencies that need to be built first. + --> + <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework> + <PreserveCompilationContext>true</PreserveCompilationContext> + + <!-- Tests do not work on Helix yet --> + <BuildHelixPayload>false</BuildHelixPayload> + <TestAppsRoot>$(MSBuildProjectDirectory)\..\testassets\</TestAppsRoot> + </PropertyGroup> + + <Import Project="$(SharedSourceRoot)MSBuild.Testing\MSBuild.Testing.targets" /> + + <ItemGroup> + <None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <ItemGroup> + <Reference Include="Microsoft.Build.Utilities.Core" /> + <Reference Include="Microsoft.Extensions.DependencyModel" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="$(RepoRoot)src\Razor\test\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.csproj" /> + + <ProjectReference Include="..\src\Microsoft.NET.Sdk.BlazorWebAssembly.csproj"> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> + </ProjectReference> + + <ProjectReference Include="$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\Microsoft.NET.Sdk.Razor.csproj"> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <SkipGetTargetFrameworkProperties>true</SkipGetTargetFrameworkProperties> + </ProjectReference> + </ItemGroup> + + <ItemGroup> + <Compile Include="..\src\BootJsonData.cs" LinkBase="Wasm" /> + <Compile Include="..\src\AssetsManifestFile.cs" LinkBase="Wasm" /> + <Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" /> + </ItemGroup> + + <Target Name="GenerateTestData" BeforeTargets="GetAssemblyAttributes"> + <Exec Condition="'$(OS)' == 'Windows_NT'" Command=""$(NuGetPackageRoot)vswhere\$(VSWhereVersion)\tools\vswhere.exe" -latest -prerelease -property installationPath -requires Microsoft.Component.MSBuild" ConsoleToMsBuild="true" StandardErrorImportance="high"> + <Output TaskParameter="ConsoleOutput" PropertyName="_VSInstallDir" /> + </Exec> + <Error Condition="'$(OS)' == 'Windows_NT' and '$(_VSInstallDir)'=='' and '$(Test)' == 'true'" Text="Visual Studio not found on Windows." /> + + <PropertyGroup> + <_DesktopMSBuildPath Condition="'$(OS)' == 'Windows_NT' and Exists('$(_VSInstallDir)\MSBuild\Current\Bin\msbuild.exe')">$(_VSInstallDir)\MSBuild\Current\Bin\msbuild.exe</_DesktopMSBuildPath> + <_DesktopMSBuildPath Condition="'$(OS)' == 'Windows_NT' and Exists('$(_VSInstallDir)\MSBuild\15.0\Bin\msbuild.exe')">$(_VSInstallDir)\MSBuild\15.0\Bin\msbuild.exe</_DesktopMSBuildPath> + </PropertyGroup> + + <Error Condition="'$(OS)' == 'Windows_NT' and '$(_DesktopMSBuildPath)'=='' and '$(Test)' == 'true'" Text="MSBuild.exe not found on Windows." /> + + <ItemGroup> + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>DesktopMSBuildPath</_Parameter1> + <_Parameter2>$(_DesktopMSBuildPath)</_Parameter2> + </AssemblyAttribute> + </ItemGroup> + </Target> + + <Target Name="RestoreTestProjects" BeforeTargets="Restore;Build" Condition="'$(DotNetBuildFromSource)' != 'true'"> + <MSBuild Projects="..\testassets\RestoreBlazorWasmTestProjects\RestoreBlazorWasmTestProjects.csproj" Targets="Restore" Properties="MicrosoftNetCompilersToolsetPackageVersion=$(MicrosoftNetCompilersToolsetPackageVersion);RepoRoot=$(RepoRoot)" /> + </Target> + + <Target Name="EnsureLogFolder" AfterTargets="Build"> + <MakeDir Directories="$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfig)'))" /> + </Target> + +</Project> diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs rename to src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs index 7585b5b76ca..8597d104805 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/ServiceWorkerAssert.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs index f91dd03863e..218567fcc54 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIncrementalismTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIncrementalismTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Xunit; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs index 12fbbd03804..c521c6be7e9 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildIntegrationTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildIntegrationTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Microsoft.AspNetCore.Testing; using Xunit; diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs similarity index 99% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs index e8f1a02d40f..424caeda2d8 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmBuildLazyLoadTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmBuildLazyLoadTest.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Xunit; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmCompressionTests.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmCompressionTests.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmCompressionTests.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs index 45c878ea5aa..1108d0bff94 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPublishIntegrationTest.cs +++ b/src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs @@ -5,7 +5,7 @@ using System.IO; using System.IO.Compression; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Tasks; +using Microsoft.NET.Sdk.BlazorWebAssembly; using Microsoft.AspNetCore.Testing; using Xunit; using static Microsoft.AspNetCore.Razor.Design.IntegrationTests.ServiceWorkerAssert; @@ -608,6 +608,17 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests assetsManifestPath: "custom-service-worker-assets.js"); } + [Fact] + public async Task Publish_HostedApp_WithRidSpecifiedInCLI_Works() + { + // Arrange + using var project = ProjectDirectory.Create("blazorhosted-rid", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); + project.RuntimeIdentifier = "linux-x64"; + var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:RuntimeIdentifier=linux-x64"); + + AssertRIDPublishOuput(project, result); + } + [Fact] public async Task Publish_HostedApp_WithRid_Works() { @@ -616,6 +627,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests project.RuntimeIdentifier = "linux-x64"; var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); + AssertRIDPublishOuput(project, result); + } + + private static void AssertRIDPublishOuput(ProjectDirectory project, MSBuildResult result) + { Assert.BuildPassed(result); var publishDirectory = project.PublishOutputDirectory; diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs b/src/Components/WebAssembly/Sdk/integrationtests/WasmPwaManifestTests.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Wasm/WasmPwaManifestTests.cs rename to src/Components/WebAssembly/Sdk/integrationtests/WasmPwaManifestTests.cs diff --git a/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json b/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json new file mode 100644 index 00000000000..d00e4ae9074 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/integrationtests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "methodDisplay": "method", + "shadowCopy": false, + "maxParallelThreads": -1 +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs b/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs new file mode 100644 index 00000000000..872b17aae22 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/AssetsManifestFile.cs @@ -0,0 +1,33 @@ +// 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.NET.Sdk.BlazorWebAssembly +{ +#pragma warning disable IDE1006 // Naming Styles + public class AssetsManifestFile + { + /// <summary> + /// Gets or sets a version string. + /// </summary> + public string version { get; set; } + + /// <summary> + /// Gets or sets the assets. Keys are URLs; values are base-64-formatted SHA256 content hashes. + /// </summary> + public AssetsManifestFileEntry[] assets { get; set; } + } + + public class AssetsManifestFileEntry + { + /// <summary> + /// Gets or sets the asset URL. Normally this will be relative to the application's base href. + /// </summary> + public string url { get; set; } + + /// <summary> + /// Gets or sets the file content hash. This should be the base-64-formatted SHA256 value. + /// </summary> + public string hash { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs new file mode 100644 index 00000000000..f6bf0c88f8d --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BlazorReadSatelliteAssemblyFile.cs @@ -0,0 +1,38 @@ +// 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.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorReadSatelliteAssemblyFile : Task + { + [Output] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem ReadFile { get; set; } + + public override bool Execute() + { + var document = XDocument.Load(ReadFile.ItemSpec); + SatelliteAssembly = document.Root + .Elements() + .Select(e => + { + // <Assembly Name="..." Culture="..." DestinationSubDirectory="..." /> + + var taskItem = new TaskItem(e.Attribute("Name").Value); + taskItem.SetMetadata("Culture", e.Attribute("Culture").Value); + taskItem.SetMetadata("DestinationSubDirectory", e.Attribute("DestinationSubDirectory").Value); + + return taskItem; + }).ToArray(); + + return true; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs new file mode 100644 index 00000000000..92fd8d33e87 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BlazorWriteSatelliteAssemblyFile.cs @@ -0,0 +1,53 @@ +// 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.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorWriteSatelliteAssemblyFile : Task + { + [Required] + public ITaskItem[] SatelliteAssembly { get; set; } + + [Required] + public ITaskItem WriteFile { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(WriteFile.ItemSpec); + WriteSatelliteAssemblyFile(fileStream); + return true; + } + + internal void WriteSatelliteAssemblyFile(Stream stream) + { + var root = new XElement("SatelliteAssembly"); + + foreach (var item in SatelliteAssembly) + { + // <Assembly Name="..." Culture="..." DestinationSubDirectory="..." /> + + root.Add(new XElement("Assembly", + new XAttribute("Name", item.ItemSpec), + new XAttribute("Culture", item.GetMetadata("Culture")), + new XAttribute("DestinationSubDirectory", item.GetMetadata("DestinationSubDirectory")))); + } + + var xmlWriterSettings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true + }; + + using var writer = XmlWriter.Create(stream, xmlWriterSettings); + var xDocument = new XDocument(root); + + xDocument.Save(writer); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/BootJsonData.cs b/src/Components/WebAssembly/Sdk/src/BootJsonData.cs new file mode 100644 index 00000000000..b3a2dbd3884 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BootJsonData.cs @@ -0,0 +1,85 @@ +// 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.Runtime.Serialization; +using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ +#pragma warning disable IDE1006 // Naming Styles + /// <summary> + /// Defines the structure of a Blazor boot JSON file + /// </summary> + public class BootJsonData + { + /// <summary> + /// Gets the name of the assembly with the application entry point + /// </summary> + public string entryAssembly { get; set; } + + /// <summary> + /// Gets the set of resources needed to boot the application. This includes the transitive + /// closure of .NET assemblies (including the entrypoint assembly), the dotnet.wasm file, + /// and any PDBs to be loaded. + /// + /// Within <see cref="ResourceHashesByNameDictionary"/>, dictionary keys are resource names, + /// and values are SHA-256 hashes formatted in prefixed base-64 style (e.g., 'sha256-abcdefg...') + /// as used for subresource integrity checking. + /// </summary> + public ResourcesData resources { get; set; } = new ResourcesData(); + + /// <summary> + /// Gets a value that determines whether to enable caching of the <see cref="resources"/> + /// inside a CacheStorage instance within the browser. + /// </summary> + public bool cacheBootResources { get; set; } + + /// <summary> + /// Gets a value that determines if this is a debug build. + /// </summary> + public bool debugBuild { get; set; } + + /// <summary> + /// Gets a value that determines if the linker is enabled. + /// </summary> + public bool linkerEnabled { get; set; } + + /// <summary> + /// Config files for the application + /// </summary> + public List<string> config { get; set; } + } + + public class ResourcesData + { + /// <summary> + /// .NET Wasm runtime resources (dotnet.wasm, dotnet.js) etc. + /// </summary> + public ResourceHashesByNameDictionary runtime { get; set; } = new ResourceHashesByNameDictionary(); + + /// <summary> + /// "assembly" (.dll) resources + /// </summary> + public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary(); + + /// <summary> + /// "debug" (.pdb) resources + /// </summary> + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary pdb { get; set; } + + /// <summary> + /// localization (.satellite resx) resources + /// </summary> + [DataMember(EmitDefaultValue = false)] + public Dictionary<string, ResourceHashesByNameDictionary> satelliteResources { get; set; } + + /// <summary> + /// Assembly (.dll) resources that are loaded lazily during runtime + /// </summary> + [DataMember(EmitDefaultValue = false)] + public ResourceHashesByNameDictionary lazyAssembly { get; set; } + } +#pragma warning restore IDE1006 // Naming Styles +} diff --git a/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs b/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs new file mode 100644 index 00000000000..89b3004a5ed --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/BrotliCompress.cs @@ -0,0 +1,125 @@ +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BrotliCompress : ToolTask + { + private static readonly char[] InvalidPathChars = Path.GetInvalidFileNameChars(); + private string _dotnetPath; + + [Required] + public ITaskItem[] FilesToCompress { get; set; } + + [Output] + public ITaskItem[] CompressedFiles { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + public string CompressionLevel { get; set; } + + public bool SkipIfOutputIsNewer { get; set; } + + [Required] + public string ToolAssembly { get; set; } + + protected override string ToolName => Path.GetDirectoryName(DotNetPath); + + private string DotNetPath + { + get + { + if (!string.IsNullOrEmpty(_dotnetPath)) + { + return _dotnetPath; + } + + _dotnetPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"); + if (string.IsNullOrEmpty(_dotnetPath)) + { + throw new InvalidOperationException("DOTNET_HOST_PATH is not set"); + } + + return _dotnetPath; + } + } + + protected override string GenerateCommandLineCommands() => ToolAssembly; + + protected override string GenerateResponseFileCommands() + { + var builder = new StringBuilder(); + + + builder.AppendLine("brotli"); + + if (!string.IsNullOrEmpty(CompressionLevel)) + { + builder.AppendLine("-c"); + builder.AppendLine(CompressionLevel); + } + + CompressedFiles = new ITaskItem[FilesToCompress.Length]; + + for (var i = 0; i < FilesToCompress.Length; i++) + { + var input = FilesToCompress[i]; + var inputFullPath = input.GetMetadata("FullPath"); + var relativePath = input.GetMetadata("RelativePath"); + var outputRelativePath = Path.Combine(OutputDirectory, CalculateTargetPath(inputFullPath, ".br")); + var outputFullPath = Path.GetFullPath(outputRelativePath); + + var outputItem = new TaskItem(outputRelativePath); + outputItem.SetMetadata("RelativePath", relativePath + ".br"); + CompressedFiles[i] = outputItem; + + if (SkipIfOutputIsNewer && File.Exists(outputFullPath) && File.GetLastWriteTimeUtc(inputFullPath) < File.GetLastWriteTimeUtc(outputFullPath)) + { + Log.LogMessage(MessageImportance.Low, $"Skipping compression for '{input.ItemSpec}' because '{outputRelativePath}' is newer than '{input.ItemSpec}'."); + continue; + } + + builder.AppendLine("-s"); + builder.AppendLine(inputFullPath); + + builder.AppendLine("-o"); + builder.AppendLine(outputFullPath); + } + + return builder.ToString(); + } + + internal static string CalculateTargetPath(string relativePath, string extension) + { + // RelativePath can be long and if used as-is to write the output, might result in long path issues on Windows. + // Instead we'll calculate a fixed length path by hashing the input file name. This uses SHA1 similar to the Hash task in MSBuild + // since it has no crytographic significance. + using var hash = SHA1.Create(); + var bytes = Encoding.UTF8.GetBytes(relativePath); + var hashString = Convert.ToBase64String(hash.ComputeHash(bytes)); + + var builder = new StringBuilder(); + + for (var i = 0; i < 8; i++) + { + var c = hashString[i]; + builder.Append(InvalidPathChars.Contains(c) ? '+' : c); + } + + builder.Append(extension); + return builder.ToString(); + } + + protected override string GenerateFullPathToTool() => DotNetPath; + } +} diff --git a/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs b/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs new file mode 100644 index 00000000000..f14ffad2219 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/CreateBlazorTrimmerRootDescriptorFile.cs @@ -0,0 +1,79 @@ +// 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.Linq; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + // Based on https://github.com/mono/linker/blob/3b329b9481e300bcf4fb88a2eebf8cb5ef8b323b/src/ILLink.Tasks/CreateRootDescriptorFile.cs + public class CreateBlazorTrimmerRootDescriptorFile : Task + { + [Required] + public ITaskItem[] Assemblies { get; set; } + + [Required] + public ITaskItem TrimmerFile { get; set; } + + public override bool Execute() + { + var rootDescriptor = CreateRootDescriptorContents(); + if (File.Exists(TrimmerFile.ItemSpec)) + { + var existing = File.ReadAllText(TrimmerFile.ItemSpec); + + if (string.Equals(rootDescriptor, existing, StringComparison.Ordinal)) + { + Log.LogMessage(MessageImportance.Low, "Skipping write to file {0} because contents would not change.", TrimmerFile.ItemSpec); + // Avoid writing if the file contents are identical. This is required for build incrementalism. + return !Log.HasLoggedErrors; + } + } + + File.WriteAllText(TrimmerFile.ItemSpec, rootDescriptor); + return !Log.HasLoggedErrors; + } + + internal string CreateRootDescriptorContents() + { + var roots = new XElement("linker"); + foreach (var assembly in Assemblies.OrderBy(a => a.ItemSpec)) + { + // NOTE: Descriptor files don't include the file extension + // in the assemblyName. + var assemblyName = assembly.GetMetadata("FileName"); + var typePreserved = assembly.GetMetadata("Preserve"); + var typeRequired = assembly.GetMetadata("Required"); + + var attributes = new List<XAttribute> + { + new XAttribute("fullname", "*"), + new XAttribute("required", typeRequired), + }; + + if (!string.IsNullOrEmpty(typePreserved)) + { + attributes.Add(new XAttribute("preserve", typePreserved)); + } + + roots.Add(new XElement("assembly", + new XAttribute("fullname", assemblyName), + new XElement("type", attributes))); + } + + var xmlWriterSettings = new XmlWriterSettings + { + Indent = true, + OmitXmlDeclaration = true + }; + + return new XDocument(roots).Root.ToString(); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GZipCompress.cs b/src/Components/WebAssembly/Sdk/src/GZipCompress.cs new file mode 100644 index 00000000000..4b8a0c542a1 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GZipCompress.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; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GZipCompress : Task + { + [Required] + public ITaskItem[] FilesToCompress { get; set; } + + [Output] + public ITaskItem[] CompressedFiles { get; set; } + + [Required] + public string OutputDirectory { get; set; } + + public override bool Execute() + { + CompressedFiles = new ITaskItem[FilesToCompress.Length]; + + Directory.CreateDirectory(OutputDirectory); + + System.Threading.Tasks.Parallel.For(0, FilesToCompress.Length, i => + { + var file = FilesToCompress[i]; + var inputPath = file.ItemSpec; + var relativePath = file.GetMetadata("RelativePath"); + var outputRelativePath = Path.Combine( + OutputDirectory, + BrotliCompress.CalculateTargetPath(relativePath, ".gz")); + + var outputItem = new TaskItem(outputRelativePath); + outputItem.SetMetadata("RelativePath", relativePath + ".gz"); + CompressedFiles[i] = outputItem; + + if (File.Exists(outputRelativePath) && File.GetLastWriteTimeUtc(inputPath) < File.GetLastWriteTimeUtc(outputRelativePath)) + { + // Incrementalism. If input source doesn't exist or it exists and is not newer than the expected output, do nothing. + Log.LogMessage(MessageImportance.Low, $"Skipping '{inputPath}' because '{outputRelativePath}' is newer than '{inputPath}'."); + return; + } + + try + { + using var sourceStream = File.OpenRead(inputPath); + using var fileStream = File.Create(outputRelativePath); + using var stream = new GZipStream(fileStream, CompressionLevel.Optimal); + + sourceStream.CopyTo(stream); + } + catch (Exception e) + { + Log.LogErrorFromException(e); + return; + } + }); + + return !Log.HasLoggedErrors; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs b/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs new file mode 100644 index 00000000000..81cb6ecb96d --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GenerateBlazorWebAssemblyBootJson.cs @@ -0,0 +1,155 @@ +// 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.Linq; +using System.Reflection; +using System.Runtime.Serialization.Json; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using ResourceHashesByNameDictionary = System.Collections.Generic.Dictionary<string, string>; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GenerateBlazorWebAssemblyBootJson : Task + { + [Required] + public string AssemblyPath { get; set; } + + [Required] + public ITaskItem[] Resources { get; set; } + + [Required] + public bool DebugBuild { get; set; } + + [Required] + public bool LinkerEnabled { get; set; } + + [Required] + public bool CacheBootResources { get; set; } + + public ITaskItem[] ConfigurationFiles { get; set; } + + [Required] + public string OutputPath { get; set; } + + public ITaskItem[] LazyLoadedAssemblies { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(OutputPath); + var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name; + + try + { + WriteBootJson(fileStream, entryAssemblyName); + } + catch (Exception ex) + { + Log.LogErrorFromException(ex); + } + + return !Log.HasLoggedErrors; + } + + // Internal for tests + public void WriteBootJson(Stream output, string entryAssemblyName) + { + var result = new BootJsonData + { + entryAssembly = entryAssemblyName, + cacheBootResources = CacheBootResources, + debugBuild = DebugBuild, + linkerEnabled = LinkerEnabled, + resources = new ResourcesData(), + config = new List<string>(), + }; + + // Build a two-level dictionary of the form: + // - assembly: + // - UriPath (e.g., "System.Text.Json.dll") + // - ContentHash (e.g., "4548fa2e9cf52986") + // - runtime: + // - UriPath (e.g., "dotnet.js") + // - ContentHash (e.g., "3448f339acf512448") + if (Resources != null) + { + var resourceData = result.resources; + foreach (var resource in Resources) + { + ResourceHashesByNameDictionary resourceList; + + var fileName = resource.GetMetadata("FileName"); + var extension = resource.GetMetadata("Extension"); + var resourceCulture = resource.GetMetadata("Culture"); + var assetType = resource.GetMetadata("AssetType"); + var resourceName = $"{fileName}{extension}"; + + if (IsLazyLoadedAssembly(fileName)) + { + resourceData.lazyAssembly ??= new ResourceHashesByNameDictionary(); + resourceList = resourceData.lazyAssembly; + } + else if (!string.IsNullOrEmpty(resourceCulture)) + { + resourceData.satelliteResources ??= new Dictionary<string, ResourceHashesByNameDictionary>(StringComparer.OrdinalIgnoreCase); + resourceName = resourceCulture + "/" + resourceName; + + if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList)) + { + resourceList = new ResourceHashesByNameDictionary(); + resourceData.satelliteResources.Add(resourceCulture, resourceList); + } + } + else if (string.Equals(extension, ".pdb", StringComparison.OrdinalIgnoreCase)) + { + resourceData.pdb ??= new ResourceHashesByNameDictionary(); + resourceList = resourceData.pdb; + } + else if (string.Equals(extension, ".dll", StringComparison.OrdinalIgnoreCase)) + { + resourceList = resourceData.assembly; + } + else if (string.Equals(assetType, "native", StringComparison.OrdinalIgnoreCase)) + { + resourceList = resourceData.runtime; + } + else + { + // This should include items such as XML doc files, which do not need to be recorded in the manifest. + continue; + } + + if (!resourceList.ContainsKey(resourceName)) + { + resourceList.Add(resourceName, $"sha256-{resource.GetMetadata("FileHash")}"); + } + } + } + + if (ConfigurationFiles != null) + { + foreach (var configFile in ConfigurationFiles) + { + result.config.Add(Path.GetFileName(configFile.ItemSpec)); + } + } + + var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings + { + UseSimpleDictionaryFormat = true + }); + + using var writer = JsonReaderWriterFactory.CreateJsonWriter(output, Encoding.UTF8, ownsStream: false, indent: true); + serializer.WriteObject(writer, result); + } + + private bool IsLazyLoadedAssembly(string fileName) + { + return LazyLoadedAssemblies != null && LazyLoadedAssemblies.Any(a => a.ItemSpec == fileName); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs b/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs new file mode 100644 index 00000000000..08c75a927af --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/GenerateServiceWorkerAssetsManifest.cs @@ -0,0 +1,97 @@ +// 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 System.Linq; +using System.Runtime.Serialization.Json; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public partial class GenerateServiceWorkerAssetsManifest : Task + { + [Required] + public ITaskItem[] Assets { get; set; } + + public string Version { get; set; } + + [Required] + public string OutputPath { get; set; } + + [Output] + public string CalculatedVersion { get; set; } + + public override bool Execute() + { + using var fileStream = File.Create(OutputPath); + CalculatedVersion = GenerateAssetManifest(fileStream); + + return true; + } + + internal string GenerateAssetManifest(Stream stream) + { + var assets = new AssetsManifestFileEntry[Assets.Length]; + System.Threading.Tasks.Parallel.For(0, assets.Length, i => + { + var item = Assets[i]; + var hash = item.GetMetadata("FileHash"); + var url = item.GetMetadata("AssetUrl"); + + if (string.IsNullOrEmpty(hash)) + { + // Some files that are part of the service worker manifest may not have their hashes previously + // calcualted. Calculate them at this time. + using var sha = SHA256.Create(); + using var file = File.OpenRead(item.ItemSpec); + var bytes = sha.ComputeHash(file); + + hash = Convert.ToBase64String(bytes); + } + + assets[i] = new AssetsManifestFileEntry + { + hash = "sha256-" + hash, + url = url, + }; + }); + + var version = Version; + if (string.IsNullOrEmpty(version)) + { + // If a version isn't specified (which is likely the most common case), construct a Version by combining + // the file names + hashes of all the inputs. + + var combinedHash = string.Join( + Environment.NewLine, + assets.OrderBy(f => f.url, StringComparer.Ordinal).Select(f => f.hash)); + + using var sha = SHA256.Create(); + var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(combinedHash)); + version = Convert.ToBase64String(bytes).Substring(0, 8); + } + + var data = new AssetsManifestFile + { + version = version, + assets = assets, + }; + + using var streamWriter = new StreamWriter(stream, Encoding.UTF8, bufferSize: 50, leaveOpen: true); + streamWriter.Write("self.assetsManifest = "); + streamWriter.Flush(); + + using var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, ownsStream: false, indent: true); + new DataContractJsonSerializer(typeof(AssetsManifestFile)).WriteObject(jsonWriter, data); + jsonWriter.Flush(); + + streamWriter.WriteLine(";"); + + return version; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj new file mode 100644 index 00000000000..adf7330a3fe --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.csproj @@ -0,0 +1,89 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <Description>MSBuild support for building Blazor WebAssembly apps.</Description> + <TargetFrameworks>$(DefaultNetCoreTargetFramework);net46</TargetFrameworks> + + <TargetName>Microsoft.NET.Sdk.BlazorWebAssembly.Tasks</TargetName> + <NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile> + <Serviceable>true</Serviceable> + <SdkOutputPath>$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\sdk-output\</SdkOutputPath> + + <!-- Allow assemblies outside of lib in the package --> + <NoWarn>$(NoWarn);NU5100</NoWarn> + <!-- Need to build this project in source build --> + <ExcludeFromSourceBuild>false</ExcludeFromSourceBuild> + </PropertyGroup> + + <ItemGroup> + <Reference Include="Microsoft.Build.Framework" /> + <Reference Include="Microsoft.Build.Utilities.Core" /> + + <ProjectReference + Include="..\tools\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj" + Targets="Publish" + ReferenceOutputAssembly="false" + IsImplicityDefined="false" + SkipGetTargetFrameworkProperties="true" + UndefineProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier;PublishDir" /> + </ItemGroup> + + <ItemGroup> + <Content Include="_._" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <Target Name="LayoutDependencies" BeforeTargets="Build" + Condition="'$(IsInnerBuild)' != 'true' AND '$(NoBuild)' != 'true'"> + <!-- Layout tasks, compiler, and extensions in the sdk-output folder. The entire folder structure gets packaged as-is into the SDK --> + + <PropertyGroup Condition="'$(ContinuousIntegrationBuild)' != 'true'"> + <_ContinueOnError>true</_ContinueOnError> + <_Retries>1</_Retries> + </PropertyGroup> + + <PropertyGroup Condition="'$(ContinuousIntegrationBuild)' == 'true'"> + <_ContinueOnError>false</_ContinueOnError> + <_Retries>10</_Retries> + </PropertyGroup> + + <ItemGroup> + <_WebAssemblyToolsOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly.Tools\$(Configuration)\$(DefaultNetCoreTargetFramework)\publish\Microsoft.*" /> + </ItemGroup> + + <Error + Text="WebAssembly SDK tools outputs were not found in $(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly.Tools\$(Configuration)\$(DefaultNetCoreTargetFramework)\publish" + Condition="'@(_WebAssemblyToolsOutput->Count())' == '0'" /> + + <Copy + SourceFiles="@(_WebAssemblyToolsOutput)" + DestinationFolder="$(SdkOutputPath)tools\$(DefaultNetCoreTargetFramework)\" + SkipUnchangedFiles="true" + Retries="$(_Retries)" + ContinueOnError="$(_ContinueOnError)" /> + + <ItemGroup> + <ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\net46*\Microsoft.NET.Sdk.BlazorWebAssembly.*" /> + <ProjectOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\$(DefaultNetCoreTargetFramework)*\Microsoft.NET.Sdk.BlazorWebAssembly.*" /> + </ItemGroup> + + <Copy SourceFiles="@(ProjectOutput)" DestinationFiles="$(SdkOutputPath)tasks\%(RecursiveDir)%(FileName)%(Extension)" SkipUnchangedFiles="true" Retries="$(_Retries)" ContinueOnError="$(_ContinueOnError)"> + <Output TaskParameter="CopiedFiles" ItemName="FileWrites" /> + </Copy> + + <Message Text="Blazor WebAssembly SDK output -> $(SdkOutputPath)" Importance="High" /> + </Target> + + <Target Name="PopulateNuspec" BeforeTargets="InitializeStandardNuspecProperties" DependsOnTargets="LayoutDependencies"> + <PropertyGroup> + <PackageTags>$(PackageTags.Replace(';',' '))</PackageTags> + </PropertyGroup> + + <ItemGroup> + <NuspecProperty Include="outputPath=$(OutputPath)\sdk-output" /> + </ItemGroup> + </Target> + + <!-- Workarounds to allow publishing to work when the SDK is referenced as a project. --> + <Target Name="GetTargetPath" /> + <Target Name="GetCopyToPublishDirectoryItems" /> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec new file mode 100644 index 00000000000..6d74a1931cc --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Microsoft.NET.Sdk.BlazorWebAssembly.nuspec @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> + <metadata> + $CommonMetadataElements$ + <dependencies> + <group targetFramework=".NET5.0" /> + </dependencies> + </metadata> + + <files> + $CommonFileElements$ + <file src="Sdk\*" target="Sdk" /> + <file src="build\**" target="build" /> + <file src="targets\**" target="targets" /> + <file src="_._" target="lib\net5.0\_._" /> + + <file src="$outputPath$\**" target="\" /> + </files> +</package> diff --git a/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props new file mode 100644 index 00000000000..3f6870441f4 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.props @@ -0,0 +1,22 @@ +<!-- +*********************************************************************************************** +Sdk.props + +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" TreatAsLocalProperty="RuntimeIdentifier"> + <PropertyGroup> + <UsingMicrosoftNETSdkBlazorWebAssembly>true</UsingMicrosoftNETSdkBlazorWebAssembly> + </PropertyGroup> + + <PropertyGroup> + <_BlazorWebAssemblyPropsFile Condition="'$(_BlazorWebAssemblyPropsFile)' == ''">$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.props</_BlazorWebAssemblyPropsFile> + </PropertyGroup> + + <Import Project="$(_BlazorWebAssemblyPropsFile)" /> +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets new file mode 100644 index 00000000000..616c56fb8a0 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/Sdk/Sdk.targets @@ -0,0 +1,20 @@ +<!-- +*********************************************************************************************** +Sdk.targets + +WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have + created a backup copy. Incorrect changes to this file will make it + impossible to load or build your projects from the command-line or the IDE. + +Copyright (c) .NET Foundation. All rights reserved. +*********************************************************************************************** +--> +<Project ToolsVersion="14.0"> + + <PropertyGroup> + <_BlazorWebAssemblyTargetsFile Condition="'$(_BlazorWebAssemblyTargetsFile)' == ''">$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets</_BlazorWebAssemblyTargetsFile> + </PropertyGroup> + + <Import Project="$(_BlazorWebAssemblyTargetsFile)" /> + +</Project> diff --git a/src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Components/WebAssembly/Sdk/src/_._ similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js rename to src/Components/WebAssembly/Sdk/src/_._ diff --git a/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props new file mode 100644 index 00000000000..27e3fde3e00 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.props @@ -0,0 +1,16 @@ +<!-- +*********************************************************************************************** +Microsoft.NET.Sdk.BlazorWebAssembly.props + +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"> + <PropertyGroup> + <_BlazorWebAssemblyPropsFile>$(MSBuildThisFileDirectory)..\..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.props</_BlazorWebAssemblyPropsFile> + </PropertyGroup> +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets new file mode 100644 index 00000000000..8091b3d876f --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/build/net5.0/Microsoft.NET.Sdk.BlazorWebAssembly.targets @@ -0,0 +1,16 @@ +<!-- +*********************************************************************************************** +Microsoft.NET.Sdk.BlazorWebAssembly.props + +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"> + <PropertyGroup> + <_BlazorWebAssemblyTargetsFile>$(MSBuildThisFileDirectory)..\..\targets\Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets</_BlazorWebAssemblyTargetsFile> + </PropertyGroup> +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config b/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config new file mode 100644 index 00000000000..7f9995d792c --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/BlazorWasm.web.config @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <system.webServer> + <staticContent> + <remove fileExtension=".dat" /> + <remove fileExtension=".dll" /> + <remove fileExtension=".json" /> + <remove fileExtension=".wasm" /> + <remove fileExtension=".woff" /> + <remove fileExtension=".woff2" /> + <mimeMap fileExtension=".dll" mimeType="application/octet-stream" /> + <mimeMap fileExtension=".dat" mimeType="application/octet-stream" /> + <mimeMap fileExtension=".json" mimeType="application/json" /> + <mimeMap fileExtension=".wasm" mimeType="application/wasm" /> + <mimeMap fileExtension=".woff" mimeType="application/font-woff" /> + <mimeMap fileExtension=".woff2" mimeType="application/font-woff" /> + </staticContent> + <httpCompression> + <dynamicTypes> + <add mimeType="application/octet-stream" enabled="true" /> + <add mimeType="application/wasm" enabled="true" /> + </dynamicTypes> + </httpCompression> + <rewrite> + <rules> + <rule name="Serve subdir"> + <match url=".*" /> + <action type="Rewrite" url="wwwroot\{R:0}" /> + </rule> + <rule name="SPA fallback routing" stopProcessing="true"> + <match url=".*" /> + <conditions logicalGrouping="MatchAll"> + <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> + </conditions> + <action type="Rewrite" url="wwwroot\" /> + </rule> + </rules> + </rewrite> + </system.webServer> +</configuration> diff --git a/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml b/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml new file mode 100644 index 00000000000..c61bc7a3cca --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/LinkerWorkaround.xml @@ -0,0 +1,15 @@ +<linker> + + <!-- This file specifies which parts of the BCL or Blazor packages must not be stripped + by the IL linker even if they are not referenced by user code. The file format is + described at https://github.com/mono/linker/blob/master/src/linker/README.md#syntax-of-xml-descriptor --> + + <assembly fullname="System"> + <!-- Without this, [Required(typeof(bool), "true", "true", ErrorMessage = "...")] fails --> + <type fullname="System.ComponentModel.BooleanConverter" /> + + <!-- TypeConverters are only used through reflection. These are two built-in TypeConverters that are useful. --> + <type fullname="System.ComponentModel.GuidConverter" /> + <type fullname="System.ComponentModel.TimeSpanConverter" /> + </assembly> +</linker> diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props new file mode 100644 index 00000000000..03b94ad5666 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.props @@ -0,0 +1,36 @@ +<!-- +*********************************************************************************************** +Microsoft.NET.Sdk.BlazorWebAssembly.props + +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" TreatAsLocalProperty="RuntimeIdentifier"> + <PropertyGroup> + <!-- Blazor WASM projects are always browser-wasm --> + <RuntimeIdentifier>browser-wasm</RuntimeIdentifier> + + <!-- Avoid having the rid show up in output paths --> + <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> + + <OutputType>exe</OutputType> + + <IsPackable>false</IsPackable> + + <WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject> + </PropertyGroup> + + <PropertyGroup> + <!-- Determines if this Sdk is responsible for importing Microsoft.NET.Sdk.Razor. Temporary workaround until we can create a SDK. --> + <_RazorSdkImportsMicrosoftNetSdkRazor Condition="'$(UsingMicrosoftNETSdkRazor)' != 'true'">true</_RazorSdkImportsMicrosoftNetSdkRazor> + </PropertyGroup> + + <Import Sdk="Microsoft.NET.Sdk.Razor" Project="Sdk.props" Condition="'$(_RazorSdkImportsMicrosoftNetSdkRazor)' == 'true'" /> + <Import Sdk="Microsoft.NET.Sdk.Web.ProjectSystem" Project="Sdk.props" /> + <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.props" /> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets new file mode 100644 index 00000000000..a11bc00625c --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets @@ -0,0 +1,582 @@ +<!-- +*********************************************************************************************** +Microsoft.NET.Sdk.BlazorWebAssembly.targets + +WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have + created a backup copy. Incorrect changes to this file will make it + impossible to load or build your projects from the command-line or the IDE. + +Copyright (c) .NET Foundation. All rights reserved. +*********************************************************************************************** +--> +<Project ToolsVersion="14.0"> + + <PropertyGroup> + <EnableDefaultContentItems Condition=" '$(EnableDefaultContentItems)' == '' ">true</EnableDefaultContentItems> + </PropertyGroup> + + <Import Sdk="Microsoft.NET.Sdk.Razor" Project="Sdk.targets" Condition="'$(_RazorSdkImportsMicrosoftNetSdkRazor)' == 'true'" /> + <Import Sdk="Microsoft.NET.Sdk.Web.ProjectSystem" Project="Sdk.targets" /> + <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.targets" /> + + <!-- + Targets supporting Razor MSBuild integration. Contain support for generating C# code using Razor + and including the generated code in the project lifecycle, including compiling, publishing and producing + nuget packages. + --> + + <!-- + This is a hook to import a set of targets before the Blazor targets. By default this is unused. + --> + <Import Project="$(CustomBeforeBlazorWebAssemblySdkTargets)" Condition="'$(CustomBeforeBlazorWebAssemblySdkTargets)' != '' and Exists('$(CustomBeforeBlazorWebAssemblySdkTargets)')"/> + + <PropertyGroup> + <!-- Paths to tools, tasks, and extensions are calculated relative to the BlazorWebAssemblySdkDirectoryRoot. This can be modified to test a local build. --> + <BlazorWebAssemblySdkDirectoryRoot Condition="'$(BlazorWebAssemblySdkDirectoryRoot)'==''">$(MSBuildThisFileDirectory)..\..\</BlazorWebAssemblySdkDirectoryRoot> + <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net5.0</_BlazorWebAssemblySdkTasksTFM> + <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net46</_BlazorWebAssemblySdkTasksTFM> + <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tasks\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll</_BlazorWebAssemblySdkTasksAssembly> + <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net5.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tools.dll</_BlazorWebAssemblySdkToolAssembly> + </PropertyGroup> + + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GenerateBlazorWebAssemblyBootJson" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BlazorWriteSatelliteAssemblyFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BlazorReadSatelliteAssemblyFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BrotliCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GzipCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + + <PropertyGroup> + <SelfContained>true</SelfContained> + <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> + + <!-- Trimmer defaults --> + <PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed> + <TrimMode Condition="'$(TrimMode)' == ''">link</TrimMode> + + <StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">/</StaticWebAssetBasePath> + <BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources> + + <!-- Turn off parts of the build that do not apply to WASM projects --> + <GenerateDependencyFile>false</GenerateDependencyFile> + <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles> + <PreserveCompilationContext>false</PreserveCompilationContext> + <PreserveCompilationReferences>false</PreserveCompilationReferences> + <IsWebConfigTransformDisabled>true</IsWebConfigTransformDisabled> + + <!-- Internal properties --> + <_BlazorOutputPath>wwwroot\_framework\</_BlazorOutputPath> + + </PropertyGroup> + + <Import Project="Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets" /> + + <Target Name="_ScrambleDotnetJsFileName" AfterTargets="ResolveRuntimePackAssets"> + <!-- + We want the dotnet.js file output to have a version to better work with caching. We'll append the runtime version to the file name as soon as file has been discovered. + --> + <PropertyGroup> + <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)</_DotNetJsVersion> + <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)</_DotNetJsVersion> + <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js</_BlazorDotnetJsFileName> + <_BlazorDotNetJsFilePath>$(IntermediateOutputPath)$(_BlazorDotnetJsFileName)</_BlazorDotNetJsFilePath> + </PropertyGroup> + + <ItemGroup> + <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" /> + </ItemGroup> + + <Copy + SourceFiles="@(_DotNetJsItem)" + DestinationFiles="$(_BlazorDotNetJsFilePath)" + SkipUnchangedFiles="true" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" /> + + <ItemGroup Condition="'@(_DotNetJsItem->Count())' != '0'"> + <ReferenceCopyLocalPaths + Include="$(_BlazorDotNetJsFilePath)" + AssetType="native" + CopyLocal="true" + DestinationSubPath="$(_BlazorDotnetJsFileName)" /> + + <ReferenceCopyLocalPaths Remove="@(_DotNetJsItem)" /> + </ItemGroup> + </Target> + + <Target Name="_ResolveBlazorWasmOutputs" DependsOnTargets="ResolveReferences;PrepareResourceNames;ComputeIntermediateSatelliteAssemblies"> + <!-- + Calculates the outputs and the paths for Blazor WASM. This target is invoked frequently and should perform minimal work. + --> + + <PropertyGroup> + <_BlazorSatelliteAssemblyCacheFile>$(IntermediateOutputPath)blazor.satelliteasm.props</_BlazorSatelliteAssemblyCacheFile> + <!-- Workaround for https://github.com/dotnet/sdk/issues/12114--> + <PublishDir Condition="'$(AppendRuntimeIdentifierToOutputPath)' != 'true' AND '$(PublishDir)' == '$(OutputPath)$(RuntimeIdentifier)\$(PublishDirName)\'">$(OutputPath)$(PublishDirName)\</PublishDir> + </PropertyGroup> + + <ItemGroup> + <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" /> + <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" /> + + <_BlazorConfigFile Include="wwwroot\appsettings*.json" /> + + <!-- Clear out temporary build artifacts that the runtime packages --> + <ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.a'" /> + + <!-- + ReferenceCopyLocalPaths includes satellite assemblies from referenced projects but are inexpicably missing + any metadata that might allow them to be differentiated. We'll explicitly add those + to _BlazorOutputWithTargetPath so that satellite assemblies from packages, the current project and referenced project + are all treated the same. + --> + + <_BlazorCopyLocalPath + Include="@(ReferenceCopyLocalPaths)" + Exclude="@(ReferenceSatellitePaths)"/> + + <_BlazorCopyLocalPath Include="@(IntermediateSatelliteAssembliesWithTargetPath)"> + <DestinationSubDirectory>%(IntermediateSatelliteAssembliesWithTargetPath.Culture)\</DestinationSubDirectory> + </_BlazorCopyLocalPath> + + <_BlazorOutputWithTargetPath Include=" + @(_BlazorCopyLocalPath); + @(IntermediateAssembly); + @(_DebugSymbolsIntermediatePath); + @(_BlazorJSFile)" /> + + <_BlazorOutputWithTargetPath Include="@(ReferenceSatellitePaths)"> + <Culture>$([System.String]::Copy('%(ReferenceSatellitePaths.DestinationSubDirectory)').Trim('\').Trim('/'))</Culture> + </_BlazorOutputWithTargetPath> + </ItemGroup> + + <!-- + BuildingProject=false is typically set for referenced projects when building inside VisualStudio. + + When building with BuildingProject=false, satellite assemblies do not get resolved (the ones for the current project and the one for + referenced project). Satellite assemblies from packages get resolved. + To workaround this, we'll cache metadata during a regular build, and rehydrate from it when BuildingProject=false. + --> + <BlazorReadSatelliteAssemblyFile + ReadFile="$(_BlazorSatelliteAssemblyCacheFile)" + Condition="'$(BuildingProject)' != 'true' AND EXISTS('$(_BlazorSatelliteAssemblyCacheFile)')"> + <Output TaskParameter="SatelliteAssembly" ItemName="_BlazorReadSatelliteAssembly" /> + </BlazorReadSatelliteAssemblyFile> + + <ItemGroup> + <!-- We've imported a previously Cacheed file. Let's turn in to a _BlazorOutputWithTargetPath --> + <_BlazorOutputWithTargetPath + Include="@(_BlazorReadSatelliteAssembly)" + Exclude="@(_BlazorOutputWithTargetPath)" + Condition="'@(_BlazorReadSatelliteAssembly->Count())' != '0'" /> + + <!-- Calculate the target path --> + <_BlazorOutputWithTargetPath + TargetPath="$(_BlazorOutputPath)%(_BlazorOutputWithTargetPath.DestinationSubDirectory)%(FileName)%(Extension)" + Condition="'%(__BlazorOutputWithTargetPath.TargetPath)' == ''" /> + </ItemGroup> + </Target> + + <Target Name="_ProcessBlazorWasmOutputs" DependsOnTargets="_ResolveBlazorWasmOutputs"> + <PropertyGroup> + <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\</_BlazorBuildGZipCompressDirectory> + </PropertyGroup> + + <!-- + Compress referenced binaries using GZip during build. This skips files such as the project's assemblies + that change from build to build. Runtime assets contribute to the bulk of the download size. Compressing it + has the most benefit while avoiding any ongoing costs to the dev inner loop. + --> + <ItemGroup> + <_GzipFileToCompressForBuild + Include="@(ReferenceCopyLocalPaths)" + RelativePath="$(_BlazorOutputPath)%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)" + Condition="'%(Extension)' == '.dll' or '%(ReferenceCopyLocalPaths.AssetType)' == 'native'" /> + </ItemGroup> + + <GZipCompress + FilesToCompress="@(_GzipFileToCompressForBuild)" + OutputDirectory="$(_BlazorBuildGZipCompressDirectory)"> + + <Output TaskParameter="CompressedFiles" ItemName="_BlazorBuildGZipCompressedFile" /> + <Output TaskParameter="CompressedFiles" ItemName="FileWrites" /> + </GZipCompress> + + <ItemGroup> + <_BlazorWriteSatelliteAssembly Include="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" /> + + <!-- Retarget ReferenceCopyLocalPaths to copy to the wwwroot directory --> + <ReferenceCopyLocalPaths DestinationSubDirectory="$(_BlazorOutputPath)%(ReferenceCopyLocalPaths.DestinationSubDirectory)" /> + </ItemGroup> + + <!-- A missing blazor.webassembly.js is our packaging error. Produce an error so it's discovered early. --> + <Error + Text="Unable to find BlazorWebAssembly JS files. This usually indicates a packaging error." + Code="RAZORSDK1007" + Condition="'@(_BlazorJSFile->Count())' == '0'" /> + + <!-- + When building with BuildingProject=false, satellite assemblies do not get resolved (the ones for the current project and the one for + referenced project). BuildingProject=false is typically set for referenced projects when building inside VisualStudio. + To workaround this, we'll cache metadata during a regular build, and rehydrate from it when BuildingProject=false. + --> + + <BlazorWriteSatelliteAssemblyFile + SatelliteAssembly="@(_BlazorWriteSatelliteAssembly)" + WriteFile="$(_BlazorSatelliteAssemblyCacheFile)" + Condition="'$(BuildingProject)' == 'true' AND '@(_BlazorWriteSatelliteAssembly->Count())' != '0'" /> + + <Delete + Files="$(_BlazorSatelliteAssemblyCacheFile)" + Condition="'$(BuildingProject)' == 'true' AND '@(_BlazorWriteSatelliteAssembly->Count())' == '0' and EXISTS('$(_BlazorSatelliteAssemblyCacheFile)')" /> + + <ItemGroup> + <FileWrites Include="$(_BlazorSatelliteAssemblyCacheFile)" Condition="Exists('$(_BlazorSatelliteAssemblyCacheFile)')" /> + </ItemGroup> + + <GetFileHash Files="@(_BlazorOutputWithTargetPath)" Algorithm="SHA256" HashEncoding="base64"> + <Output TaskParameter="Items" ItemName="_BlazorOutputWithHash" /> + </GetFileHash> + </Target> + + <PropertyGroup> + <PrepareForRunDependsOn> + _BlazorWasmPrepareForRun; + $(PrepareForRunDependsOn) + </PrepareForRunDependsOn> + + <GetCurrentProjectStaticWebAssetsDependsOn> + $(GetCurrentProjectStaticWebAssetsDependsOn); + _BlazorWasmPrepareForRun; + </GetCurrentProjectStaticWebAssetsDependsOn> + </PropertyGroup> + + <Target Name="_BlazorWasmPrepareForRun" DependsOnTargets="_ProcessBlazorWasmOutputs" BeforeTargets="_RazorPrepareForRun" AfterTargets="GetCurrentProjectStaticWebAssets"> + <PropertyGroup> + <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json</_BlazorBuildBootJsonPath> + </PropertyGroup> + + <GenerateBlazorWebAssemblyBootJson + AssemblyPath="@(IntermediateAssembly)" + Resources="@(_BlazorOutputWithHash)" + DebugBuild="true" + LinkerEnabled="false" + CacheBootResources="$(BlazorCacheBootResources)" + OutputPath="$(_BlazorBuildBootJsonPath)" + ConfigurationFiles="@(_BlazorConfigFile)" + LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)" /> + + <ItemGroup> + <FileWrites Include="$(OutDir)$(_BlazorOutputPath)blazor.boot.json" /> + </ItemGroup> + + <ItemGroup> + <_BlazorWebAssemblyStaticWebAsset Include="$(_BlazorBuildBootJsonPath)"> + <SourceId>$(PackageId)</SourceId> + <SourceType></SourceType> + <ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot> + <BasePath>$(StaticWebAssetBasePath)</BasePath> + <RelativePath>_framework/blazor.boot.json</RelativePath> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </_BlazorWebAssemblyStaticWebAsset> + + <_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorOutputWithHash)"> + <SourceId>$(PackageId)</SourceId> + <SourceType></SourceType> + <ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot> + <BasePath>$(StaticWebAssetBasePath)</BasePath> + <RelativePath>$([System.String]::Copy('%(_BlazorOutputWithHash.TargetPath)').Replace('\','/').Substring(8))</RelativePath> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </_BlazorWebAssemblyStaticWebAsset> + + <_BlazorWebAssemblyStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile)"> + <SourceId>$(PackageId)</SourceId> + <SourceType></SourceType> + <ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot> + <BasePath>$(StaticWebAssetBasePath)</BasePath> + <RelativePath>$([System.String]::Copy('%(_BlazorBuildGZipCompressedFile.RelativePath)').Replace('\','/').Substring(8))</RelativePath> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </_BlazorWebAssemblyStaticWebAsset> + + <StaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" /> + <_ExternalStaticWebAsset Include="@(_BlazorWebAssemblyStaticWebAsset)" SourceType="Generated" /> + </ItemGroup> + </Target> + + <!-- Mimics the behavior of CopyFilesToOutputDirectory. We simply copy relevant build outputs to the wwwroot directory --> + <Target Name="_BlazorCopyFilesToOutputDirectory" AfterTargets="CopyFilesToOutputDirectory"> + <Copy + SourceFiles="@(IntermediateAssembly)" + DestinationFolder="$(OutDir)$(_BlazorOutputPath)" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)" + Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'"> + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + + <Message Importance="High" Text="$(MSBuildProjectName) (Blazor output) -> $(TargetDir)wwwroot" Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)'!='true'" /> + + <Copy + SourceFiles="@(_DebugSymbolsIntermediatePath)" + DestinationFolder="$(OutDir)$(_BlazorOutputPath)" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)" + Condition="'$(_DebugSymbolsProduced)'=='true' and '$(SkipCopyingSymbolsToOutputDirectory)' != 'true' and '$(CopyOutputSymbolsToOutputDirectory)'=='true'"> + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + + <Copy + SourceFiles="@(IntermediateSatelliteAssembliesWithTargetPath)" + DestinationFiles="@(IntermediateSatelliteAssembliesWithTargetPath->'$(OutDir)$(_BlazorOutputPath)%(Culture)\$(TargetName).resources.dll')" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)" + Condition="'@(IntermediateSatelliteAssembliesWithTargetPath)' != ''" > + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + + <Copy + SourceFiles="@(_BlazorJSFile);$(_BlazorBuildBootJsonPath)" + DestinationFolder="$(OutDir)$(_BlazorOutputPath)" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)"> + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + + <Copy + SourceFiles="@(_BlazorBuildGZipCompressedFile)" + DestinationFiles="@(_BlazorBuildGZipCompressedFile->'$(OutDir)%(RelativePath)')" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)"> + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + </Target> + + <Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink"> + <PropertyGroup> + <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile> + </PropertyGroup> + + <ItemGroup> + <_BlazorTypeGranularAssembly + Include="@(ManagedAssemblyToLink)" + Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))"> + <Required>false</Required> + <Preserve>all</Preserve> + </_BlazorTypeGranularAssembly> + + <ManagedAssemblyToLink + IsTrimmable="true" + Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))" /> + </ItemGroup> + + <CreateBlazorTrimmerRootDescriptorFile + Assemblies="@(_BlazorTypeGranularAssembly)" + TrimmerFile="$(_BlazorTypeGranularTrimmerDescriptorFile)" /> + + <ItemGroup> + <TrimmerRootDescriptor Include="$(_BlazorTypeGranularTrimmerDescriptorFile)" /> + <TrimmerRootDescriptor Include="$(MSBuildThisFileDirectory)LinkerWorkaround.xml" /> + + <FileWrites Include="$(_BlazorTypeGranularTrimmerDescriptorFile)" /> + </ItemGroup> + </Target> + + <Target Name="_ProcessPublishFilesForBlazor" DependsOnTargets="_ResolveBlazorWasmOutputs" AfterTargets="ILLink"> + + <!-- + ResolvedFileToPublish.Culture is missing for satellite assemblies from project references. + Since we need the culture to correctly generate blazor.boot.json, we cross-reference the culture we calculate as part of _ResolveBlazorWasmOutputs + --> + <JoinItems Left="@(ResolvedFileToPublish)" + Right="@(_BlazorOutputWithTargetPath->HasMetadata('Culture'))" + LeftMetadata="*" + RightMetadata="Culture" + ItemSpecToUse="Left"> + <Output TaskParameter="JoinResult" ItemName="_ResolvedSatelliteToPublish" /> + </JoinItems> + + <ItemGroup> + <ResolvedFileToPublish Remove="@(_ResolvedSatelliteToPublish)" /> + <ResolvedFileToPublish Include="@(_ResolvedSatelliteToPublish)" /> + + <ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'%(Extension)' == '.a'" /> + + <!-- Remove dotnet.js from publish output --> + <ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'%(ResolvedFileToPublish.RelativePath)' == 'dotnet.js'" /> + + <!-- Retarget so that items are published to the wwwroot directory --> + <ResolvedFileToPublish + RelativePath="$(_BlazorOutputPath)%(ResolvedFileToPublish.RelativePath)" + Condition="'%(ResolvedFileToPublish.RelativePath)' != 'web.config' AND !$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" /> + + <!-- Remove pdbs from the publish output --> + <ResolvedFileToPublish Remove="@(ResolvedFileToPublish)" Condition="'$(BlazorWebAssemblyEnableDebugging)' != 'true' AND '%(Extension)' == '.pdb'" /> + </ItemGroup> + + <ItemGroup Condition="'@(ResolvedFileToPublish->AnyHaveMetadataValue('RelativePath', 'web.config'))' != 'true'"> + <ResolvedFileToPublish + Include="$(MSBuildThisFileDirectory)BlazorWasm.web.config" + ExcludeFromSingleFile="true" + CopyToPublishDirectory="PreserveNewest" + RelativePath="web.config" /> + </ItemGroup> + + <!-- Generate the publish boot json --> + <ItemGroup> + <_BlazorPublishBootResource + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(RelativePath)').Replace('\','/').StartsWith('wwwroot/_framework')) AND '%(Extension)' != '.a'" /> + </ItemGroup> + + <GetFileHash Files="@(_BlazorPublishBootResource)" Algorithm="SHA256" HashEncoding="base64"> + <Output TaskParameter="Items" ItemName="_BlazorPublishBootResourceWithHash" /> + </GetFileHash> + + <GenerateBlazorWebAssemblyBootJson + AssemblyPath="@(IntermediateAssembly)" + Resources="@(_BlazorPublishBootResourceWithHash)" + DebugBuild="false" + LinkerEnabled="$(PublishTrimmed)" + CacheBootResources="$(BlazorCacheBootResources)" + OutputPath="$(IntermediateOutputPath)blazor.publish.boot.json" + ConfigurationFiles="@(_BlazorConfigFile)" + LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)" /> + + <ItemGroup> + <ResolvedFileToPublish + Include="$(IntermediateOutputPath)blazor.publish.boot.json" + RelativePath="$(_BlazorOutputPath)blazor.boot.json" /> + + <ResolvedFileToPublish + Include="@(_BlazorJSFile)" + RelativePath="$(_BlazorOutputPath)%(FileName)%(Extension)" /> + </ItemGroup> + </Target> + + <Target Name="_BlazorCompressPublishFiles" AfterTargets="_ProcessPublishFilesForBlazor" Condition="'$(BlazorEnableCompression)' != 'false'"> + <PropertyGroup> + <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\</_CompressedFileOutputPath> + <_BlazorWebAssemblyBrotliIncremental>true</_BlazorWebAssemblyBrotliIncremental> + </PropertyGroup> + + <ItemGroup> + <_FileToCompress + Include="@(ResolvedFileToPublish)" + Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))" /> + </ItemGroup> + + <Message Text="Compressing Blazor WebAssembly publish artifacts. This may take a while..." Importance="High" /> + + <MakeDir Directories="$(_CompressedFileOutputPath)" Condition="!Exists('$(_CompressedFileOutputPath)')" /> + + <PropertyGroup Condition="'$(DOTNET_HOST_PATH)' == ''"> + <_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory> + <_DotNetHostFileName>dotnet</_DotNetHostFileName> + <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe</_DotNetHostFileName> + </PropertyGroup> + + <BrotliCompress + OutputDirectory="$(_CompressedFileOutputPath)" + FilesToCompress="@(_FileToCompress)" + CompressionLevel="$(_BlazorBrotliCompressionLevel)" + SkipIfOutputIsNewer="$(_BlazorWebAssemblyBrotliIncremental)" + ToolAssembly="$(_BlazorWebAssemblySdkToolAssembly)" + ToolExe="$(_DotNetHostFileName)" + ToolPath="$(_DotNetHostDirectory)"> + + <Output TaskParameter="CompressedFiles" ItemName="_BrotliCompressedFile" /> + <Output TaskParameter="CompressedFiles" ItemName="FileWrites" /> + </BrotliCompress> + + <GZipCompress + OutputDirectory="$(_CompressedFileOutputPath)" + FilesToCompress="@(_FileToCompress)"> + + <Output TaskParameter="CompressedFiles" ItemName="_BlazorPublishGZipCompressedFile" /> + <Output TaskParameter="CompressedFiles" ItemName="FileWrites" /> + </GZipCompress> + + <ItemGroup> + <ResolvedFileToPublish Include="@(_BrotliCompressedFile)" /> + <ResolvedFileToPublish Include="@(_BlazorPublishGZipCompressedFile)" /> + </ItemGroup> + </Target> + + <Target Name="_SetupPublishSemaphore" BeforeTargets="PrepareForPublish"> + <PropertyGroup> + <!-- + Add marker that indicates Blazor WASM is doing a publish. This is used to identify when GetCopyToPublishDirectoryItems + is invoked as a result of a P2P reference. + --> + <_PublishingBlazorWasmProject>true</_PublishingBlazorWasmProject> + </PropertyGroup> + </Target> + + <Target Name="_GetBlazorWasmFilesForPublishInner" + DependsOnTargets="_ResolveBlazorWasmOutputs;ComputeFilesToPublish" + Returns="@(ResolvedFileToPublish)" /> + + <Target Name="_GetBlazorWasmFilesForPublish" BeforeTargets="GetCopyToPublishDirectoryItems"> + <MSBuild + Projects="$(MSBuildProjectFullPath)" + Targets="_GetBlazorWasmFilesForPublishInner" + Properties="BuildProjectReferences=false;ResolveAssemblyReferencesFindRelatedSatellites=true;_PublishingBlazorWasmProject=true" + RemoveProperties="NoBuild;RuntimeIdentifier" + BuildInParallel="$(BuildInParallel)" + Condition="'$(_PublishingBlazorWasmProject)' != 'true'"> + + <Output TaskParameter="TargetOutputs" ItemName="_ResolvedFileToPublish" /> + </MSBuild> + + <ItemGroup> + <AllPublishItemsFullPathWithTargetPath Include="@(_ResolvedFileToPublish->'%(FullPath)')"> + <TargetPath>%(RelativePath)</TargetPath> + <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> + </AllPublishItemsFullPathWithTargetPath> + </ItemGroup> + </Target> + + <Target Name="_BlazorApplyLinkPreferencesToContent" BeforeTargets="AssignTargetPaths;ResolveCurrentProjectStaticWebAssetsInputs;ResolveStaticWebAssetsInputs" Returns="@(Content)"> + <ItemGroup> + <Content + Condition="'%(Content.Link)' != '' AND '%(Content.CopyToPublishDirectory)' == '' AND $([System.String]::Copy('%(Content.Link)').Replace('\','/').StartsWith('wwwroot/'))" + CopyToPublishDirectory="PreserveNewest" /> + + </ItemGroup> + </Target> + + <!-- + This is a hook to import a set of targets after the Blazor WebAssembly targets. By default this is unused. + --> + <Import Project="$(CustomAfterBlazorWebAssemblySdkTargets)" Condition="'$(CustomAfterBlazorWebAssemblySdkTargets)' != '' and Exists('$(CustomAfterBlazorWebAssemblySdkTargets)')"/> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets new file mode 100644 index 00000000000..5202eb338d4 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets @@ -0,0 +1,169 @@ +<!-- +*********************************************************************************************** +Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets + +WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have + created a backup copy. Incorrect changes to this file will make it + impossible to load or build your projects from the command-line or the IDE. + +Copyright (c) .NET Foundation. All rights reserved. +*********************************************************************************************** +--> + +<Project> + <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GenerateServiceWorkerAssetsManifest" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" /> + + <Target Name="_ComputeServiceWorkerAssets" BeforeTargets="ResolveStaticWebAssetsInputs"> + + <PropertyGroup> + <_ServiceWorkerAssetsManifestIntermediateOutputPath Condition="'$([System.IO.Path]::IsPathRooted($(BaseIntermediateOutputPath)))' == 'true'">obj\$(Configuration)\$(TargetFramework)\$(ServiceWorkerAssetsManifest)</_ServiceWorkerAssetsManifestIntermediateOutputPath> + <_ServiceWorkerAssetsManifestIntermediateOutputPath Condition="'$([System.IO.Path]::IsPathRooted($(BaseIntermediateOutputPath)))' != 'true'">$(IntermediateOutputPath)$(ServiceWorkerAssetsManifest)</_ServiceWorkerAssetsManifestIntermediateOutputPath> + <_ServiceWorkerAssetsManifestFullPath>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/$(_ServiceWorkerAssetsManifestIntermediateOutputPath)'))</_ServiceWorkerAssetsManifestFullPath> + </PropertyGroup> + + <ItemGroup> + <_ManifestStaticWebAsset Include="$(_ServiceWorkerAssetsManifestFullPath)"> + <SourceType></SourceType> + <SourceId>$(PackageId)</SourceId> + <ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot> + <BasePath>$(StaticWebAssetBasePath)</BasePath> + <RelativePath>$(ServiceWorkerAssetsManifest)</RelativePath> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </_ManifestStaticWebAsset> + + <!-- Figure out where we're getting the content for each @(ServiceWorker) entry, depending on whether there's a PublishedContent value --> + <_ServiceWorkerIntermediateFile Include="@(ServiceWorker->'$(IntermediateOutputPath)serviceworkers\%(Identity)')"> + <ContentSourcePath Condition="'%(_ServiceWorker.PublishedContent)' != ''">%(ServiceWorker.PublishedContent)</ContentSourcePath> + <ContentSourcePath Condition="'%(_ServiceWorker.PublishedContent)' == ''">%(ServiceWorker.Identity)</ContentSourcePath> + <OriginalPath>%(ServiceWorker.Identity)</OriginalPath> + <TargetOutputPath>%(ServiceWorker.Identity)</TargetOutputPath> + <TargetOutputPath Condition="$([System.String]::Copy('%(ServiceWorker.Identity)').Replace('\','/').StartsWith('wwwroot/'))">$([System.String]::Copy('%(ServiceWorker.Identity)').Substring(8))</TargetOutputPath> + </_ServiceWorkerIntermediateFile> + + <_ServiceWorkerStaticWebAsset Include="%(_ServiceWorkerIntermediateFile.FullPath)"> + <SourceType></SourceType> + <SourceId>$(PackageId)</SourceId> + <ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot> + <BasePath>$(StaticWebAssetBasePath)</BasePath> + <RelativePath>%(TargetOutputPath)</RelativePath> + <CopyToPublishDirectory>Never</CopyToPublishDirectory> + </_ServiceWorkerStaticWebAsset> + + <StaticWebAsset Include=" + @(_ManifestStaticWebAsset); + @(_ServiceWorkerStaticWebAsset)" /> + </ItemGroup> + + </Target> + + <Target Name="_WriteServiceWorkerAssetsManifest" + DependsOnTargets="_ComputeServiceWorkerAssets;ResolveStaticWebAssetsInputs"> + + <ItemGroup> + <_ServiceWorkItem Include="@(StaticWebAsset)" Exclude="$(_ServiceWorkerAssetsManifestFullPath);@(_ServiceWorkerStaticWebAsset)"> + <AssetUrl>$([System.String]::Copy('$([System.String]::Copy('%(StaticWebAsset.BasePath)').TrimEnd('/'))/%(StaticWebAsset.RelativePath)').Replace('\','/').TrimStart('/'))</AssetUrl> + </_ServiceWorkItem> + </ItemGroup> + + <GenerateServiceWorkerAssetsManifest + Version="$(ServiceWorkerAssetsManifestVersion)" + Assets="@(_ServiceWorkItem)" + OutputPath="$(_ServiceWorkerAssetsManifestIntermediateOutputPath)"> + <Output TaskParameter="CalculatedVersion" PropertyName="_ServiceWorkerAssetsManifestVersion" /> + </GenerateServiceWorkerAssetsManifest> + + <Copy + SourceFiles="%(_ServiceWorkerIntermediateFile.ContentSourcePath)" + DestinationFiles="%(_ServiceWorkerIntermediateFile.Identity)" /> + + <WriteLinesToFile + File="%(_ServiceWorkerIntermediateFile.Identity)" + Lines="/* Manifest version: $(_ServiceWorkerAssetsManifestVersion) */" + Condition="'$(_ServiceWorkerAssetsManifestVersion)' != ''" /> + + <ItemGroup> + <FileWrites Include="@(_ServiceWorkerIntermediateFile)" /> + <FileWrites Include="$(_ServiceWorkerAssetsManifestIntermediateOutputPath)" /> + </ItemGroup> + + </Target> + + <Target Name="_BlazorStaticAssetsCopyFilesToOutputDirectory" AfterTargets="CopyFilesToOutputDirectory" DependsOnTargets="_WriteServiceWorkerAssetsManifest"> + <Copy + SourceFiles="@(_ManifestStaticWebAsset);@(_ServiceWorkerStaticWebAsset)" + DestinationFiles="$(OutDir)wwwroot\%(RelativePath)" + SkipUnchangedFiles="$(SkipCopyUnchangedFiles)" + OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" + Retries="$(CopyRetryCount)" + RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" + UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)" + Condition="Exists('%(Identity)')"> + + <Output TaskParameter="DestinationFiles" ItemName="FileWrites"/> + </Copy> + </Target> + + <Target Name="_OmitServiceWorkerContent" + BeforeTargets="AssignTargetPaths;ResolveCurrentProjectStaticWebAssetsInputs"> + + <ItemGroup> + <!-- Don't emit the service worker source files to the output --> + <Content Remove="@(ServiceWorker)" /> + <Content Remove="@(ServiceWorker->'%(PublishedContent)')" /> + </ItemGroup> + </Target> + + <Target Name="_GenerateServiceWorkerFileForPublish" + BeforeTargets="_BlazorCompressPublishFiles" + AfterTargets="_ProcessPublishFilesForBlazor"> + + <PropertyGroup> + <_ServiceWorkerAssetsManifestPublishIntermediateOutputPath>$(IntermediateOutputPath)publish-$(ServiceWorkerAssetsManifest)</_ServiceWorkerAssetsManifestPublishIntermediateOutputPath> + </PropertyGroup> + + <ItemGroup> + <_ServiceWorkerIntermediatePublishFile Include="$(IntermediateOutputPath)serviceworkers\%(FileName).publish%(Extension)"> + <ContentSourcePath Condition="'%(ServiceWorker.PublishedContent)' != ''">%(ServiceWorker.PublishedContent)</ContentSourcePath> + <ContentSourcePath Condition="'%(ServiceWorker.PublishedContent)' == ''">%(ServiceWorker.Identity)</ContentSourcePath> + <RelativePath>%(ServiceWorker.Identity)</RelativePath> + </_ServiceWorkerIntermediatePublishFile> + + <_ServiceWorkerPublishFile Include="@(ResolvedFileToPublish)" Condition="$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').StartsWith('wwwroot/'))"> + <AssetUrl>$([System.String]::Copy('%(ResolvedFileToPublish.RelativePath)').Replace('\','/').TrimStart('/'))</AssetUrl> + <AssetUrl>$([System.String]::Copy('%(RelativePath)').Replace('\','/').Substring(8))</AssetUrl> + </_ServiceWorkerPublishFile> + </ItemGroup> + + <GenerateServiceWorkerAssetsManifest + Version="$(ServiceWorkerAssetsManifestVersion)" + Assets="@(_ServiceWorkerPublishFile)" + OutputPath="$(_ServiceWorkerAssetsManifestPublishIntermediateOutputPath)"> + + <Output TaskParameter="CalculatedVersion" PropertyName="_ServiceWorkerPublishAssetsManifestVersion" /> + </GenerateServiceWorkerAssetsManifest> + + <Copy SourceFiles="%(_ServiceWorkerIntermediatePublishFile.ContentSourcePath)" + DestinationFiles="%(_ServiceWorkerIntermediatePublishFile.Identity)" /> + + <WriteLinesToFile + File="%(_ServiceWorkerIntermediatePublishFile.Identity)" + Lines="/* Manifest version: $(_ServiceWorkerPublishAssetsManifestVersion) */" /> + + <ItemGroup> + <ResolvedFileToPublish + Include="@(_ServiceWorkerIntermediatePublishFile)" + CopyToPublishDirectory="PreserveNewest" + RelativePath="%(_ServiceWorkerIntermediatePublishFile.RelativePath)" + ExcludeFromSingleFile="true" /> + + <ResolvedFileToPublish + Include="$(_ServiceWorkerAssetsManifestPublishIntermediateOutputPath)" + CopyToPublishDirectory="PreserveNewest" + RelativePath="wwwroot\$(ServiceWorkerAssetsManifest)" + ExcludeFromSingleFile="true" /> + </ItemGroup> + </Target> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs b/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs new file mode 100644 index 00000000000..b95a6154b66 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/BlazorReadSatelliteAssemblyFileTest.cs @@ -0,0 +1,68 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Moq; +using Xunit; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class BlazorReadSatelliteAssemblyFileTest + { + [Fact] + public void WritesAndReadsRoundTrip() + { + // Arrange/Act + var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + var writer = new BlazorWriteSatelliteAssemblyFile + { + BuildEngine = Mock.Of<IBuildEngine>(), + WriteFile = new TaskItem(tempFile), + SatelliteAssembly = new[] + { + new TaskItem("Resources.fr.dll", new Dictionary<string, string> + { + ["Culture"] = "fr", + ["DestinationSubDirectory"] = "fr\\", + }), + new TaskItem("Resources.ja-jp.dll", new Dictionary<string, string> + { + ["Culture"] = "ja-jp", + ["DestinationSubDirectory"] = "ja-jp\\", + }), + }, + }; + + var reader = new BlazorReadSatelliteAssemblyFile + { + BuildEngine = Mock.Of<IBuildEngine>(), + ReadFile = new TaskItem(tempFile), + }; + + writer.Execute(); + + Assert.True(File.Exists(tempFile), "Write should have succeeded."); + + reader.Execute(); + + Assert.Collection( + reader.SatelliteAssembly, + assembly => + { + Assert.Equal("Resources.fr.dll", assembly.ItemSpec); + Assert.Equal("fr", assembly.GetMetadata("Culture")); + Assert.Equal("fr\\", assembly.GetMetadata("DestinationSubDirectory")); + }, + assembly => + { + Assert.Equal("Resources.ja-jp.dll", assembly.ItemSpec); + Assert.Equal("ja-jp", assembly.GetMetadata("Culture")); + Assert.Equal("ja-jp\\", assembly.GetMetadata("DestinationSubDirectory")); + }); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs b/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs new file mode 100644 index 00000000000..139f22f27fb --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/GenerateBlazorBootJsonTest.cs @@ -0,0 +1,195 @@ +// 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.Linq; +using System.Runtime.Serialization.Json; +using Microsoft.Build.Framework; +using Moq; +using Xunit; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly +{ + public class GenerateBlazorWebAssemblyBootJsonTest + { + [Fact] + public void GroupsResourcesByType() + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson + { + AssemblyPath = "MyApp.Entrypoint.dll", + Resources = new[] + { + CreateResourceTaskItem( + ("FileName", "My.Assembly1"), + ("Extension", ".dll"), + ("FileHash", "abcdefghikjlmnopqrstuvwxyz")), + + CreateResourceTaskItem( + ("FileName", "My.Assembly2"), + ("Extension", ".dll"), + ("FileHash", "012345678901234567890123456789")), + + CreateResourceTaskItem( + ("FileName", "SomePdb"), + ("Extension", ".pdb"), + ("FileHash", "pdbhashpdbhashpdbhash")), + + CreateResourceTaskItem( + ("FileName", "My.Assembly1"), + ("Extension", ".pdb"), + ("FileHash", "pdbdefghikjlmnopqrstuvwxyz")), + + CreateResourceTaskItem( + ("FileName", "some-runtime-file"), + ("FileHash", "runtimehashruntimehash"), + ("AssetType", "native")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly1"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly1"), + ("Culture", "en-GB")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly2"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly2"), + ("Culture", "fr")), + + CreateResourceTaskItem( + ("FileName", "satellite-assembly3"), + ("Extension", ".dll"), + ("FileHash", "hashsatelliteassembly3"), + ("Culture", "en-GB")), + } + }; + + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal("MyEntrypointAssembly", parsedContent.entryAssembly); + + var resources = parsedContent.resources.assembly; + Assert.Equal(2, resources.Count); + Assert.Equal("sha256-abcdefghikjlmnopqrstuvwxyz", resources["My.Assembly1.dll"]); + Assert.Equal("sha256-012345678901234567890123456789", resources["My.Assembly2.dll"]); + + resources = parsedContent.resources.pdb; + Assert.Equal(2, resources.Count); + Assert.Equal("sha256-pdbhashpdbhashpdbhash", resources["SomePdb.pdb"]); + Assert.Equal("sha256-pdbdefghikjlmnopqrstuvwxyz", resources["My.Assembly1.pdb"]); + + resources = parsedContent.resources.runtime; + Assert.Single(resources); + Assert.Equal("sha256-runtimehashruntimehash", resources["some-runtime-file"]); + + var satelliteResources = parsedContent.resources.satelliteResources; + Assert.Collection( + satelliteResources.OrderBy(kvp => kvp.Key), + kvp => + { + Assert.Equal("en-GB", kvp.Key); + Assert.Collection( + kvp.Value.OrderBy(item => item.Key), + item => + { + Assert.Equal("en-GB/satellite-assembly1.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly1", item.Value); + }, + item => + { + Assert.Equal("en-GB/satellite-assembly3.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly3", item.Value); + }); + }, + kvp => + { + Assert.Equal("fr", kvp.Key); + Assert.Collection( + kvp.Value.OrderBy(item => item.Key), + item => + { + Assert.Equal("fr/satellite-assembly2.dll", item.Key); + Assert.Equal("sha256-hashsatelliteassembly2", item.Value); + }); + }); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyCacheBootResources(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { CacheBootResources = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.cacheBootResources); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyDebugBuild(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { DebugBuild = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.debugBuild); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CanSpecifyLinkerEnabled(bool flagValue) + { + // Arrange + var taskInstance = new GenerateBlazorWebAssemblyBootJson { LinkerEnabled = flagValue }; + using var stream = new MemoryStream(); + + // Act + taskInstance.WriteBootJson(stream, "MyEntrypointAssembly"); + + // Assert + var parsedContent = ParseBootData(stream); + Assert.Equal(flagValue, parsedContent.linkerEnabled); + } + + private static BootJsonData ParseBootData(Stream stream) + { + stream.Position = 0; + var serializer = new DataContractJsonSerializer( + typeof(BootJsonData), + new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true }); + return (BootJsonData)serializer.ReadObject(stream); + } + + private static ITaskItem CreateResourceTaskItem(params (string key, string value)[] values) + { + var mock = new Mock<ITaskItem>(); + + foreach (var (key, value) in values) + { + mock.Setup(m => m.GetMetadata(key)).Returns(value); + } + return mock.Object; + } + } +} diff --git a/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj b/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj new file mode 100644 index 00000000000..5d30b4f7814 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <Reference Include="Microsoft.Build.Utilities.Core" /> + <Reference Include="Microsoft.NET.Sdk.BlazorWebAssembly" /> + </ItemGroup> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props new file mode 100644 index 00000000000..6d0949542f9 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.props @@ -0,0 +1,50 @@ +<Project> + <Import Project="Before.Directory.Build.props" Condition="Exists('Before.Directory.Build.props')" /> + + <PropertyGroup> + <!-- + In the case that a user is building a sample directly the MicrosoftNetCompilersToolsetPackagerVersion will not be provided. + We'll fall back to whatever the current SDK provides in regards to Roslyn's Microsoft.Net.Compilers.Toolset. + --> + <BuildingTestAppsIndependently>false</BuildingTestAppsIndependently> + <BuildingTestAppsIndependently Condition="'$(MicrosoftNetCompilersToolsetPackageVersion)' == ''">true</BuildingTestAppsIndependently> + + <!-- Do not resolve Reference ItemGroup since it has a different semantic meaning in Razor test apps --> + <EnableCustomReferenceResolution>false</EnableCustomReferenceResolution> + + <DefaultNetCoreTargetFramework>net5.0</DefaultNetCoreTargetFramework> + + <RepoRoot Condition="'$(RepoRoot)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, 'AspNetCore.sln'))\</RepoRoot> + + <RazorSdkCurrentVersionProps>$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\build\netstandard2.0\Sdk.Razor.CurrentVersion.props</RazorSdkCurrentVersionProps> + <RazorSdkCurrentVersionTargets>$(RepoRoot)src\Razor\Microsoft.NET.Sdk.Razor\src\build\netstandard2.0\Sdk.Razor.CurrentVersion.targets</RazorSdkCurrentVersionTargets> + <RazorSdkArtifactsDirectory>$(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.Razor\</RazorSdkArtifactsDirectory> + <BlazorWebAssemblySdkArtifactsDirectory>$(RepoRoot)artifacts\bin\Microsoft.NET.Sdk.BlazorWebAssembly\</BlazorWebAssemblySdkArtifactsDirectory> + <BlazorWebAssemblyJSPath>$(MSBuildThisFileDirectory)blazor.webassembly.js</BlazorWebAssemblyJSPath> + </PropertyGroup> + + <Import Project="$(RepoRoot)eng\Versions.props" /> + + <PropertyGroup> + <!-- Reset version prefix to 1.0.0 for test projects --> + <VersionPrefix>1.0.0</VersionPrefix> + + <!-- Turn down the compression level for brotli --> + <_BlazorBrotliCompressionLevel>NoCompression</_BlazorBrotliCompressionLevel> + </PropertyGroup> + + <ItemGroup> + <!-- Have the SDK treat the MvcShim as an MVC assembly --> + <_MvcAssemblyName Include="Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib" /> + </ItemGroup> + + <ItemGroup Condition="$(BuildingTestAppsIndependently) == 'false'"> + <PackageReference Include="Microsoft.Net.Compilers.Toolset" + Version="$(MicrosoftNetCompilersToolsetPackageVersion)" + PrivateAssets="all" + IsImplicitlyDefined="true" /> + </ItemGroup> + + <Import Project="After.Directory.Build.props" Condition="Exists('After.Directory.Build.props')" /> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets new file mode 100644 index 00000000000..45a2ee1e565 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/Directory.Build.targets @@ -0,0 +1,7 @@ +<Project> + <PropertyGroup> + <RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' ">$(MicrosoftNETCoreAppRuntimeVersion)</RuntimeFrameworkVersion> + <!-- aspnet/BuildTools#662 Don't police what version of NetCoreApp we use --> + <NETCoreAppMaximumVersion>99.9</NETCoreAppMaximumVersion> + </PropertyGroup> +</Project> diff --git a/src/Razor/test/testassets/LinkBaseToWebRoot/js/LinkedScript.js b/src/Components/WebAssembly/Sdk/testassets/LinkBaseToWebRoot/js/LinkedScript.js similarity index 100% rename from src/Razor/test/testassets/LinkBaseToWebRoot/js/LinkedScript.js rename to src/Components/WebAssembly/Sdk/testassets/LinkBaseToWebRoot/js/LinkedScript.js diff --git a/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj b/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj new file mode 100644 index 00000000000..f4debf80880 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/RestoreBlazorWasmTestProjects/RestoreBlazorWasmTestProjects.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <ProjectReference Include="..\blazorhosted\blazorhosted.csproj" /> + <ProjectReference Include="..\blazorhosted-rid\blazorhosted-rid.csproj" /> + <ProjectReference Include="..\blazorwasm\blazorwasm.csproj" /> + </ItemGroup> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js b/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js new file mode 100644 index 00000000000..84362ca046b --- /dev/null +++ b/src/Components/WebAssembly/Sdk/testassets/blazor.webassembly.js @@ -0,0 +1 @@ +Test file \ No newline at end of file diff --git a/src/Razor/test/testassets/blazorhosted-rid/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorhosted-rid/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/Program.cs diff --git a/src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/blazorhosted-rid.csproj similarity index 100% rename from src/Razor/test/testassets/blazorhosted-rid/blazorhosted-rid.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted-rid/blazorhosted-rid.csproj diff --git a/src/Razor/test/testassets/blazorhosted/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorhosted/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorhosted/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted/Program.cs diff --git a/src/Razor/test/testassets/blazorhosted/blazorhosted.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorhosted/blazorhosted.csproj similarity index 100% rename from src/Razor/test/testassets/blazorhosted/blazorhosted.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorhosted/blazorhosted.csproj diff --git a/src/Razor/test/testassets/blazorwasm/App.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/App.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/App.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/App.razor diff --git a/src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/LinkToWebRoot/css/app.css similarity index 100% rename from src/Razor/test/testassets/blazorwasm/LinkToWebRoot/css/app.css rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/LinkToWebRoot/css/app.css diff --git a/src/Razor/test/testassets/blazorwasm/Pages/Index.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Pages/Index.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Pages/Index.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Pages/Index.razor diff --git a/src/Razor/test/testassets/blazorwasm/Program.cs b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Program.cs similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Program.cs rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Program.cs diff --git a/src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/Resources.ja.resx.txt similarity index 100% rename from src/Razor/test/testassets/blazorwasm/Resources.ja.resx.txt rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/Resources.ja.resx.txt diff --git a/src/Razor/test/testassets/blazorwasm/_Imports.razor b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/_Imports.razor similarity index 100% rename from src/Razor/test/testassets/blazorwasm/_Imports.razor rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/_Imports.razor diff --git a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj similarity index 88% rename from src/Razor/test/testassets/blazorwasm/blazorwasm.csproj rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj index 36a511e7c77..c8906b1bd29 100644 --- a/src/Razor/test/testassets/blazorwasm/blazorwasm.csproj +++ b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/blazorwasm.csproj @@ -1,10 +1,12 @@ <Project Sdk="Microsoft.NET.Sdk.Razor"> + + <Import Project="$(RepoRoot)src\Components\WebAssembly\Sdk\src\Sdk\Sdk.props" /> + <PropertyGroup> <TargetFramework>net5.0</TargetFramework> - <UseBlazorWebAssembly>true</UseBlazorWebAssembly> <RuntimeIdentifier>browser-wasm</RuntimeIdentifier> - <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <RazorSdkDirectoryRoot>$(RazorSdkArtifactsDirectory)$(Configuration)\sdk-output\</RazorSdkDirectoryRoot> + <BlazorWebAssemblySdkDirectoryRoot>$(BlazorWebAssemblySdkArtifactsDirectory)$(Configuration)\sdk-output\</BlazorWebAssemblySdkDirectoryRoot> <ServiceWorkerAssetsManifest>custom-service-worker-assets.js</ServiceWorkerAssetsManifest> </PropertyGroup> @@ -50,4 +52,6 @@ <ServiceWorker Include="wwwroot\serviceworkers\my-service-worker.js" PublishedContent="wwwroot\serviceworkers\my-prod-service-worker.js" /> </ItemGroup> + <Import Project="$(RepoRoot)src\Components\WebAssembly\Sdk\src\Sdk\Sdk.targets" /> + </Project> diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/Fake-License.txt similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/Fake-License.txt rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/Fake-License.txt diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/css/app.css similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/css/app.css rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/css/app.css diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/index.html b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/index.html similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/index.html rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/index.html diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-prod-service-worker.js diff --git a/src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js b/src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js similarity index 100% rename from src/Razor/test/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js rename to src/Components/WebAssembly/Sdk/testassets/blazorwasm/wwwroot/serviceworkers/my-service-worker.js diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Class1.cs similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Class1.cs rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Class1.cs diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/Resources.es-ES.resx diff --git a/src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj b/src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj similarity index 100% rename from src/Razor/test/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj rename to src/Components/WebAssembly/Sdk/testassets/classlibrarywithsatelliteassemblies/classlibrarywithsatelliteassemblies.csproj diff --git a/src/Razor/test/testassets/razorclasslibrary/Class1.cs b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/Class1.cs similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/Class1.cs rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/Class1.cs diff --git a/src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/RazorClassLibrary.csproj similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/RazorClassLibrary.csproj rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/RazorClassLibrary.csproj diff --git a/src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/styles.css similarity index 100% rename from src/Razor/test/testassets/razorclasslibrary/wwwroot/styles.css rename to src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/styles.css diff --git a/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js b/src/Components/WebAssembly/Sdk/testassets/razorclasslibrary/wwwroot/wwwroot/exampleJsInterop.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Components/WebAssembly/Sdk/tools/Application.cs b/src/Components/WebAssembly/Sdk/tools/Application.cs new file mode 100644 index 00000000000..d9d66d10bc8 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Application.cs @@ -0,0 +1,98 @@ +// 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; +using System.Threading; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal class Application : CommandLineApplication + { + public Application( + CancellationToken cancellationToken, + TextWriter output = null, + TextWriter error = null) + { + CancellationToken = cancellationToken; + Out = output ?? Out; + Error = error ?? Error; + + Name = "BlazorWebAssembly.Tools"; + FullName = "Microsoft Blazor WebAssembly SDK tool"; + Description = "CLI for Blazor WebAssembly operations."; + ShortVersionGetter = GetInformationalVersion; + + HelpOption("-?|-h|--help"); + + Commands.Add(new BrotliCompressCommand(this)); + } + + public CancellationToken CancellationToken { get; } + + public new int Execute(params string[] args) + { + try + { + return base.Execute(ExpandResponseFiles(args)); + } + catch (AggregateException ex) when (ex.InnerException != null) + { + foreach (var innerException in ex.Flatten().InnerExceptions) + { + Error.WriteLine(innerException.Message); + Error.WriteLine(innerException.StackTrace); + } + return 1; + } + catch (CommandParsingException ex) + { + // Don't show a call stack when we have unneeded arguments, just print the error message. + // The code that throws this exception will print help, so no need to do it here. + Error.WriteLine(ex.Message); + return 1; + } + catch (OperationCanceledException) + { + // This is a cancellation, not a failure. + Error.WriteLine("Cancelled"); + return 1; + } + catch (Exception ex) + { + Error.WriteLine(ex.Message); + Error.WriteLine(ex.StackTrace); + return 1; + } + } + + private string GetInformationalVersion() + { + var assembly = typeof(Application).GetTypeInfo().Assembly; + var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>(); + return attribute.InformationalVersion; + } + + private static string[] ExpandResponseFiles(string[] args) + { + var expandedArgs = new List<string>(); + foreach (var arg in args) + { + if (!arg.StartsWith("@", StringComparison.Ordinal)) + { + expandedArgs.Add(arg); + } + else + { + var fileName = arg.Substring(1); + expandedArgs.AddRange(File.ReadLines(fileName)); + } + } + + return expandedArgs.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs b/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs new file mode 100644 index 00000000000..136c2fc660e --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/BrotliCompressCommand.cs @@ -0,0 +1,85 @@ +// 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 System.IO.Compression; +using System.Threading.Tasks; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal class BrotliCompressCommand : CommandLineApplication + { + public BrotliCompressCommand(Application parent) + : base(throwOnUnexpectedArg: true) + { + base.Parent = parent; + Name = "brotli"; + Sources = Option("-s", "files to compress", CommandOptionType.MultipleValue); + Outputs = Option("-o", "Output file path", CommandOptionType.MultipleValue); + CompressionLevelOption = Option("-c", "Compression level", CommandOptionType.SingleValue); + + Invoke = () => Execute().GetAwaiter().GetResult(); + } + + public CommandOption Sources { get; } + + public CommandOption Outputs { get; } + + public CommandOption CompressionLevelOption { get; } + + public CompressionLevel CompressionLevel { get; private set; } = CompressionLevel.Optimal; + + private Task<int> Execute() + { + if (!ValidateArguments()) + { + ShowHelp(); + return Task.FromResult(1); + } + + return ExecuteCoreAsync(); + } + + private bool ValidateArguments() + { + if (Sources.Values.Count != Outputs.Values.Count) + { + Error.WriteLine($"{Sources.Description} has {Sources.Values.Count}, but {Outputs.Description} has {Outputs.Values.Count} values."); + return false; + } + + if (CompressionLevelOption.HasValue()) + { + if (!Enum.TryParse<CompressionLevel>(CompressionLevelOption.Value(), out var value)) + { + Error.WriteLine($"Invalid option {CompressionLevelOption.Value()} for {CompressionLevelOption.Template}."); + return false; + } + + CompressionLevel = value; + } + + return true; + } + + private Task<int> ExecuteCoreAsync() + { + Parallel.For(0, Sources.Values.Count, i => + { + var source = Sources.Values[i]; + var output = Outputs.Values[i]; + + using var sourceStream = File.OpenRead(source); + using var fileStream = new FileStream(output, FileMode.Create); + + using var stream = new BrotliStream(fileStream, CompressionLevel); + + sourceStream.CopyTo(stream); + }); + + return Task.FromResult(0); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/tools/DebugMode.cs b/src/Components/WebAssembly/Sdk/tools/DebugMode.cs new file mode 100644 index 00000000000..816bb4a7835 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/DebugMode.cs @@ -0,0 +1,27 @@ +// 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.Diagnostics; +using System.Linq; +using System.Threading; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal static class DebugMode + { + public static void HandleDebugSwitch(ref string[] args) + { + if (args.Length > 0 && string.Equals("--debug", args[0], StringComparison.OrdinalIgnoreCase)) + { + args = args.Skip(1).ToArray(); + + Console.WriteLine("Waiting for debugger in pid: {0}", Process.GetCurrentProcess().Id); + while (!Debugger.IsAttached) + { + Thread.Sleep(TimeSpan.FromSeconds(3)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj b/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj new file mode 100644 index 00000000000..6ac7a26c192 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Microsoft.NET.Sdk.BlazorWebAssembly.Tools.csproj @@ -0,0 +1,22 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net5.0</TargetFramework> + + <IsPackable>false</IsPackable> + <IsShipping>false</IsShipping> + <DisablePubternalApiCheck>true</DisablePubternalApiCheck> + + <UseAppHost>false</UseAppHost> + <RuntimeIdentifier /> + + <!-- Need to build this project in source build --> + <ExcludeFromSourceBuild>false</ExcludeFromSourceBuild> + </PropertyGroup> + + <ItemGroup> + <Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" /> + </ItemGroup> + +</Project> diff --git a/src/Components/WebAssembly/Sdk/tools/Program.cs b/src/Components/WebAssembly/Sdk/tools/Program.cs new file mode 100644 index 00000000000..b00093323d0 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/Program.cs @@ -0,0 +1,30 @@ +// 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 System.Threading; +using Microsoft.CodeAnalysis; + +namespace Microsoft.NET.Sdk.BlazorWebAssembly.Tools +{ + internal static class Program + { + public static int Main(string[] args) + { + DebugMode.HandleDebugSwitch(ref args); + + var cancel = new CancellationTokenSource(); + Console.CancelKeyPress += (sender, e) => { cancel.Cancel(); }; + + var application = new Application( + cancel.Token, + Console.Out, + Console.Error); + + application.Commands.Add(new BrotliCompressCommand(application)); + + return application.Execute(args); + } + } +} diff --git a/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json b/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json new file mode 100644 index 00000000000..2c73f398906 --- /dev/null +++ b/src/Components/WebAssembly/Sdk/tools/runtimeconfig.template.json @@ -0,0 +1,3 @@ +{ + "rollForwardOnNoCandidateFx": 2 +} \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj index a1e160322eb..2832be128d1 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj +++ b/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Microsoft.NET.Sdk.Razor.IntegrationTests.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <!-- @@ -13,8 +13,11 @@ <!-- Tests do not work on Helix yet --> <BuildHelixPayload>false</BuildHelixPayload> + <TestAppsRoot>$(MSBuildProjectDirectory)\..\..\test\testassets\</TestAppsRoot> </PropertyGroup> + <Import Project="$(SharedSourceRoot)MSBuild.Testing\MSBuild.Testing.targets" /> + <ItemGroup> <None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup> @@ -24,49 +27,6 @@ <Reference Include="Microsoft.Extensions.DependencyModel" /> </ItemGroup> - <ItemGroup> - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>Testing.AdditionalRestoreSources</_Parameter1> - <_Parameter2>$(MSBuildThisFileDirectory)..\testassets\PregeneratedPackages</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>ArtifactsLogDir</_Parameter1> - <_Parameter2>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfig)'))</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>ProcDumpToolPath</_Parameter1> - <_Parameter2>$(ProcDumpPath)</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>Testing.RepoRoot</_Parameter1> - <_Parameter2>$(RepoRoot)</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>MicrosoftNETCoreAppRuntimeVersion</_Parameter1> - <_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion)</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>DefaultNetCoreTargetFramework</_Parameter1> - <_Parameter2>$(DefaultNetCoreTargetFramework)</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>MicrosoftNetCompilersToolsetPackageVersion</_Parameter1> - <_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion)</_Parameter2> - </AssemblyAttribute> - - <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> - <_Parameter1>RazorSdkDirectoryRoot</_Parameter1> - <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\sdk-output\</_Parameter2> - </AssemblyAttribute> - - </ItemGroup> - <ItemGroup> <Reference Include="rzc" /> <ProjectReference Include="..\..\test\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib\Microsoft.AspNetCore.Razor.Test.MvcShim.ClassLib.csproj" /> diff --git a/src/Shared/E2ETesting/E2ETesting.targets b/src/Shared/E2ETesting/E2ETesting.targets index d0fa6cb856e..76ced2cce99 100644 --- a/src/Shared/E2ETesting/E2ETesting.targets +++ b/src/Shared/E2ETesting/E2ETesting.targets @@ -66,14 +66,9 @@ <_DefaultProjectRoot>$([System.IO.Path]::GetFullPath($(_DefaultProjectFilter)))</_DefaultProjectRoot> </PropertyGroup> <ItemGroup> - <_ContentRootProjectReferencesUnfiltered - Include="@(ReferencePath)" - Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference'" /> - <_ContentRootProjectReferencesFilter - Include="@(_ContentRootProjectReferencesUnfiltered->StartsWith('$(_DefaultProjectRoot)'))" /> <_ContentRootProjectReferences - Include="@(_ContentRootProjectReferencesFilter)" - Condition="'%(Identity)' == 'True'" /> + Include="@(ReferencePath)" + Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference' AND $([System.String]::Copy(%(ReferencePath.MSBuildSourceProjectFile)).StartsWith('$(_DefaultProjectRoot)'))" /> </ItemGroup> </Target> diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs b/src/Shared/MSBuild.Testing/Assert.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/Assert.cs rename to src/Shared/MSBuild.Testing/Assert.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs b/src/Shared/MSBuild.Testing/BuildVariables.cs similarity index 83% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs rename to src/Shared/MSBuild.Testing/BuildVariables.cs index 797d2446250..b659ba918c7 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/BuildVariables.cs +++ b/src/Shared/MSBuild.Testing/BuildVariables.cs @@ -19,8 +19,12 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests public static string RazorSdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "RazorSdkDirectoryRoot").Value; + public static string BlazorWebAssemblySdkDirectoryRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "BlazorWebAssemblySdkDirectoryRoot").Value; + public static string RepoRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "Testing.RepoRoot").Value; - + public static string DefaultNetCoreTargetFramework => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "DefaultNetCoreTargetFramework").Value; + + public static string TestAppsRoot => TestAssemblyMetadata.SingleOrDefault(a => a.Key == "TestAppsRoot").Value; } } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs b/src/Shared/MSBuild.Testing/FIleThumbPrint.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/FIleThumbPrint.cs rename to src/Shared/MSBuild.Testing/FIleThumbPrint.cs diff --git a/src/Shared/MSBuild.Testing/MSBuild.Testing.targets b/src/Shared/MSBuild.Testing/MSBuild.Testing.targets new file mode 100644 index 00000000000..8868a23d979 --- /dev/null +++ b/src/Shared/MSBuild.Testing/MSBuild.Testing.targets @@ -0,0 +1,53 @@ +<Project> + +<ItemGroup> + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>ArtifactsLogDir</_Parameter1> + <_Parameter2>$([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'log', '$(_BuildConfig)'))</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>ProcDumpToolPath</_Parameter1> + <_Parameter2>$(ProcDumpPath)</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>Testing.RepoRoot</_Parameter1> + <_Parameter2>$(RepoRoot)</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>MicrosoftNETCoreAppRuntimeVersion</_Parameter1> + <_Parameter2>$(MicrosoftNETCoreAppRuntimeVersion)</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>DefaultNetCoreTargetFramework</_Parameter1> + <_Parameter2>$(DefaultNetCoreTargetFramework)</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>MicrosoftNetCompilersToolsetPackageVersion</_Parameter1> + <_Parameter2>$(MicrosoftNetCompilersToolsetPackageVersion)</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>RazorSdkDirectoryRoot</_Parameter1> + <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor\$(Configuration)\sdk-output\</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>BlazorWebAssemblySdkDirectoryRoot</_Parameter1> + <_Parameter2>$(ArtifactsBinDir)Microsoft.NET.Sdk.BlazorWebAssembly\$(Configuration)\sdk-output\</_Parameter2> + </AssemblyAttribute> + + <AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute"> + <_Parameter1>TestAppsRoot</_Parameter1> + <_Parameter2>$(TestAppsRoot)</_Parameter2> + </AssemblyAttribute> + </ItemGroup> + + <ItemGroup> + <Compile Include="$(MSBuildThisFileDirectory)*.cs" LinkBase="Infrastructure" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessKind.cs b/src/Shared/MSBuild.Testing/MSBuildProcessKind.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessKind.cs rename to src/Shared/MSBuild.Testing/MSBuildProcessKind.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs b/src/Shared/MSBuild.Testing/MSBuildProcessManager.cs similarity index 98% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs rename to src/Shared/MSBuild.Testing/MSBuildProcessManager.cs index cc79523d5bf..3d9ada158bf 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildProcessManager.cs +++ b/src/Shared/MSBuild.Testing/MSBuildProcessManager.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; @@ -37,6 +37,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests $"/p:MicrosoftNETCoreAppRuntimeVersion={BuildVariables.MicrosoftNETCoreAppRuntimeVersion}", $"/p:MicrosoftNetCompilersToolsetPackageVersion={BuildVariables.MicrosoftNetCompilersToolsetPackageVersion}", $"/p:RazorSdkDirectoryRoot={BuildVariables.RazorSdkDirectoryRoot}", + $"/p:BlazorWebAssemblySdkDirectoryRoot={BuildVariables.BlazorWebAssemblySdkDirectoryRoot}", $"/p:RepoRoot={BuildVariables.RepoRoot}", $"/p:Configuration={project.Configuration}", $"/t:{target}", diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildResult.cs b/src/Shared/MSBuild.Testing/MSBuildResult.cs similarity index 100% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/MSBuildResult.cs rename to src/Shared/MSBuild.Testing/MSBuildResult.cs diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs b/src/Shared/MSBuild.Testing/ProjectDirectory.cs similarity index 97% rename from src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs rename to src/Shared/MSBuild.Testing/ProjectDirectory.cs index d0d0e571f46..0f669c31fce 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ProjectDirectory.cs +++ b/src/Shared/MSBuild.Testing/ProjectDirectory.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; @@ -49,12 +49,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } var repositoryRoot = BuildVariables.RepoRoot; - var solutionRoot = Path.Combine(repositoryRoot, "src", "Razor"); var binariesRoot = Path.GetDirectoryName(typeof(ProjectDirectory).Assembly.Location); + var testAppsRoot = BuildVariables.TestAppsRoot; foreach (var project in new string[] { originalProjectName, }.Concat(additionalProjects)) { - var testAppsRoot = Path.Combine(solutionRoot, "test", "testassets"); var projectRoot = Path.Combine(testAppsRoot, project); if (!Directory.Exists(projectRoot)) { @@ -146,6 +145,11 @@ $@"<Project> .ForEach(file => { var source = Path.Combine(testAppsRoot, file); + if (!File.Exists(source)) + { + return; + } + var destination = Path.Combine(projectDestination, file); File.Copy(source, destination); }); -- GitLab