[Fixes #66] Adds linking support to the build
* Switch the tasks used to generate the blazor output to be MSBuild based. * Package the optimized mono runtime and the BCL inside a nuget package. * Add opt-in support for linking the application on build. * Make the whole build process incremental.
This commit is contained in:
parent
f56fc1be3d
commit
700c2203c6
|
|
@ -16,13 +16,13 @@ test: 'off'
|
|||
deploy: 'off'
|
||||
os: Visual Studio 2017 Preview
|
||||
build_script:
|
||||
- build.cmd /p:SkipTests=true
|
||||
- build.cmd /p:SkipTests=true /p:BlazorOutputStatistics=true
|
||||
before_test:
|
||||
- choco install googlechrome --ignore-checksum
|
||||
- npm install -g selenium-standalone
|
||||
- selenium-standalone install
|
||||
- ps: $SeleniumProcess = Start-Process "selenium-standalone" -ArgumentList "start" -PassThru
|
||||
test_script:
|
||||
- build.cmd /t:Test /p:BlazorAllTests=true
|
||||
- build.cmd /t:Test /p:BlazorAllTests=true /p:BlazorOutputStatistics=true
|
||||
after_test:
|
||||
- ps: Stop-Process -Id $SeleniumProcess.Id
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ obj/
|
|||
launchSettings.json
|
||||
artifacts/
|
||||
msbuild.binlog
|
||||
.vscode/
|
||||
|
|
@ -36,6 +36,6 @@ install:
|
|||
- curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel Current --version latest --install-dir "$DOTNET_INSTALL_DIR"
|
||||
- export PATH="$DOTNET_INSTALL_DIR:$PATH"
|
||||
script:
|
||||
- ./build.sh /p:SkipTests=true
|
||||
- ./build.sh /p:SkipTests=true /p:BlazorOutputStatistics=true
|
||||
- selenium-standalone start &
|
||||
- ./build.sh /t:Test /p:BlazorAllTests=true
|
||||
|
|
|
|||
44
Blazor.sln
44
Blazor.sln
|
|
@ -12,16 +12,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mono", "mono", "{7B5CAAB1-A
|
|||
src\mono\mono.targets = src\mono\mono.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Mono", "src\Microsoft.AspNetCore.Blazor.Mono\Microsoft.AspNetCore.Blazor.Mono.csproj", "{39FEC72D-AF52-47A3-B63D-7BF0E4335248}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Server", "src\Microsoft.AspNetCore.Blazor.Server\Microsoft.AspNetCore.Blazor.Server.csproj", "{5A694793-3257-4D37-BB74-4A41B3894685}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Mono.Test", "test\Microsoft.AspNetCore.Blazor.Mono.Test\Microsoft.AspNetCore.Blazor.Mono.Test.csproj", "{118484D3-3993-45CE-97C1-6F28A517529B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.E2ETest", "test\Microsoft.AspNetCore.Blazor.E2ETest\Microsoft.AspNetCore.Blazor.E2ETest.csproj", "{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanityClient", "samples\MonoSanityClient\MonoSanityClient.csproj", "{06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}"
|
||||
|
|
@ -40,10 +36,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneApp", "samples\St
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MonoSanity", "MonoSanity", "{2A076721-6081-4517-8329-B9E5110D6DAC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Common", "src\Microsoft.AspNetCore.Blazor.Common\Microsoft.AspNetCore.Blazor.Common.csproj", "{21EF76A4-63CC-455D-907C-F86C9E442CEC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Common.Test", "test\Microsoft.AspNetCore.Blazor.Common.Test\Microsoft.AspNetCore.Blazor.Common.Test.csproj", "{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Build.Test", "test\Microsoft.AspNetCore.Blazor.Build.Test\Microsoft.AspNetCore.Blazor.Build.Test.csproj", "{709C7EBE-EB93-4F6D-9491-D714B0D2E898}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Build", "src\Microsoft.AspNetCore.Blazor.Build\Microsoft.AspNetCore.Blazor.Build.csproj", "{8B3D0F1C-0E38-4E6D-BFF1-C4FDA0CD9815}"
|
||||
|
|
@ -103,14 +95,6 @@ Global
|
|||
ReleaseNoVSIX|Any CPU = ReleaseNoVSIX|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -127,14 +111,6 @@ Global
|
|||
{5A694793-3257-4D37-BB74-4A41B3894685}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A694793-3257-4D37-BB74-4A41B3894685}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A694793-3257-4D37-BB74-4A41B3894685}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -191,22 +167,6 @@ Global
|
|||
{B241434A-1642-44CC-AE9A-2012B5C5BD02}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B241434A-1642-44CC-AE9A-2012B5C5BD02}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B241434A-1642-44CC-AE9A-2012B5C5BD02}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{709C7EBE-EB93-4F6D-9491-D714B0D2E898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{709C7EBE-EB93-4F6D-9491-D714B0D2E898}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{709C7EBE-EB93-4F6D-9491-D714B0D2E898}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -347,10 +307,8 @@ Global
|
|||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{39FEC72D-AF52-47A3-B63D-7BF0E4335248} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7} = {2A076721-6081-4517-8329-B9E5110D6DAC}
|
||||
{5A694793-3257-4D37-BB74-4A41B3894685} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{118484D3-3993-45CE-97C1-6F28A517529B} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||
{06AAAE9E-96DE-4574-97DA-9C4C7D9FE990} = {2A076721-6081-4517-8329-B9E5110D6DAC}
|
||||
{BB34336F-E68E-4411-9805-CAAA919F5EA1} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
|
|
@ -360,8 +318,6 @@ Global
|
|||
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{B241434A-1642-44CC-AE9A-2012B5C5BD02} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA}
|
||||
{2A076721-6081-4517-8329-B9E5110D6DAC} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA}
|
||||
{21EF76A4-63CC-455D-907C-F86C9E442CEC} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{7F0BF3EA-6985-49F6-8070-0BBA41448BB0} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||
{709C7EBE-EB93-4F6D-9491-D714B0D2E898} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||
{8B3D0F1C-0E38-4E6D-BFF1-C4FDA0CD9815} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
{8A19B1CE-9B62-4440-93B3-152DDBB39D0A} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<BlazorLinkOnBuild>true</BlazorLinkOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor" TreatAsLocalProperty="BlazorLinkOnBuild">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Local alternative to <PackageReference Include="Microsoft.AspNetCore.Blazor.Build" /> -->
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
||||
<BlazorLinkOnBuild>true</BlazorLinkOnBuild>
|
||||
<!-- Local alternative to <RunArguments>blazor serve</RunArguments> -->
|
||||
<RunCommand>dotnet</RunCommand>
|
||||
<RunArguments>run --project ../../src/Microsoft.AspNetCore.Blazor.Cli serve</RunArguments>
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Browser.JS
|
||||
{
|
||||
public static class BlazorBrowserFileProvider
|
||||
{
|
||||
public static IFileProvider Instance = new EmbeddedResourceFileProvider(
|
||||
typeof(BlazorBrowserFileProvider).Assembly,
|
||||
"blazor.");
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,6 @@
|
|||
<WebpackInputs Include="**\*.ts" Exclude="node_modules\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Common\Microsoft.AspNetCore.Blazor.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\Microsoft.AspNetCore.Blazor.BuildTools\ReferenceFromSource.props" />
|
||||
|
||||
<Target Name="EnsureNpmRestored" Condition="!Exists('node_modules')">
|
||||
|
|
|
|||
|
|
@ -1830,15 +1830,6 @@
|
|||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
|
|
@ -1872,6 +1863,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Build.Core;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Cli.Commands
|
||||
{
|
||||
internal class BuildCommand
|
||||
{
|
||||
public static void Command(CommandLineApplication command)
|
||||
{
|
||||
var clientAssemblyPath = command.Argument("assembly",
|
||||
"Specifies the assembly for the Blazor application.");
|
||||
var webRootPath = command.Option("--webroot",
|
||||
"Specifies the path to the directory containing static files to be served",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(clientAssemblyPath.Value))
|
||||
{
|
||||
Console.WriteLine($"ERROR: No value specified for required argument '{clientAssemblyPath.Name}'.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Building Blazor app from {clientAssemblyPath.Value}...");
|
||||
AppBuilder.Execute(clientAssemblyPath.Value, webRootPath.HasValue() ? webRootPath.Value() : null);
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ERROR: {ex.Message}");
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Cli.Commands
|
||||
{
|
||||
internal class BuildIndexHtmlCommand
|
||||
{
|
||||
public static void Command(CommandLineApplication command)
|
||||
{
|
||||
var clientPage = command.Option("--html-page",
|
||||
"Path to the HTML Page containing the Blazor bootstrap script tag.",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var references = command.Option("--reference",
|
||||
"The path from the _bin folder to a given referenced dll file (Typically just the dll name)",
|
||||
CommandOptionType.MultipleValue);
|
||||
|
||||
var outputPath = command.Option("--output",
|
||||
"Path to the output file",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var mainAssemblyPath = command.Argument("assembly",
|
||||
"Path to the assembly containing the entry point of the application.");
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(mainAssemblyPath.Value) ||
|
||||
!clientPage.HasValue() || !references.HasValue() || !outputPath.HasValue())
|
||||
{
|
||||
command.ShowHelp(command.Name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IndexHtmlWriter.UpdateIndex(clientPage.Value(), mainAssemblyPath.Value, references.Values.ToArray(), outputPath.Value());
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ERROR: {ex.Message}");
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 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 Microsoft.Extensions.CommandLineUtils;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Cli.Commands
|
||||
{
|
||||
class ResolveRuntimeDependenciesCommand
|
||||
{
|
||||
public static void Command(CommandLineApplication command)
|
||||
{
|
||||
var references = command.Option("--reference",
|
||||
"Full path to a referenced assembly file",
|
||||
CommandOptionType.MultipleValue);
|
||||
|
||||
var baseClassLibrary = command.Option("--base-class-library",
|
||||
"Full path to a directory in which BCL assemblies can be found",
|
||||
CommandOptionType.MultipleValue);
|
||||
|
||||
var outputPath = command.Option("--output",
|
||||
"Path to the output file that will contain the list with the full paths of the resolved assemblies",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var mainAssemblyPath = command.Argument("assembly",
|
||||
"Path to the assembly containing the entry point of the application.");
|
||||
|
||||
command.OnExecute(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(mainAssemblyPath.Value) ||
|
||||
!baseClassLibrary.HasValue() || !outputPath.HasValue())
|
||||
{
|
||||
command.ShowHelp(command.Name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RuntimeDependenciesResolver.ResolveRuntimeDependencies(
|
||||
mainAssemblyPath.Value,
|
||||
references.Values.ToArray(),
|
||||
baseClassLibrary.Values.ToArray(),
|
||||
outputPath.Value());
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ERROR: {ex.Message}");
|
||||
Console.WriteLine(ex.StackTrace);
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,8 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
};
|
||||
app.HelpOption("-?|-h|--help");
|
||||
|
||||
app.Command("build", BuildCommand.Command);
|
||||
app.Command("build", BuildIndexHtmlCommand.Command);
|
||||
app.Command("resolve-dependencies", ResolveRuntimeDependenciesCommand.Command);
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Build.Core.FileSystem;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core
|
||||
{
|
||||
internal static class AppBuilder
|
||||
{
|
||||
internal static void Execute(string assemblyPath, string webRootPath)
|
||||
{
|
||||
var clientFileSystem = new ClientFileProvider(assemblyPath, webRootPath);
|
||||
var distDirPath = Path.Combine(Path.GetDirectoryName(assemblyPath), "dist");
|
||||
FileUtil.WriteFileProviderToDisk(clientFileSystem, distDirPath, clean: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Mono.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
||||
{
|
||||
internal class ClientFileProvider : CompositeMountedFileProvider
|
||||
{
|
||||
public ClientFileProvider(string clientAssemblyPath, string webRootPath)
|
||||
: base(GetContents(clientAssemblyPath, webRootPath))
|
||||
{
|
||||
}
|
||||
|
||||
private static (string, IFileProvider)[] GetContents(string clientAssemblyPath, string webRootPath)
|
||||
{
|
||||
var fileProviders = new List<(string, IFileProvider)>();
|
||||
|
||||
// There must always be a client assembly, and we always supply a /_framework
|
||||
// directory containing everything needed to execute it
|
||||
if (!File.Exists(clientAssemblyPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Could not find client assembly file at '{clientAssemblyPath}'.", clientAssemblyPath);
|
||||
}
|
||||
var frameworkFileProvider = new FrameworkFileProvider(clientAssemblyPath);
|
||||
fileProviders.Add(("/_framework", frameworkFileProvider));
|
||||
|
||||
// The web root directory is optional. If it exists and contains /index.html, then
|
||||
// we will inject the relevant <script> tag and supply that file. Otherwise, we just
|
||||
// don't supply an /index.html file.
|
||||
if (TryCreateIndexHtmlFileProvider(
|
||||
webRootPath, clientAssemblyPath, frameworkFileProvider, out var indexHtmlFileProvider))
|
||||
{
|
||||
fileProviders.Add(("/", indexHtmlFileProvider));
|
||||
}
|
||||
|
||||
return fileProviders.ToArray();
|
||||
}
|
||||
|
||||
private static bool TryCreateIndexHtmlFileProvider(
|
||||
string webRootPath, string assemblyPath, IFileProvider frameworkFileProvider, out IFileProvider result)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(webRootPath))
|
||||
{
|
||||
var path = Path.Combine(webRootPath, "index.html");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var template = File.ReadAllText(path);
|
||||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
var assemblyEntryPoint = GetAssemblyEntryPoint(assemblyPath);
|
||||
var binFiles = frameworkFileProvider.GetDirectoryContents("/_bin");
|
||||
result = new IndexHtmlFileProvider(template, assemblyName, assemblyEntryPoint, binFiles);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetAssemblyEntryPoint(string assemblyPath)
|
||||
{
|
||||
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath))
|
||||
{
|
||||
var entryPoint = assemblyDefinition.EntryPoint;
|
||||
if (entryPoint == null)
|
||||
{
|
||||
throw new ArgumentException($"The assembly at {assemblyPath} has no specified entry point.");
|
||||
}
|
||||
|
||||
return $"{entryPoint.DeclaringType.FullName}::{entryPoint.Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Browser.JS;
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using Microsoft.AspNetCore.Blazor.Mono;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
||||
{
|
||||
internal class FrameworkFileProvider : CompositeMountedFileProvider
|
||||
{
|
||||
public FrameworkFileProvider(string clientAssemblyPath)
|
||||
: base(GetContents(clientAssemblyPath))
|
||||
{
|
||||
}
|
||||
|
||||
private static (string, IFileProvider)[] GetContents(string clientAssemblyPath)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
("/", MonoStaticFileProvider.JsFiles),
|
||||
("/", BlazorBrowserFileProvider.Instance),
|
||||
("/_bin", CreateBinDirFileProvider(clientAssemblyPath))
|
||||
};
|
||||
}
|
||||
|
||||
private static IFileProvider CreateBinDirFileProvider(string clientAssemblyPath)
|
||||
=> new ReferencedAssemblyFileProvider(
|
||||
Path.GetFileNameWithoutExtension(clientAssemblyPath),
|
||||
new ReferencedAssemblyResolver(
|
||||
MonoStaticFileProvider.BclFiles,
|
||||
Path.GetDirectoryName(clientAssemblyPath)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
||||
{
|
||||
internal class ReferencedAssemblyFileProvider : InMemoryFileProvider
|
||||
{
|
||||
public ReferencedAssemblyFileProvider(string rootAssemblyName, ReferencedAssemblyResolver resolver)
|
||||
: base(ComputeContents(rootAssemblyName, resolver))
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<(string, byte[])> ComputeContents(
|
||||
string rootAssemblyName,
|
||||
ReferencedAssemblyResolver resolver)
|
||||
{
|
||||
var foundAssemblies = new Dictionary<string, ReferencedAssemblyInfo>();
|
||||
AddWithReferencesRecursive(rootAssemblyName, resolver, foundAssemblies);
|
||||
|
||||
return foundAssemblies.Values.Select(assembly => (
|
||||
$"/{assembly.Definition.Name.Name}.dll",
|
||||
assembly.Data));
|
||||
}
|
||||
|
||||
private static void AddWithReferencesRecursive(
|
||||
string name,
|
||||
ReferencedAssemblyResolver resolver,
|
||||
IDictionary<string, ReferencedAssemblyInfo> results)
|
||||
{
|
||||
if (resolver.TryResolve(name, out var assemblyBytes))
|
||||
{
|
||||
var assemblyInfo = new ReferencedAssemblyInfo(assemblyBytes);
|
||||
results.Add(assemblyInfo.Definition.Name.Name, assemblyInfo);
|
||||
|
||||
var childReferencesToAdd = assemblyInfo.Definition.Modules
|
||||
.SelectMany(module => module.AssemblyReferences)
|
||||
.Select(childReference => childReference.Name)
|
||||
.Where(childReferenceName => !results.ContainsKey(childReferenceName));
|
||||
foreach (var childReferenceName in childReferencesToAdd)
|
||||
{
|
||||
AddWithReferencesRecursive(childReferenceName, resolver, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferencedAssemblyInfo
|
||||
{
|
||||
public byte[] Data { get; }
|
||||
public AssemblyDefinition Definition { get; }
|
||||
|
||||
public ReferencedAssemblyInfo(byte[] rawData)
|
||||
{
|
||||
Data = rawData;
|
||||
|
||||
using (var ms = new MemoryStream(rawData))
|
||||
{
|
||||
Definition = AssemblyDefinition.ReadAssembly(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
||||
{
|
||||
internal class ReferencedAssemblyResolver
|
||||
{
|
||||
private readonly IFileProvider _bcl;
|
||||
private readonly string _searchDirectory;
|
||||
|
||||
public ReferencedAssemblyResolver(IFileProvider bcl, string searchDirectory)
|
||||
{
|
||||
_bcl = bcl;
|
||||
_searchDirectory = searchDirectory;
|
||||
}
|
||||
|
||||
public bool TryResolve(string name, out byte[] assemblyBytes)
|
||||
{
|
||||
var filename = $"{name}.dll";
|
||||
if (SearchInFileProvider(_bcl, string.Empty, filename, out var fileInfo))
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var fileInfoStream = fileInfo.CreateReadStream())
|
||||
{
|
||||
fileInfoStream.CopyTo(ms);
|
||||
assemblyBytes = ms.ToArray();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var searchDirPath = Path.Combine(_searchDirectory, filename);
|
||||
if (File.Exists(searchDirPath))
|
||||
{
|
||||
assemblyBytes = File.ReadAllBytes(searchDirPath);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
assemblyBytes = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SearchInFileProvider(IFileProvider fileProvider, string searchRootDirNoTrailingSlash, string name, out IFileInfo file)
|
||||
{
|
||||
var possibleFullPath = $"{searchRootDirNoTrailingSlash}/{name}";
|
||||
var possibleResult = fileProvider.GetFileInfo(possibleFullPath);
|
||||
if (possibleResult.Exists)
|
||||
{
|
||||
file = possibleResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
var childDirs = fileProvider.GetDirectoryContents(searchRootDirNoTrailingSlash)
|
||||
.Where(item => item.IsDirectory);
|
||||
foreach (var childDir in childDirs)
|
||||
{
|
||||
if (SearchInFileProvider(fileProvider, childDir.PhysicalPath, name, out file))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
file = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core
|
||||
{
|
||||
internal static class FileUtil
|
||||
{
|
||||
public static void WriteFileProviderToDisk(IFileProvider fileProvider, string outputDir, bool clean)
|
||||
{
|
||||
Directory.CreateDirectory(outputDir);
|
||||
|
||||
if (clean)
|
||||
{
|
||||
CleanDirectory(outputDir);
|
||||
}
|
||||
|
||||
WriteFileProviderToDisk(fileProvider, string.Empty, outputDir);
|
||||
}
|
||||
|
||||
private static void WriteFileProviderToDisk(IFileProvider fileProvider, string subpath, string outputDir)
|
||||
{
|
||||
foreach (var item in fileProvider.GetDirectoryContents(subpath))
|
||||
{
|
||||
var itemOutputPath = Path.Combine(outputDir, item.Name);
|
||||
if (item.IsDirectory)
|
||||
{
|
||||
Directory.CreateDirectory(itemOutputPath);
|
||||
WriteFileProviderToDisk(fileProvider, item.PhysicalPath, itemOutputPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var stream = item.CreateReadStream())
|
||||
using (var fileStream = File.Open(itemOutputPath, FileMode.Create))
|
||||
{
|
||||
stream.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanDirectory(string path)
|
||||
{
|
||||
foreach (var itemName in Directory.GetFileSystemEntries(path))
|
||||
{
|
||||
var itemPath = Path.Combine(path, itemName);
|
||||
if (File.GetAttributes(itemPath).HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
Directory.Delete(itemPath, recursive: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Delete(itemPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +1,73 @@
|
|||
// 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 Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AngleSharp.Parser.Html;
|
||||
using System.Text;
|
||||
using AngleSharp;
|
||||
using AngleSharp.Html;
|
||||
using System;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
||||
namespace Microsoft.AspNetCore.Blazor.Build
|
||||
{
|
||||
internal class IndexHtmlFileProvider : InMemoryFileProvider
|
||||
internal class IndexHtmlWriter
|
||||
{
|
||||
public IndexHtmlFileProvider(
|
||||
string htmlTemplate,
|
||||
string assemblyName,
|
||||
string assemblyEntryPoint,
|
||||
IEnumerable<IFileInfo> binFiles) : base(ComputeContents(htmlTemplate, assemblyName, assemblyEntryPoint, binFiles))
|
||||
public static void UpdateIndex(string path, string assemblyPath, IEnumerable<string> references, string outputPath)
|
||||
{
|
||||
var template = GetTemplate(path);
|
||||
if (template == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
var entryPoint = GetAssemblyEntryPoint(assemblyPath);
|
||||
var updatedContent = GetIndexHtmlContents(template, assemblyName, entryPoint, references);
|
||||
var normalizedOutputPath = Normalize(outputPath);
|
||||
Console.WriteLine("Writing index to: " + normalizedOutputPath);
|
||||
File.WriteAllText(normalizedOutputPath, updatedContent);
|
||||
}
|
||||
|
||||
private static IEnumerable<(string, byte[])> ComputeContents(
|
||||
string htmlTemplate,
|
||||
string assemblyName,
|
||||
string assemblyEntryPoint,
|
||||
IEnumerable<IFileInfo> binFiles)
|
||||
private static string Normalize(string outputPath) =>
|
||||
Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileName(outputPath).ToLowerInvariant());
|
||||
|
||||
private static string GetTemplate(string path)
|
||||
{
|
||||
if (htmlTemplate != null)
|
||||
var fileName = Path.GetFileName(path);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var html = GetIndexHtmlContents(htmlTemplate, assemblyName, assemblyEntryPoint, binFiles);
|
||||
var htmlBytes = Encoding.UTF8.GetBytes(html);
|
||||
yield return ("/index.html", htmlBytes);
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
if (Directory.Exists(Path.GetDirectoryName(path)))
|
||||
{
|
||||
var files = Directory.EnumerateFiles(Path.GetDirectoryName(path))
|
||||
.OrderBy(f => f);
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (string.Equals(fileName, Path.GetFileName(file), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return File.ReadAllText(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetAssemblyEntryPoint(string assemblyPath)
|
||||
{
|
||||
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath))
|
||||
{
|
||||
var entryPoint = assemblyDefinition.EntryPoint;
|
||||
if (entryPoint == null)
|
||||
{
|
||||
throw new ArgumentException($"The assembly at {assemblyPath} has no specified entry point.");
|
||||
}
|
||||
|
||||
return $"{entryPoint.DeclaringType.FullName}::{entryPoint.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,11 +89,11 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
|||
/// responsible for completing the Blazor boot process.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private static string GetIndexHtmlContents(
|
||||
public static string GetIndexHtmlContents(
|
||||
string htmlTemplate,
|
||||
string assemblyName,
|
||||
string assemblyEntryPoint,
|
||||
IEnumerable<IFileInfo> binFiles)
|
||||
IEnumerable<string> binFiles)
|
||||
{
|
||||
var resultBuilder = new StringBuilder();
|
||||
|
||||
|
|
@ -107,7 +141,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case HtmlTokenType.EndTag:
|
||||
// If this is an end tag corresponding to the Blazor boot script tag, we
|
||||
// can switch back into the mode of emitting the original source text
|
||||
|
|
@ -136,14 +170,12 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
|
|||
StringBuilder resultBuilder,
|
||||
string assemblyName,
|
||||
string assemblyEntryPoint,
|
||||
IEnumerable<IFileInfo> binFiles,
|
||||
IEnumerable<string> binFiles,
|
||||
List<KeyValuePair<string, string>> attributes)
|
||||
{
|
||||
var assemblyNameWithExtension = $"{assemblyName}.dll";
|
||||
var referenceNames = binFiles
|
||||
.Where(file => !string.Equals(file.Name, assemblyNameWithExtension))
|
||||
.Select(file => file.Name);
|
||||
var referencesAttribute = string.Join(",", referenceNames.ToArray());
|
||||
|
||||
var referencesAttribute = string.Join(",", binFiles.ToArray());
|
||||
|
||||
var attributesDict = attributes.ToDictionary(x => x.Key, x => x.Value);
|
||||
attributesDict.Remove("type");
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build
|
||||
{
|
||||
internal class RuntimeDependenciesResolver
|
||||
{
|
||||
public static void ResolveRuntimeDependencies(
|
||||
string entryPoint,
|
||||
string[] applicationDependencies,
|
||||
string[] monoBclDirectories,
|
||||
string outputFile)
|
||||
{
|
||||
var paths = ResolveRuntimeDependenciesCore(entryPoint, applicationDependencies, monoBclDirectories);
|
||||
File.WriteAllLines(outputFile, paths);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ResolveRuntimeDependenciesCore(
|
||||
string entryPoint,
|
||||
string[] applicationDependencies,
|
||||
string[] monoBclDirectories)
|
||||
{
|
||||
var assembly = new AssemblyEntry(entryPoint, AssemblyDefinition.ReadAssembly(entryPoint));
|
||||
|
||||
var dependencies = applicationDependencies
|
||||
.Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
|
||||
.ToArray();
|
||||
|
||||
var bcl = monoBclDirectories
|
||||
.SelectMany(d => Directory.EnumerateFiles(d, "*.dll").Select(f => Path.Combine(d, f)))
|
||||
.Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
|
||||
.ToArray();
|
||||
|
||||
var assemblyResolutionContext = new AssemblyResolutionContext(
|
||||
assembly,
|
||||
dependencies,
|
||||
bcl);
|
||||
|
||||
assemblyResolutionContext.ResolveAssemblies();
|
||||
|
||||
var paths = assemblyResolutionContext.Results.Select(r => r.Path);
|
||||
return paths;
|
||||
}
|
||||
|
||||
public class AssemblyResolutionContext
|
||||
{
|
||||
public AssemblyResolutionContext(
|
||||
AssemblyEntry assembly,
|
||||
AssemblyEntry[] dependencies,
|
||||
AssemblyEntry[] bcl)
|
||||
{
|
||||
Assembly = assembly;
|
||||
Dependencies = dependencies;
|
||||
Bcl = bcl;
|
||||
}
|
||||
|
||||
public AssemblyEntry Assembly { get; }
|
||||
public AssemblyEntry[] Dependencies { get; }
|
||||
public AssemblyEntry[] Bcl { get; }
|
||||
|
||||
public IList<AssemblyEntry> Results { get; } = new List<AssemblyEntry>();
|
||||
|
||||
internal void ResolveAssemblies()
|
||||
{
|
||||
var visitedAssemblies = new Dictionary<string, AssemblyEntry>();
|
||||
var pendingAssemblies = new Stack<AssemblyNameReference>();
|
||||
pendingAssemblies.Push(Assembly.Definition.Name);
|
||||
ResolveAssembliesCore();
|
||||
|
||||
void ResolveAssembliesCore()
|
||||
{
|
||||
while (pendingAssemblies.TryPop(out var current))
|
||||
{
|
||||
if (!visitedAssemblies.ContainsKey(current.Name))
|
||||
{
|
||||
var resolved = Resolve(current);
|
||||
visitedAssemblies[current.Name] = resolved;
|
||||
Results.Add(resolved);
|
||||
var references = GetAssemblyReferences(resolved);
|
||||
foreach (var reference in references)
|
||||
{
|
||||
pendingAssemblies.Push(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<AssemblyNameReference> GetAssemblyReferences(AssemblyEntry current) =>
|
||||
current.Definition.Modules.SelectMany(m => m.AssemblyReferences);
|
||||
|
||||
AssemblyEntry Resolve(AssemblyNameReference current)
|
||||
{
|
||||
if (Assembly.Definition.Name.Name == current.Name)
|
||||
{
|
||||
return Assembly;
|
||||
}
|
||||
|
||||
var referencedAssemblyCandidate = FindCandidate(current, Dependencies);
|
||||
var bclAssemblyCandidate = FindCandidate(current, Bcl);
|
||||
|
||||
// Resolution logic. For right now, we will prefer the mono BCL version of a given
|
||||
// assembly if there is a candidate assembly and an equivalent mono assembly.
|
||||
if (bclAssemblyCandidate != null)
|
||||
{
|
||||
return bclAssemblyCandidate;
|
||||
}
|
||||
|
||||
return referencedAssemblyCandidate;
|
||||
}
|
||||
|
||||
AssemblyEntry FindCandidate(AssemblyNameReference current, AssemblyEntry[] candidates)
|
||||
{
|
||||
// Do simple name match. Assume no duplicates.
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (current.Name == candidate.Definition.Name.Name)
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString(),nq}")]
|
||||
public class AssemblyEntry
|
||||
{
|
||||
public AssemblyEntry(string path, AssemblyDefinition definition)
|
||||
{
|
||||
Path = path;
|
||||
Definition = definition;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public AssemblyDefinition Definition { get; set; }
|
||||
|
||||
public override string ToString() => Definition.FullName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Browser.JS\Microsoft.AspNetCore.Blazor.Browser.JS.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Mono\Microsoft.AspNetCore.Blazor.Mono.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Razor.Extensions\Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor\Microsoft.AspNetCore.Blazor.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
|
|
|
|||
|
|
@ -11,5 +11,8 @@
|
|||
<file src="build\**" target="build" />
|
||||
<file src="targets\**" target="targets" />
|
||||
<file src="$publishdir$\netcoreapp2.0\**\*" target="tools/" />
|
||||
<file src="..\mono\dist\optimized\**\*" target="tools\mono" />
|
||||
<file src="..\mono\tools\binaries\illink\**\*.*" target="tools\illink" />
|
||||
<file src="..\Microsoft.AspNetCore.Blazor.Browser.JS\dist\blazor.js" target="tools\blazor\blazor.js" />
|
||||
</files>
|
||||
</package>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<Project>
|
||||
|
||||
<!--
|
||||
Importing this file is equivalent to having:
|
||||
<PackageDependency Include="Microsoft.AspNetCore.Blazor.Build" />
|
||||
|
|
@ -7,8 +8,22 @@
|
|||
|
||||
This is only intended for use by other projects in this repo.
|
||||
-->
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)targets\All.targets" />
|
||||
<PropertyGroup>
|
||||
<BlazorBuildReferenceFromSource>true</BlazorBuildReferenceFromSource>
|
||||
<BlazorMonoRuntimeBasePath>$(MSBuildThisFileDirectory)../mono/</BlazorMonoRuntimeBasePath>
|
||||
<MonoLinkerPath>$(BlazorMonoRuntimeBasePath)tools/binaries/illink/illink.dll</MonoLinkerPath>
|
||||
<MonoBaseClassLibraryPath>$(BlazorMonoRuntimeBasePath)dist/optimized/bcl/</MonoBaseClassLibraryPath>
|
||||
<MonoBaseClassLibraryFacadesPath>$(BlazorMonoRuntimeBasePath)dist/optimized/bcl/Facades/</MonoBaseClassLibraryFacadesPath>
|
||||
<MonoAsmjsRuntimePath>$(BlazorMonoRuntimeBasePath)dist/optimized/asmjs/</MonoAsmjsRuntimePath>
|
||||
<MonoWasmRuntimePath>$(BlazorMonoRuntimeBasePath)dist/optimized/wasm/</MonoWasmRuntimePath>
|
||||
<BlazorJsPath>$(MSBuildThisFileDirectory)../Microsoft.AspNetCore.Blazor.Browser.JS/dist/blazor.js</BlazorJsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)targets/All.props" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)targets/All.targets" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)../mono/mono.targets" />
|
||||
|
||||
<Target Name="ProvisionMono" DependsOnTargets="OptimizeMono" BeforeTargets="BuildBlazorBuildBinary" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="$(RazorPackageVersion)"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\..\targets\All.props" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<Project>
|
||||
<Import Project="Blazor.MonoRuntime.props" />
|
||||
</Project>
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<Import Project="RazorCompilation.targets" />
|
||||
<Import Project="Blazor.MonoRuntime.targets" />
|
||||
|
||||
<Target Name="GenerateBlazorMetadataFile" BeforeTargets="GetCopyToOutputDirectoryItems">
|
||||
<PropertyGroup>
|
||||
|
|
@ -25,14 +26,4 @@
|
|||
<ContentWithTargetPath Include="$(BlazorMetadataFilePath)" TargetPath="$(BlazorMetadataFileName)" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="BuildBlazorApp" AfterTargets="Build" Inputs="$(ProjectDir)**" Outputs="$(ProjectDir)$(OutDir)dist\**">
|
||||
<PropertyGroup>
|
||||
<WebRootName>wwwroot</WebRootName>
|
||||
<WebRootPath>$(ProjectDir)$(WebRootName)</WebRootPath>
|
||||
<WebRootParam Condition="Exists('$(WebRootPath)')">--webroot "$(WebRootPath)"</WebRootParam>
|
||||
</PropertyGroup>
|
||||
<!-- TODO: Find the correct time to run this (right after assemblies were written) -->
|
||||
<Exec Command="$(BlazorBuildExe) build "$(ProjectDir)$(OutDir)$(AssemblyName).dll" $(WebRootParam)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<Project>
|
||||
<PropertyGroup Condition="$(BlazorBuildReferenceFromSource) == ''" Label="Blazor build Inputs">
|
||||
<BlazorMonoRuntimeBasePath>$(MSBuildThisFileDirectory)../</BlazorMonoRuntimeBasePath>
|
||||
<MonoLinkerPath>$(BlazorMonoRuntimeBasePath)tools/illink/illink.dll</MonoLinkerPath>
|
||||
<MonoBaseClassLibraryPath>$(BlazorMonoRuntimeBasePath)tools/mono/bcl/</MonoBaseClassLibraryPath>
|
||||
<MonoBaseClassLibraryFacadesPath>$(BlazorMonoRuntimeBasePath)tools/mono/bcl/Facades/</MonoBaseClassLibraryFacadesPath>
|
||||
<MonoAsmjsRuntimePath>$(BlazorMonoRuntimeBasePath)tools/mono/asmjs/</MonoAsmjsRuntimePath>
|
||||
<MonoWasmRuntimePath>$(BlazorMonoRuntimeBasePath)tools/mono/wasm/</MonoWasmRuntimePath>
|
||||
<BlazorJsPath>$(BlazorMonoRuntimeBasePath)tools/blazor/</BlazorJsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Blazor build outputs">
|
||||
<AdditionalMonoLinkerOptions>-c link -u link -t --verbose </AdditionalMonoLinkerOptions>
|
||||
<BaseBlazorRuntimeOutputPath>dist/_framework/</BaseBlazorRuntimeOutputPath>
|
||||
<BaseBlazorRuntimeBinOutputPath>$(BaseBlazorRuntimeOutputPath)_bin/</BaseBlazorRuntimeBinOutputPath>
|
||||
<BaseBlazorRuntimeAsmjsOutputPath>$(BaseBlazorRuntimeOutputPath)asmjs/</BaseBlazorRuntimeAsmjsOutputPath>
|
||||
<BaseBlazorRuntimeWasmOutputPath>$(BaseBlazorRuntimeOutputPath)wasm/</BaseBlazorRuntimeWasmOutputPath>
|
||||
<BaseBlazorJsOutputPath>$(BaseBlazorRuntimeOutputPath)</BaseBlazorJsOutputPath>
|
||||
<BaseBlazorIntermediateOutputPath>blazor/</BaseBlazorIntermediateOutputPath>
|
||||
<BlazorWebRootName>wwwroot/</BlazorWebRootName>
|
||||
<BlazorIndexHtmlName>Index.html</BlazorIndexHtmlName>
|
||||
<BlazorOutputIndexHtmlName>$(BlazorIndexHtmlName.ToLowerInvariant())</BlazorOutputIndexHtmlName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MonoBCLFile Include="$(MonoBaseClassLibraryPath)*.dll" />
|
||||
<MonoBCLFacadeFile Include="$(MonoBaseClassLibraryFacadesPath)*.dll" />
|
||||
<MonoAsmjsFile Include="$(MonoAsmjsRuntimePath)**/*.*" />
|
||||
<MonoWasmFile Include="$(MonoWasmRuntimePath)**/*.*" />
|
||||
<BlazorJsFile Include="$(BlazorJsPath)" />
|
||||
<MonoBaseClassLibraryFolder Include="$(MonoBaseClassLibraryPath);$(MonoBaseClassLibraryFacadesPath)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,561 @@
|
|||
<Project>
|
||||
|
||||
<Target
|
||||
Name="_BlazorCopyFilesToOutputDirectory"
|
||||
DependsOnTargets="PrepareBlazorOutputs"
|
||||
Inputs="@(BlazorItemOutput)"
|
||||
Outputs="@(BlazorItemOutput->'%(TargetOutputPath)')"
|
||||
AfterTargets="CopyFilesToOutputDirectory">
|
||||
|
||||
<!-- Copy the blazor output files -->
|
||||
<Copy
|
||||
SourceFiles="@(BlazorItemOutput)"
|
||||
DestinationFiles="@(BlazorItemOutput->'%(TargetOutputPath)')"
|
||||
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
||||
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
||||
Retries="$(CopyRetryCount)"
|
||||
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
|
||||
UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
|
||||
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)"
|
||||
Condition="'@(BlazorItemOutput)' != '' and '$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'">
|
||||
</Copy>
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_BlazorBuildReport"
|
||||
AfterTargets="_BlazorCopyFilesToOutputDirectory">
|
||||
|
||||
<ItemGroup>
|
||||
<_BlazorStatisticsOutput Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_BlazorStatisticsReportImportance Condition="'$(BlazorOutputStatistics)' == ''">normal</_BlazorStatisticsReportImportance>
|
||||
<_BlazorStatisticsReportImportance Condition="'$(BlazorOutputStatistics)' != ''">high</_BlazorStatisticsReportImportance>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Importance="high" Text="Blazor Build result -> @(_BlazorStatisticsOutput->Distinct()->Count()) files in $(ProjectDir)$(OutputPath)dist" />
|
||||
<Message Importance="$(_BlazorStatisticsReportImportance)" Text="%(_BlazorStatisticsOutput.Identity)" />
|
||||
</Target>
|
||||
|
||||
<!-- Preparing blazor files for output:
|
||||
PrepareBlazorOutputs
|
||||
_PrepareBlazorOutputConfiguration
|
||||
_DefineBlazorCommonInputs
|
||||
_BlazorResolveOutputBinaries
|
||||
When link on build:
|
||||
_GenerateLinkerDescriptor
|
||||
_CollectBlazorLinkerDescriptors
|
||||
_LinkBlazorApplication
|
||||
_CollectLinkerOutputs
|
||||
When don't link on build:
|
||||
_CollectResolvedAssemblies
|
||||
_ResolveBlazorApplicationAssemblies
|
||||
_ReadResolvedBlazorApplicationAssemblies
|
||||
_IntermediateCopyBlazorApplicationAssemblies
|
||||
_TouchBlazorApplicationAssemblies
|
||||
_GenerateBlazorIndexHtml
|
||||
_BlazorCopyFilesToOutputDirectory
|
||||
|
||||
The process for doing builds goes as follows:
|
||||
Produce a hash file with the Hash SDK task and write that hash to a marker file.
|
||||
Produce a marker file that saves whether we are linking or not in this build so that we can take that as
|
||||
input in future builds and do the correct thing for incremental builds.
|
||||
We only produce marker files when the input changes, if the input doesn't change the marker stays the
|
||||
same.
|
||||
|
||||
If we are linking on this build the process is as follows:
|
||||
1) We determine if there are linker descriptors available, if not generate one.
|
||||
2) Collect the list of linker descriptors and create a marker for the linker if it doesn't exist or changed
|
||||
from a previous build.
|
||||
3) Run the linker in case the linker inputs marker is newer than the linker result file.
|
||||
4) Collect the outputs from the linker.
|
||||
|
||||
If we are not linking in this build the process is as follows:
|
||||
1) Resolve the assemblies for the application only if the inputs marker is newer than the resolved assemblies
|
||||
result file.
|
||||
2) Read the result file with the resolved assemblies.
|
||||
3) Copy the resolved assemblies to an intermediate folder.
|
||||
4) In case we are switching from linking to not linking, touch the files in the intermediate folder to ensure
|
||||
that updated versions of the files get copied to the output folder.
|
||||
|
||||
Once the binary outputs are resolved:
|
||||
1) Create a marker file with the resolved assemblies and the index html file as inputs.
|
||||
2) If the marker file is newer than the index.html in the output folder, regenerate the
|
||||
index.html
|
||||
|
||||
Once all the outputs are resolved (static content + binary outputs + index.html)
|
||||
Copy all the files to the output folder.
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<PrepareBlazorOutputs>
|
||||
_PrepareBlazorOutputConfiguration;
|
||||
_DefineBlazorCommonInputs;
|
||||
_BlazorResolveOutputBinaries;
|
||||
_GenerateBlazorIndexHtml;
|
||||
</PrepareBlazorOutputs>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PrepareBlazorOutputs" DependsOnTargets="$(PrepareBlazorOutputs)" />
|
||||
|
||||
<!--
|
||||
Prepare blazor outputs preamble:
|
||||
* Creates updated marker files (if necessary) for incremental builds.
|
||||
* Computes intermediate and final output paths.
|
||||
* Computes the list of static items to copy to the output folder.
|
||||
-->
|
||||
|
||||
<Target Name="_PrepareBlazorOutputConfiguration">
|
||||
<!--
|
||||
This task produces all the "final" paths for all the files we need to produce the final output.
|
||||
|
||||
The final folder is something like bin/<<Configuration>>/<<TargetFramework>>/dist
|
||||
/_framework/_bin <- This will contain either the BCL + app assemblies or the result of linking the app.
|
||||
/_framework/asmjs <- This will contain the asmjs runtime copied from the nuget package.
|
||||
/_framework/wasm <- This will contain the wsm runtime copied from the nuget package.
|
||||
/_framework/blazor.js <- This is the blazor.js file copied from the nuget package.
|
||||
/index.html <- This is the optional index.html file generated from wwwroot/Index.html in case it's present. It
|
||||
will be canonicalized to index.html
|
||||
|
||||
This task also defines some intermediate paths that we will use:
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker <- This will be used to create the output from the linker.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt <- This will be used to save the output files from
|
||||
the linker and use that as marker to identify whether or not we need to run the linker.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker.descriptor.xml <- This will be used to generate an XML descriptor
|
||||
for the mono linker.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/inputs.basic.cache <- This is the marker file to track the inputs common
|
||||
inputs to the output generation process.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/inputs.linkerswitch.cache <- This is the marker file to track the
|
||||
switch from linking to not linking and viceversa.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/inputs.linker.cache <- This is the marker file to track the inputs
|
||||
to the linker.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/resolvedassemblies/ <- This will be used to store the resolved assemblies
|
||||
before copying them to the output when linking is not enabled.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/resolved.assemblies.txt <- This keeps track of all the resolved assemblies.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/index.html <- The generated index.html with the updated blazor script tag.
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/inputs.index.cache <- The marker file that track whether index.html needs to
|
||||
be regenerated.
|
||||
-->
|
||||
|
||||
<PropertyGroup Label="Build properties">
|
||||
<_BlazorShouldLinkApplicationAssemblies Condition="$(BlazorLinkOnBuild) == 'false'"></_BlazorShouldLinkApplicationAssemblies>
|
||||
<_BlazorShouldLinkApplicationAssemblies Condition="$(BlazorLinkOnBuild) == 'true'">true</_BlazorShouldLinkApplicationAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Blazor HTML inputs">
|
||||
<BlazorWebRootPath>$(ProjectDir)$(BlazorWebRootName)</BlazorWebRootPath>
|
||||
<BlazorIndexHtml>$(BlazorWebRootPath)$(BlazorIndexHtmlName)</BlazorIndexHtml>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Static content to copy to the output folder">
|
||||
<BlazorItemOutput Include="@(MonoAsmjsFile)">
|
||||
<TargetOutputPath>$(ProjectDir)$(OutputPath)$(BaseBlazorRuntimeAsmjsOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>AsmJs</Type>
|
||||
<IsStatic>true</IsStatic>
|
||||
</BlazorItemOutput>
|
||||
<BlazorItemOutput Include="@(MonoWasmFile)">
|
||||
<TargetOutputPath>$(ProjectDir)$(OutputPath)$(BaseBlazorRuntimeWasmOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>WebAssembly</Type>
|
||||
<IsStatic>true</IsStatic>
|
||||
</BlazorItemOutput>
|
||||
<BlazorItemOutput Include="@(BlazorJsFile)">
|
||||
<TargetOutputPath>$(ProjectDir)$(OutputPath)$(BaseBlazorJsOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>BlazorRuntime</Type>
|
||||
<IsStatic>true</IsStatic>
|
||||
</BlazorItemOutput>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Intermediate output paths">
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor -->
|
||||
<BlazorIntermediateOutputPath>$(ProjectDir)$(IntermediateOutputPath)$(BaseBlazorIntermediateOutputPath)</BlazorIntermediateOutputPath>
|
||||
|
||||
<!-- Common marker files paths -->
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.basic.cache -->
|
||||
<BlazorBuildCommonInputsCache>$(BlazorIntermediateOutputPath)inputs.basic.cache</BlazorBuildCommonInputsCache>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linkerswitch.cache -->
|
||||
<BlazorBuildLinkerSwitchInputsCache>$(BlazorIntermediateOutputPath)inputs.linkerswitch.cache</BlazorBuildLinkerSwitchInputsCache>
|
||||
|
||||
<!-- Linker paths and marker files -->
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.linker.cache -->
|
||||
<BlazorBuildLinkerInputsCache>$(BlazorIntermediateOutputPath)inputs.linker.cache</BlazorBuildLinkerInputsCache>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker.descriptor.xml -->
|
||||
<GeneratedBlazorLinkerDescriptor>$(BlazorIntermediateOutputPath)linker.descriptor.xml</GeneratedBlazorLinkerDescriptor>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/linker/ -->
|
||||
<BlazorIntermediateLinkerOutputPath>$(BlazorIntermediateOutputPath)linker/</BlazorIntermediateLinkerOutputPath>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/linked.assemblies.txt -->
|
||||
<BlazorIntermediateLinkerResultFilePath>$(BlazorIntermediateOutputPath)linked.assemblies.txt</BlazorIntermediateLinkerResultFilePath>
|
||||
|
||||
<!-- Resolved assemblies paths and marker files -->
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/resolvedassemblies/ -->
|
||||
<BlazorIntermediateResolvedApplicationAssembliesOutputPath>$(BlazorIntermediateOutputPath)resolvedassemblies/</BlazorIntermediateResolvedApplicationAssembliesOutputPath>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/resolved.assemblies.txt -->
|
||||
<BlazorResolvedAssembliesOutputPath>$(BlazorIntermediateOutputPath)resolved.assemblies.txt</BlazorResolvedAssembliesOutputPath>
|
||||
|
||||
<!-- Index.html related paths and markers -->
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/index.html -->
|
||||
<BlazorIndexHtmlOutputPath>$(BlazorIntermediateOutputPath)$(BlazorOutputIndexHtmlName)</BlazorIndexHtmlOutputPath>
|
||||
|
||||
<!-- /obj/<<configuration>>/<<targetframework>>/blazor/inputs.index.cache -->
|
||||
<BlazorBuildIndexInputsCache>$(BlazorIntermediateOutputPath)inputs.index.cache</BlazorBuildIndexInputsCache>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Final output paths">
|
||||
<BlazorRuntimeBinOutputPath>$(ProjectDir)$(OutputPath)$(BaseBlazorRuntimeBinOutputPath)</BlazorRuntimeBinOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<MakeDir Directories="$(BlazorIntermediateOutputPath)" />
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_DefineBlazorCommonInputs">
|
||||
|
||||
<ItemGroup>
|
||||
<_BlazorCommonInput Include="@(IntermediateAssembly)" />
|
||||
<_BlazorCommonInput Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll'))" />
|
||||
<_BlazorCommonInput Include="$(_BlazorShouldLinkApplicationAssemblies)" />
|
||||
<_BlazorLinkingOption Condition="_BlazorShouldLinkApplicationAssemblies == ''" Include="false" />
|
||||
<_BlazorLinkingOption Condition="_BlazorShouldLinkApplicationAssemblies != ''" Include="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<Hash ItemsToHash="@(_BlazorCommonInput)">
|
||||
<Output TaskParameter="HashResult" PropertyName="_BlazorBuildBasicInputHash" />
|
||||
</Hash>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_BlazorBuildBasicInputHash)"
|
||||
File="$(BlazorBuildCommonInputsCache)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<!-- Switch to detect when we switch from linking to not linking and viceversa -->
|
||||
<WriteLinesToFile
|
||||
Lines="@(_BlazorLinkingOption)"
|
||||
File="$(BlazorBuildLinkerSwitchInputsCache)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(BlazorBuildLinkerSwitchInputsCache)" />
|
||||
<FileWrites Include="$(BlazorBuildCommonInputsCache)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_BlazorResolveOutputBinaries" DependsOnTargets="_CollectLinkerOutputs;_CollectResolvedAssemblies" />
|
||||
|
||||
<!--
|
||||
Linker enabled part of the pipeline:
|
||||
|
||||
* If there are no descriptors defined, generate a new linker descriptor.
|
||||
* Collect the list of descriptors and produce a marker file to determine when the
|
||||
inputs to the linker change in future builds.
|
||||
* Invoke the linker if the linker inputs marker file is newer than the linker outputs.
|
||||
* Read the outputs from the linker and add them to the list of blazor outputs.
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<_CollectLinkerOutputsDependsOn>
|
||||
_GenerateLinkerDescriptor;
|
||||
_CollectBlazorLinkerDescriptors;
|
||||
_LinkBlazorApplication
|
||||
</_CollectLinkerOutputsDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target
|
||||
Name="_CollectLinkerOutputs"
|
||||
Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''"
|
||||
DependsOnTargets="$(_CollectLinkerOutputsDependsOn)">
|
||||
<!--
|
||||
Read the outputs from the linker (from this run or a previous run) and set them in an item group for
|
||||
later use.
|
||||
-->
|
||||
<ReadLinesFromFile File="$(BlazorIntermediateLinkerResultFilePath)">
|
||||
<Output TaskParameter="Lines" ItemName="_OptimizedFiles"/>
|
||||
</ReadLinesFromFile>
|
||||
|
||||
<ItemGroup>
|
||||
<BlazorItemOutput Include="@(_OptimizedFiles)">
|
||||
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>Assembly</Type>
|
||||
<PrimaryOutput Condition="'%(FileName)' == @(IntermediateAssembly->'%(FileName)')">true</PrimaryOutput>
|
||||
</BlazorItemOutput>
|
||||
<FileWrites Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(TargetOutputPath)')" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_GenerateLinkerDescriptor"
|
||||
Inputs="$(BlazorBuildCommonInputsCache)"
|
||||
Outputs="$(GeneratedBlazorLinkerDescriptor)"
|
||||
Condition="$(_BlazorShouldLinkApplicationAssemblies) != '' and '@(BlazorLinkerDescriptor)' == ''">
|
||||
|
||||
<ItemGroup>
|
||||
<_PrepareLinkerDescriptorAssemblyLine Include="@(IntermediateAssembly->'%(FileName)')" />
|
||||
<_GeneratedLinkerDescriptorLine Include="<linker>" />
|
||||
<_GeneratedLinkerDescriptorLine Include="@(_PrepareLinkerDescriptorAssemblyLine->'<assembly fullname="%(Identity)" />')" />
|
||||
<_GeneratedLinkerDescriptorLine Include="</linker>" />
|
||||
</ItemGroup>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="@(_GeneratedLinkerDescriptorLine)"
|
||||
File="$(GeneratedBlazorLinkerDescriptor)"
|
||||
Overwrite="true"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_CollectBlazorLinkerDescriptors">
|
||||
|
||||
<ItemGroup Condition="@(BlazorLinkerDescriptor) == ''">
|
||||
<BlazorLinkerDescriptor Include="$(GeneratedBlazorLinkerDescriptor)" />
|
||||
<FileWrites Include="$(GeneratedBlazorLinkerDescriptor)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_BlazorLinkerInput Include="@(IntermediateAssembly)" />
|
||||
<_BlazorLinkerInput Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll'))" />
|
||||
<_BlazorLinkerInput Include="@(BlazorLinkerDescriptor)" />
|
||||
<_BlazorLinkerInput Include="$(AdditionalLinkerOptions)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Hash ItemsToHash="@(_BlazorLinkerInput)">
|
||||
<Output TaskParameter="HashResult" PropertyName="_BlazorLinkerInputHash" />
|
||||
</Hash>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_BlazorLinkerInputHash)"
|
||||
File="$(BlazorBuildLinkerInputsCache)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(BlazorBuildLinkerInputsCache)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_LinkBlazorApplication"
|
||||
Condition="$(_BlazorShouldLinkApplicationAssemblies) != ''"
|
||||
Inputs="$(BlazorBuildLinkerInputsCache)"
|
||||
Outputs="$(BlazorIntermediateLinkerResultFilePath)"
|
||||
>
|
||||
<!--
|
||||
At this point we have decided to run the mono linker on the Blazor assembly and its dependencies.
|
||||
The steps to run the mono linker are the following:
|
||||
1) Clear the linker output directory if not clean before hand, as we don't know what the outputs of
|
||||
the linker will be.
|
||||
2) Run the linker on the main assembly, its dependencies and pass in the BCL folders to do the lookup
|
||||
for framework assemblies.
|
||||
3) Once we've run the linker we need to capture the produced output and generate a marker file containing
|
||||
the list of produced files. This file will act as a marker to skip running the linker if none of the inputs
|
||||
has changed.
|
||||
4) Add the file we just created to the list of file writes, to support incremental builds.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<_BlazorAssembliesToLink Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll')->'-a "%(FullPath)"')" />
|
||||
<_BlazorAssembliesToLink Include="@(IntermediateAssembly->'-a "%(FullPath)"')" />
|
||||
<_BlazorFolderLookupPaths Include="@(MonoBaseClassLibraryFolder->'-d "%(Identity)"')" />
|
||||
<_BlazorAssemblyDescriptorFiles
|
||||
Include="@(BlazorLinkerDescriptor->'-x "%(FullPath)"')" Condition="'@(BlazorLinkerDescriptor)' != ''" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_BlazorLinkerAdditionalOptions>$(AdditionalMonoLinkerOptions)</_BlazorLinkerAdditionalOptions>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Clear the contents of /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker -->
|
||||
<Delete Files="$(BlazorIntermediateLinkerOutputPath)*.dll" />
|
||||
|
||||
<!-- Run the linker and put the results in /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker -->
|
||||
<Exec Command="dotnet $(MonoLinkerPath) $(_BlazorLinkerAdditionalOptions) @(_BlazorFolderLookupPaths, ' ') -o $(BlazorIntermediateLinkerOutputPath) @(_BlazorAssemblyDescriptorFiles, ' ') @(_BlazorAssembliesToLink, ' ')" />
|
||||
|
||||
<!-- Collect the contents of /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/ -->
|
||||
<ItemGroup>
|
||||
<_BlazorLinkerOutput Include="$(BlazorIntermediateLinkerOutputPath)*.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Write the list of files in /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/ into
|
||||
/obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt
|
||||
-->
|
||||
<WriteLinesToFile
|
||||
File="$(BlazorIntermediateLinkerResultFilePath)"
|
||||
Lines="@(_BlazorLinkerOutput)"
|
||||
Overwrite="true" />
|
||||
|
||||
<!-- Add /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linked.assemblies.txt to the list of written files. -->
|
||||
<!-- Add /obj/<<configuration>>/<<targetframework>>/blazor/blazor/linker/*.dll to the list of written files. -->
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(BlazorIntermediateLinkerResultFilePath)" />
|
||||
<FileWrites Include="@(_BlazorLinkerOutput)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Linker disabled part of the pipeline:
|
||||
|
||||
* Run a CLI tool to produce the transitive closure of application references using the main application
|
||||
as entry point.
|
||||
* Read the list of resolved application references from the file produced by the previous step.
|
||||
* Copy the resolved application references into an intermediate folder.
|
||||
* If we are switching from linking to not linking
|
||||
Touch the files in the intermediate folder to ensure they are copied to the output and replace
|
||||
the linked versions with the same name.
|
||||
* Collect the list of resolved assemblies in the intermediate output folder and prepare them to be
|
||||
copied to their final destination in the output folder.
|
||||
-->
|
||||
|
||||
<PropertyGroup>
|
||||
<_CollectResolvedAssembliesDependsOn>
|
||||
_ResolveBlazorApplicationAssemblies;
|
||||
_ReadResolvedBlazorApplicationAssemblies;
|
||||
_IntermediateCopyBlazorApplicationAssemblies;
|
||||
_TouchBlazorApplicationAssemblies
|
||||
</_CollectResolvedAssembliesDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target
|
||||
Name="_CollectResolvedAssemblies"
|
||||
DependsOnTargets="$(_CollectResolvedAssembliesDependsOn)"
|
||||
Condition="'$(_BlazorShouldLinkApplicationAssemblies)' == ''">
|
||||
|
||||
<!--
|
||||
At this point we have decided not to run the linker and instead to just copy the assemblies
|
||||
from the BCL referenced by the app the nuget package into the _framework/_bin folder.
|
||||
The only thing we need to do here is collect the list of items that will go into _framework/_bin.
|
||||
-->
|
||||
|
||||
<ItemGroup>
|
||||
<BlazorItemOutput Include="@(_IntermediateResolvedRuntimeDependencies)">
|
||||
<TargetOutputPath>$(BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>Assembly</Type>
|
||||
<PrimaryOutput Condition="'%(FileName)' == @(IntermediateAssembly->'%(FileName)')">true</PrimaryOutput>
|
||||
</BlazorItemOutput>
|
||||
<FileWrites Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(TargetOutputPath)')" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_ResolveBlazorApplicationAssemblies"
|
||||
Condition="'$(_BlazorShouldLinkApplicationAssemblies)' == ''"
|
||||
Inputs="$(BlazorBuildCommonInputsCache)"
|
||||
Outputs="$(BlazorResolvedAssembliesOutputPath)"
|
||||
>
|
||||
<ItemGroup>
|
||||
<_DependenciesParameter Include="@(ReferenceCopyLocalPaths->WithMetadataValue('Extension','.dll')->'--reference "%(FullPath)"')" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_BclParameter>--base-class-library "$(MonoBaseClassLibraryPath)" --base-class-library "$(MonoBaseClassLibraryFacadesPath)"</_BclParameter>
|
||||
</PropertyGroup>
|
||||
|
||||
<Exec Command="$(BlazorBuildExe) resolve-dependencies "@(IntermediateAssembly->'%(FullPath)')" @(_DependenciesParameter, ' ') $(_BclParameter) --output "$(BlazorResolvedAssembliesOutputPath)"" />
|
||||
|
||||
</Target>
|
||||
|
||||
<Target Name="_ReadResolvedBlazorApplicationAssemblies">
|
||||
|
||||
<ReadLinesFromFile File="$(BlazorResolvedAssembliesOutputPath)">
|
||||
<Output TaskParameter="Lines" ItemName="_BlazorResolvedRuntimeDependencies"/>
|
||||
</ReadLinesFromFile>
|
||||
|
||||
<ItemGroup>
|
||||
<_IntermediateResolvedRuntimeDependencies Include="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(BlazorResolvedAssembliesOutputPath)" />
|
||||
<FileWrites Include="@(_IntermediateResolvedRuntimeDependencies)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_IntermediateCopyBlazorApplicationAssemblies"
|
||||
Inputs="@(_BlazorResolvedRuntimeDependencies)"
|
||||
Outputs="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')">
|
||||
|
||||
<Copy
|
||||
SourceFiles="@(_BlazorResolvedRuntimeDependencies)"
|
||||
DestinationFiles="@(_BlazorResolvedRuntimeDependencies->'$(BlazorIntermediateResolvedApplicationAssembliesOutputPath)%(FileName)%(Extension)')"
|
||||
SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
|
||||
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
|
||||
Retries="$(CopyRetryCount)"
|
||||
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
|
||||
UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)"
|
||||
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" />
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_TouchBlazorApplicationAssemblies"
|
||||
Inputs="$(BlazorBuildLinkerSwitchInputsCache)"
|
||||
Outputs="@(_IntermediateResolvedRuntimeDependencies)">
|
||||
|
||||
<Touch Files="@(_IntermediateResolvedRuntimeDependencies)" ForceTouch="true" />
|
||||
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Final part of the build pipeline:
|
||||
|
||||
* Collect the blazor application assemblies to be copied to the output and create a marker file.
|
||||
* Call our CLI tool to generate the index html if the list of assemblies has changed.
|
||||
-->
|
||||
|
||||
<Target Name="_ResolveBlazorIndexHtmlInputs">
|
||||
<ItemGroup>
|
||||
<BlazorIndexHtmlInput Include="$(BlazorIndexHtml)" />
|
||||
<BlazorIndexHtmlInput Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(FullPath)')" />
|
||||
</ItemGroup>
|
||||
|
||||
<WriteLinesToFile
|
||||
File="$(BlazorBuildIndexInputsCache)"
|
||||
Lines="@(BlazorIndexHtmlInput)"
|
||||
Overwrite="true"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(BlazorBuildIndexInputsCache)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_GenerateBlazorIndexHtml"
|
||||
DependsOnTargets="_ResolveBlazorIndexHtmlInputs"
|
||||
Inputs="$(BlazorBuildIndexInputsCache)"
|
||||
Outputs="$(BlazorIndexHtmlOutputPath)">
|
||||
<ItemGroup>
|
||||
<_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->WithMetadataValue('PrimaryOutput','')->'%(FileName)%(Extension)')" />
|
||||
</ItemGroup>
|
||||
|
||||
<Exec Command="$(BlazorBuildExe) build @(IntermediateAssembly) --html-page "$(BlazorIndexHtml)" @(_AppReferences->'--reference %(Identity)', ' ') --output "$(BlazorIndexHtmlOutputPath)"" />
|
||||
|
||||
<ItemGroup Condition="Exists('$(BlazorIndexHtmlOutputPath)')">
|
||||
<_BlazorIndex Include="$(BlazorIndexHtmlOutputPath)" />
|
||||
<BlazorItemOutput Include="@(_BlazorIndex)">
|
||||
<TargetOutputPath>$(ProjectDir)$(OutputPath)dist/%(FileName)%(Extension)</TargetOutputPath>
|
||||
<Type>EntryPoint</Type>
|
||||
</BlazorItemOutput>
|
||||
<FileWrites Include="$(BlazorIndexHtmlOutputPath)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders
|
||||
{
|
||||
// This is like a CompositeFileProvider, except that:
|
||||
// (a) the child providers can be mounted at non-root locations
|
||||
// (b) the set of contents is immutable and fully indexed at construction time
|
||||
// so that all subsequent reads are "O(dictionary lookup)" time
|
||||
public class CompositeMountedFileProvider : InMemoryFileProvider
|
||||
{
|
||||
public CompositeMountedFileProvider(params (string, IFileProvider)[] providers)
|
||||
: base(GetCompositeContents(providers))
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<IFileInfo> GetCompositeContents(
|
||||
IEnumerable<(string, IFileProvider)> providers)
|
||||
{
|
||||
return providers
|
||||
.Select(pair => new { MountPoint = pair.Item1, Files = ReadAllFiles(pair.Item2, string.Empty) })
|
||||
.SelectMany(info => info.Files.Select(file => (IFileInfo)new MountedFileInfo(info.MountPoint, file)));
|
||||
}
|
||||
|
||||
private static IEnumerable<IFileInfo> ReadAllFiles(IFileProvider provider, string subpath)
|
||||
{
|
||||
return provider.GetDirectoryContents(subpath).SelectMany(
|
||||
item => item.IsDirectory
|
||||
? ReadAllFiles(provider, item.PhysicalPath)
|
||||
: new[] { item });
|
||||
}
|
||||
|
||||
private class MountedFileInfo : IFileInfo
|
||||
{
|
||||
private readonly IFileInfo _file;
|
||||
|
||||
public MountedFileInfo(string mountPoint, IFileInfo file)
|
||||
{
|
||||
_file = file;
|
||||
|
||||
if (!file.PhysicalPath.StartsWith("/"))
|
||||
{
|
||||
throw new ArgumentException($"For mounted files, {nameof(file.PhysicalPath)} must start with '/'. Value supplied: '{file.PhysicalPath}'.");
|
||||
}
|
||||
|
||||
if (!mountPoint.StartsWith("/"))
|
||||
{
|
||||
throw new ArgumentException("The path must start with '/'", nameof(mountPoint));
|
||||
}
|
||||
|
||||
if (mountPoint == "/")
|
||||
{
|
||||
PhysicalPath = file.PhysicalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mountPoint.EndsWith("/"))
|
||||
{
|
||||
throw new ArgumentException("Non-root paths must not end with '/'", nameof(mountPoint));
|
||||
}
|
||||
|
||||
PhysicalPath = mountPoint + file.PhysicalPath;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists => _file.Exists;
|
||||
|
||||
public long Length => _file.Length;
|
||||
|
||||
public string PhysicalPath { get; }
|
||||
|
||||
public string Name => _file.Name;
|
||||
|
||||
public DateTimeOffset LastModified => _file.LastModified;
|
||||
|
||||
public bool IsDirectory => _file.IsDirectory;
|
||||
|
||||
public Stream CreateReadStream() => _file.CreateReadStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders
|
||||
{
|
||||
public class EmbeddedResourceFileProvider : InMemoryFileProvider
|
||||
{
|
||||
public EmbeddedResourceFileProvider(Assembly assembly, string resourceNamePrefix)
|
||||
: base(ReadEmbeddedResources(assembly, resourceNamePrefix))
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<(string, byte[])> ReadEmbeddedResources(
|
||||
Assembly assembly, string resourceNamePrefix)
|
||||
{
|
||||
return assembly.GetManifestResourceNames()
|
||||
.Where(name => name.StartsWith(resourceNamePrefix, StringComparison.Ordinal))
|
||||
.Select(name => (
|
||||
name.Substring(resourceNamePrefix.Length),
|
||||
ReadManifestResource(assembly, name)));
|
||||
}
|
||||
|
||||
private static byte[] ReadManifestResource(Assembly assembly, string name)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var resourceStream = assembly.GetManifestResourceStream(name))
|
||||
{
|
||||
resourceStream.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders
|
||||
{
|
||||
internal class InMemoryDirectoryContents : IDirectoryContents
|
||||
{
|
||||
private readonly Dictionary<string, object> _names; // Just to ensure there are no duplicate names
|
||||
private readonly List<IFileInfo> _contents;
|
||||
|
||||
public InMemoryDirectoryContents(IEnumerable<IFileInfo> contents)
|
||||
{
|
||||
if (contents != null)
|
||||
{
|
||||
_contents = contents.ToList();
|
||||
_names = _contents.ToDictionary(item => item.Name, item => (object)null);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists => _contents != null;
|
||||
|
||||
public IEnumerator<IFileInfo> GetEnumerator() => GetEnumeratorIfExists();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorIfExists();
|
||||
|
||||
internal void AddItem(IFileInfo item)
|
||||
{
|
||||
_names.Add(item.Name, null); // Asserts uniqueness
|
||||
_contents.Add(item);
|
||||
}
|
||||
internal bool ContainsName(string name)
|
||||
=> _names.ContainsKey(name);
|
||||
|
||||
private IEnumerator<IFileInfo> GetEnumeratorIfExists() => Exists
|
||||
? _contents.GetEnumerator()
|
||||
: throw new InvalidOperationException("The directory does not exist");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders
|
||||
{
|
||||
internal class InMemoryFileInfo : IFileInfo
|
||||
{
|
||||
private readonly bool _exists;
|
||||
private readonly bool _isDirectory;
|
||||
private readonly byte[] _fileData;
|
||||
private readonly string _name;
|
||||
private readonly string _physicalPath;
|
||||
private readonly DateTimeOffset _lastModified;
|
||||
|
||||
public static IFileInfo ForExistingDirectory(string path)
|
||||
=> new InMemoryFileInfo(path, isDir: true, exists: true);
|
||||
|
||||
public static IFileInfo ForExistingFile(string path, byte[] data, DateTimeOffset lastModified)
|
||||
=> new InMemoryFileInfo(path, isDir: false, exists: true, data: data, lastModified: lastModified);
|
||||
|
||||
public static IFileInfo ForNonExistingFile(string path)
|
||||
=> new InMemoryFileInfo(path, isDir: false, exists: false);
|
||||
|
||||
private InMemoryFileInfo(string physicalPath, bool isDir, bool exists, byte[] data = null, DateTimeOffset lastModified = default(DateTimeOffset))
|
||||
{
|
||||
_exists = exists;
|
||||
_isDirectory = isDir;
|
||||
_name = Path.GetFileName(physicalPath);
|
||||
_physicalPath = physicalPath ?? throw new ArgumentNullException(nameof(physicalPath));
|
||||
_lastModified = lastModified;
|
||||
|
||||
if (_exists)
|
||||
{
|
||||
if (!_physicalPath.StartsWith(InMemoryFileProvider.DirectorySeparatorChar))
|
||||
{
|
||||
throw new ArgumentException($"Must start with '{InMemoryFileProvider.DirectorySeparatorChar}'",
|
||||
nameof(physicalPath));
|
||||
}
|
||||
|
||||
if (_physicalPath.EndsWith(InMemoryFileProvider.DirectorySeparatorChar))
|
||||
{
|
||||
throw new ArgumentException($"Must not end with '{InMemoryFileProvider.DirectorySeparatorChar}'",
|
||||
nameof(physicalPath));
|
||||
}
|
||||
}
|
||||
|
||||
_fileData = data;
|
||||
}
|
||||
|
||||
public bool Exists => _exists;
|
||||
|
||||
public long Length => Exists && !IsDirectory
|
||||
? _fileData.Length
|
||||
: throw new InvalidOperationException(IsDirectory
|
||||
? "The item is a directory."
|
||||
: "The item does not exist.");
|
||||
|
||||
public string PhysicalPath => _physicalPath;
|
||||
|
||||
public string Name => _name;
|
||||
|
||||
public DateTimeOffset LastModified => _lastModified;
|
||||
|
||||
public bool IsDirectory => _isDirectory;
|
||||
|
||||
public Stream CreateReadStream() => Exists && !IsDirectory
|
||||
? new MemoryStream(_fileData)
|
||||
: throw new InvalidOperationException(IsDirectory
|
||||
? "The item is a directory."
|
||||
: "The item does not exist.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders
|
||||
{
|
||||
public class InMemoryFileProvider : IFileProvider
|
||||
{
|
||||
// Since the IFileProvider APIs don't include any way of asking for parent or
|
||||
// child directories, it's efficient to store everything by full path
|
||||
private readonly IDictionary<string, IFileInfo> _filesByFullPath;
|
||||
private readonly IDictionary<string, InMemoryDirectoryContents> _directoriesByFullPath;
|
||||
|
||||
// It's convenient to use forward slash, because it matches URL conventions
|
||||
public const string DirectorySeparatorChar = "/";
|
||||
|
||||
public InMemoryFileProvider(IEnumerable<(string, byte[])> contents) : this(
|
||||
contents.Select(pair => InMemoryFileInfo
|
||||
.ForExistingFile(pair.Item1, pair.Item2, DateTime.Now)))
|
||||
{
|
||||
}
|
||||
|
||||
public InMemoryFileProvider(IEnumerable<IFileInfo> contents)
|
||||
{
|
||||
_filesByFullPath = contents
|
||||
.ToDictionary(
|
||||
fileInfo => fileInfo.PhysicalPath,
|
||||
fileInfo => (IFileInfo)fileInfo);
|
||||
|
||||
_directoriesByFullPath = _filesByFullPath.Values
|
||||
.GroupBy(file => GetDirectoryName(file.PhysicalPath))
|
||||
.ToDictionary(
|
||||
group => group.Key,
|
||||
group => new InMemoryDirectoryContents(group));
|
||||
|
||||
foreach (var dirToInsert in _directoriesByFullPath.Keys.ToList())
|
||||
{
|
||||
AddSubdirectoryEntry(dirToInsert);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSubdirectoryEntry(string dirPath)
|
||||
{
|
||||
// If this is the root directory, there's no parent
|
||||
if (dirPath.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
var parentDirPath = GetDirectoryName(dirPath);
|
||||
if (!_directoriesByFullPath.ContainsKey(parentDirPath))
|
||||
{
|
||||
_directoriesByFullPath.Add(
|
||||
parentDirPath,
|
||||
new InMemoryDirectoryContents(Enumerable.Empty<IFileInfo>()));
|
||||
}
|
||||
|
||||
var parentDir = _directoriesByFullPath[parentDirPath];
|
||||
if (!parentDir.ContainsName(Path.GetFileName(dirPath)))
|
||||
{
|
||||
parentDir.AddItem(
|
||||
InMemoryFileInfo.ForExistingDirectory(dirPath));
|
||||
}
|
||||
|
||||
// Doing this recursively creates all ancestor directories
|
||||
AddSubdirectoryEntry(parentDirPath);
|
||||
}
|
||||
|
||||
private static string GetDirectoryName(string fullPath)
|
||||
=> fullPath.Substring(0, fullPath.LastIndexOf(DirectorySeparatorChar));
|
||||
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
=> _directoriesByFullPath.TryGetValue(StripTrailingSeparator(subpath), out var result)
|
||||
? result
|
||||
: new InMemoryDirectoryContents(null);
|
||||
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
=> _filesByFullPath.TryGetValue(subpath, out var fileInfo)
|
||||
? fileInfo
|
||||
: InMemoryFileInfo.ForNonExistingFile(subpath);
|
||||
|
||||
public IChangeToken Watch(string filter)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
private string StripTrailingSeparator(string path) => path.EndsWith(DirectorySeparatorChar)
|
||||
? path.Substring(0, path.Length - 1)
|
||||
: path;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\mono\mono.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Common\Microsoft.AspNetCore.Blazor.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="EmbedMonoResources" BeforeTargets="PreBuildEvent" DependsOnTargets="OptimizeMono" Inputs="$(MonoUnoptimizedDir)\**" Outputs="$(MonoOptimizedDir)\**">
|
||||
<ItemGroup>
|
||||
<MonoWasmResourcesToEmbed Include="$(MonoOptimizedDir)wasm/**" />
|
||||
<MonoAsmJsResourcesToEmbed Include="$(MonoOptimizedDir)asmjs/**" />
|
||||
<MonoBclResourcesToEmbed Include="$(MonoOptimizedDir)bcl/**" />
|
||||
<EmbeddedResource Include="@(MonoWasmResourcesToEmbed)">
|
||||
<LogicalName>mono.js./wasm$([System.String]::Copy('/%(RecursiveDir)%(FileName)%(Extension)').Replace('\', '/'))</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="@(MonoAsmJsResourcesToEmbed)">
|
||||
<LogicalName>mono.js./asmjs$([System.String]::Copy('/%(RecursiveDir)%(FileName)%(Extension)').Replace('\', '/'))</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="@(MonoBclResourcesToEmbed)">
|
||||
<LogicalName>mono.bcl./bcl$([System.String]::Copy('/%(RecursiveDir)%(FileName)%(Extension)').Replace('\', '/'))</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Mono
|
||||
{
|
||||
public static class MonoStaticFileProvider
|
||||
{
|
||||
public readonly static IFileProvider JsFiles = new EmbeddedResourceFileProvider(
|
||||
typeof(MonoStaticFileProvider).Assembly, "mono.js.");
|
||||
|
||||
public readonly static IFileProvider BclFiles = new EmbeddedResourceFileProvider(
|
||||
typeof(MonoStaticFileProvider).Assembly, "mono.bcl.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using System;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Microsoft.AspNetCore.Blazor.Build.Core.FileSystem;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.Test
|
||||
{
|
||||
public class IndexHtmlFileProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void SuppliesNoIndexHtmlFileGivenNoTemplate()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new IndexHtmlFileProvider(
|
||||
null, "fakeassembly", "MyNamespace.MyType::MyMethod", Enumerable.Empty<IFileInfo>());
|
||||
|
||||
// Act
|
||||
var file = instance.GetFileInfo("/index.html");
|
||||
|
||||
// Assert
|
||||
Assert.False(file.Exists);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuppliesIndexHtmlFileGivenTemplate()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "test";
|
||||
var instance = new IndexHtmlFileProvider(
|
||||
htmlTemplate, "fakeassembly", "MyNamespace.MyType::MyMethod", Enumerable.Empty<IFileInfo>());
|
||||
|
||||
// Act
|
||||
var file = instance.GetFileInfo("/index.html");
|
||||
|
||||
// Assert
|
||||
Assert.True(file.Exists);
|
||||
Assert.False(file.IsDirectory);
|
||||
Assert.Equal("/index.html", file.PhysicalPath);
|
||||
Assert.Equal("index.html", file.Name);
|
||||
Assert.Equal(htmlTemplate, ReadString(file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RootDirectoryContainsOnlyIndexHtml()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "test";
|
||||
var instance = new IndexHtmlFileProvider(
|
||||
htmlTemplate, "fakeassembly", "MyNamespace.MyType::MyMethod", Enumerable.Empty<IFileInfo>());
|
||||
|
||||
// Act
|
||||
var directory = instance.GetDirectoryContents(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True(directory.Exists);
|
||||
Assert.Collection(directory,
|
||||
item => Assert.Equal("/index.html", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InjectsScriptTagReferencingAssemblyAndDependencies()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplatePrefix = @"
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello</h1>
|
||||
Some text
|
||||
<script>alert(1)</script>";
|
||||
var htmlTemplateSuffix = @"
|
||||
</body>
|
||||
</html>";
|
||||
var htmlTemplate =
|
||||
$@"{htmlTemplatePrefix}
|
||||
<script type='blazor-boot' custom1 custom2=""value"">some text that should be removed</script>
|
||||
{htmlTemplateSuffix}";
|
||||
var dependencies = new IFileInfo[]
|
||||
{
|
||||
new TestFileInfo("System.Abc.dll"),
|
||||
new TestFileInfo("MyApp.ClassLib.dll"),
|
||||
};
|
||||
var instance = new IndexHtmlFileProvider(
|
||||
htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", dependencies);
|
||||
|
||||
// Act
|
||||
var file = instance.GetFileInfo("/index.html");
|
||||
var fileContents = ReadString(file);
|
||||
|
||||
// Assert: Start and end is not modified (including formatting)
|
||||
Assert.StartsWith(htmlTemplatePrefix, fileContents);
|
||||
Assert.EndsWith(htmlTemplateSuffix, fileContents);
|
||||
|
||||
// Assert: Boot tag is correct
|
||||
var scriptTagText = fileContents.Substring(htmlTemplatePrefix.Length, fileContents.Length - htmlTemplatePrefix.Length - htmlTemplateSuffix.Length);
|
||||
var parsedHtml = new HtmlParser().Parse("<html><body>" + scriptTagText + "</body></html>");
|
||||
var scriptElem = parsedHtml.Body.QuerySelector("script");
|
||||
Assert.False(scriptElem.HasChildNodes);
|
||||
Assert.Equal("_framework/blazor.js", scriptElem.GetAttribute("src"));
|
||||
Assert.Equal("MyApp.Entrypoint.dll", scriptElem.GetAttribute("main"));
|
||||
Assert.Equal("MyNamespace.MyType::MyMethod", scriptElem.GetAttribute("entrypoint"));
|
||||
Assert.Equal("System.Abc.dll,MyApp.ClassLib.dll", scriptElem.GetAttribute("references"));
|
||||
Assert.False(scriptElem.HasAttribute("type"));
|
||||
Assert.Equal(string.Empty, scriptElem.Attributes["custom1"].Value);
|
||||
Assert.Equal("value", scriptElem.Attributes["custom2"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuppliesHtmlTemplateUnchangedIfNoBootScriptPresent()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "<!DOCTYPE html><html><body><h1 style='color:red'>Hello</h1>Some text<script type='irrelevant'>blah</script></body></html>";
|
||||
var dependencies = new IFileInfo[]
|
||||
{
|
||||
new TestFileInfo("System.Abc.dll"),
|
||||
new TestFileInfo("MyApp.ClassLib.dll"),
|
||||
};
|
||||
var instance = new IndexHtmlFileProvider(
|
||||
htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", dependencies);
|
||||
|
||||
// Act
|
||||
var file = instance.GetFileInfo("/index.html");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(htmlTemplate, ReadString(file));
|
||||
}
|
||||
|
||||
private static string ReadString(IFileInfo file)
|
||||
{
|
||||
using (var stream = file.CreateReadStream())
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
class TestFileInfo : IFileInfo
|
||||
{
|
||||
public TestFileInfo(string physicalPath)
|
||||
{
|
||||
PhysicalPath = physicalPath;
|
||||
}
|
||||
|
||||
public bool Exists => true;
|
||||
|
||||
public long Length => throw new NotImplementedException();
|
||||
|
||||
public string PhysicalPath { get; }
|
||||
|
||||
public string Name => Path.GetFileName(PhysicalPath);
|
||||
|
||||
public DateTimeOffset LastModified => throw new NotImplementedException();
|
||||
|
||||
public bool IsDirectory => false;
|
||||
|
||||
public Stream CreateReadStream() => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// 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 AngleSharp.Parser.Html;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Test
|
||||
{
|
||||
public class IndexHtmlWriterTest
|
||||
{
|
||||
[Fact]
|
||||
public void InjectsScriptTagReferencingAssemblyAndDependencies()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplatePrefix = @"
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello</h1>
|
||||
Some text
|
||||
<script>alert(1)</script>";
|
||||
var htmlTemplateSuffix = @"
|
||||
</body>
|
||||
</html>";
|
||||
var htmlTemplate =
|
||||
$@"{htmlTemplatePrefix}
|
||||
<script type='blazor-boot' custom1 custom2=""value"">some text that should be removed</script>
|
||||
{htmlTemplateSuffix}";
|
||||
var dependencies = new string[]
|
||||
{
|
||||
"System.Abc.dll",
|
||||
"MyApp.ClassLib.dll",
|
||||
};
|
||||
var instance = IndexHtmlWriter.GetIndexHtmlContents(
|
||||
htmlTemplate,
|
||||
"MyApp.Entrypoint",
|
||||
"MyNamespace.MyType::MyMethod", dependencies);
|
||||
|
||||
// Act & Assert: Start and end is not modified (including formatting)
|
||||
Assert.StartsWith(htmlTemplatePrefix, instance);
|
||||
Assert.EndsWith(htmlTemplateSuffix, instance);
|
||||
|
||||
// Assert: Boot tag is correct
|
||||
var scriptTagText = instance.Substring(htmlTemplatePrefix.Length, instance.Length - htmlTemplatePrefix.Length - htmlTemplateSuffix.Length);
|
||||
var parsedHtml = new HtmlParser().Parse("<html><body>" + scriptTagText + "</body></html>");
|
||||
var scriptElem = parsedHtml.Body.QuerySelector("script");
|
||||
Assert.False(scriptElem.HasChildNodes);
|
||||
Assert.Equal("_framework/blazor.js", scriptElem.GetAttribute("src"));
|
||||
Assert.Equal("MyApp.Entrypoint.dll", scriptElem.GetAttribute("main"));
|
||||
Assert.Equal("MyNamespace.MyType::MyMethod", scriptElem.GetAttribute("entrypoint"));
|
||||
Assert.Equal("System.Abc.dll,MyApp.ClassLib.dll", scriptElem.GetAttribute("references"));
|
||||
Assert.False(scriptElem.HasAttribute("type"));
|
||||
Assert.Equal(string.Empty, scriptElem.Attributes["custom1"].Value);
|
||||
Assert.Equal("value", scriptElem.Attributes["custom2"].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuppliesHtmlTemplateUnchangedIfNoBootScriptPresent()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "<!DOCTYPE html><html><body><h1 style='color:red'>Hello</h1>Some text<script type='irrelevant'>blah</script></body></html>";
|
||||
var dependencies = new string[]
|
||||
{
|
||||
"System.Abc.dll",
|
||||
"MyApp.ClassLib.dll",
|
||||
};
|
||||
|
||||
var content = IndexHtmlWriter.GetIndexHtmlContents(
|
||||
htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", dependencies);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(htmlTemplate, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,13 @@
|
|||
<Compile Include="..\shared\**\*.cs" Link="Helpers\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- A bit of msbuild magic to support reference resolver tests -->
|
||||
<Target Name="CreateReferenceHintPathsList" AfterTargets="Build">
|
||||
<WriteLinesToFile Lines="@(ReferencePath)" File="$(ProjectDir)$(OutputPath)referenceHints.txt" WriteOnlyWhenDifferent="true" Overwrite="true" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Blazor.Mono\Microsoft.AspNetCore.Blazor.Mono.csproj" />
|
||||
<ProjectReference Include="..\..\src\anglesharp\AngleSharpBuilder\AngleSharpBuilder.csproj" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Blazor.Razor.Extensions\Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj" />
|
||||
<Reference Include="Microsoft.AspNetCore.Blazor.AngleSharp" HintPath="..\..\src\anglesharp\AngleSharpBuilder\dist\Microsoft.AspNetCore.Blazor.AngleSharp.dll" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Build.Core.FileSystem;
|
||||
using Microsoft.AspNetCore.Blazor.Mono;
|
||||
using Mono.Cecil;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server.Test
|
||||
{
|
||||
public class ReferencedAssemblyFileProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void RootDirContainsOnlyDlls()
|
||||
{
|
||||
var provider = new ReferencedAssemblyFileProvider(
|
||||
"mscorlib",
|
||||
new ReferencedAssemblyResolver(MonoStaticFileProvider.BclFiles, string.Empty));
|
||||
foreach (var item in provider.GetDirectoryContents("/"))
|
||||
{
|
||||
Assert.False(item.IsDirectory);
|
||||
Assert.EndsWith(".dll", item.Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindsReferencedAssemblyGraphSimple()
|
||||
{
|
||||
var provider = new ReferencedAssemblyFileProvider(
|
||||
"System.Linq.Expressions",
|
||||
new ReferencedAssemblyResolver(MonoStaticFileProvider.BclFiles, string.Empty));
|
||||
var contents = provider.GetDirectoryContents("").OrderBy(i => i.Name).ToList();
|
||||
Assert.Collection(contents,
|
||||
item => { Assert.Equal("/Mono.Security.dll", item.PhysicalPath); },
|
||||
item => { Assert.Equal("/mscorlib.dll", item.PhysicalPath); },
|
||||
item => { Assert.Equal("/System.Core.dll", item.PhysicalPath); },
|
||||
item => { Assert.Equal("/System.dll", item.PhysicalPath); },
|
||||
item => { Assert.Equal("/System.Linq.Expressions.dll", item.PhysicalPath); },
|
||||
item => { Assert.Equal("/System.Xml.dll", item.PhysicalPath); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindsReferencedAssemblyGraphRealistic()
|
||||
{
|
||||
// Arrange
|
||||
var standaloneAppAssembly = typeof(StandaloneApp.Program).Assembly;
|
||||
var provider = new ReferencedAssemblyFileProvider(
|
||||
standaloneAppAssembly.GetName().Name,
|
||||
new ReferencedAssemblyResolver(
|
||||
MonoStaticFileProvider.BclFiles,
|
||||
Path.GetDirectoryName(standaloneAppAssembly.Location)));
|
||||
var expectedContents = new[]
|
||||
{
|
||||
/*
|
||||
The current Mono WASM BCL forwards from netstandard.dll to various facade assemblies
|
||||
in which small bits of implementation live, such as System.Xml.XPath.XDocument. So
|
||||
if you reference netstandard, then you also reference System.Xml.XPath.XDocument.dll,
|
||||
even though you're very unlikely to be calling it at runtime. That's why the following
|
||||
list (for a very basic Blazor app) is longer than you'd expect.
|
||||
|
||||
These redundant references could be stripped out during publishing, but it's still
|
||||
unfortunate that in development mode you'd see all these unexpected assemblies get
|
||||
fetched from the server. We should try to get the Mono WASM BCL reorganized so that
|
||||
all the implementation goes into mscorlib.dll, with the facade assemblies existing only
|
||||
in case someone (or some 3rd party assembly) references them directly, but with their
|
||||
implementations 100% forwarding to mscorlib.dll. Then in development you'd fetch far
|
||||
fewer assemblies from the server, and during publishing, illink would remove all the
|
||||
uncalled implementation code from mscorlib.dll anyway.
|
||||
*/
|
||||
"/Microsoft.AspNetCore.Blazor.Browser.dll",
|
||||
"/Microsoft.AspNetCore.Blazor.dll",
|
||||
"/Mono.Security.dll",
|
||||
"/mscorlib.dll",
|
||||
"/netstandard.dll",
|
||||
"/StandaloneApp.dll",
|
||||
"/System.ComponentModel.Composition.dll",
|
||||
"/System.Core.dll",
|
||||
"/System.Data.dll",
|
||||
"/System.Diagnostics.StackTrace.dll",
|
||||
"/System.dll",
|
||||
"/System.Drawing.dll",
|
||||
"/System.Globalization.Extensions.dll",
|
||||
"/System.IO.Compression.dll",
|
||||
"/System.IO.Compression.FileSystem.dll",
|
||||
"/System.Net.Http.dll",
|
||||
"/System.Numerics.dll",
|
||||
"/System.Runtime.Serialization.dll",
|
||||
"/System.Runtime.Serialization.Primitives.dll",
|
||||
"/System.Runtime.Serialization.Xml.dll",
|
||||
"/System.Security.Cryptography.Algorithms.dll",
|
||||
"/System.Security.SecureString.dll",
|
||||
"/System.ServiceModel.Internals.dll",
|
||||
"/System.Transactions.dll",
|
||||
"/System.Web.Services.dll",
|
||||
"/System.Xml.dll",
|
||||
"/System.Xml.Linq.dll",
|
||||
"/System.Xml.XPath.XDocument.dll",
|
||||
};
|
||||
|
||||
// Act
|
||||
var contents = provider.GetDirectoryContents("")
|
||||
.OrderBy(i => i.Name, StringComparer.InvariantCulture).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContents.Length, contents.Count);
|
||||
for (var i = 0; i < expectedContents.Length; i++)
|
||||
{
|
||||
Assert.Equal(expectedContents[i], contents[i].PhysicalPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static (AssemblyDefinition, byte[]) GetBclAssemblyForTest(string name)
|
||||
{
|
||||
var possibleFilenames = new[] { $"/bcl/{name}.dll", $"/bcl/Facades/{name}.dll" };
|
||||
var fileInfo = possibleFilenames
|
||||
.Select(MonoStaticFileProvider.BclFiles.GetFileInfo)
|
||||
.First(item => item.Exists);
|
||||
using (var data = new MemoryStream())
|
||||
{
|
||||
fileInfo.CreateReadStream().CopyTo(data);
|
||||
return (AssemblyDefinition.ReadAssembly(fileInfo.CreateReadStream()), data.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Test
|
||||
{
|
||||
public class RuntimeDependenciesResolverTest
|
||||
{
|
||||
[Fact]
|
||||
public void FindsReferenceAssemblyGraph_ForStandaloneApp()
|
||||
{
|
||||
// Arrange
|
||||
var standaloneAppAssembly = typeof(StandaloneApp.Program).Assembly;
|
||||
var mainAssemblyLocation = standaloneAppAssembly.Location;
|
||||
// This list of hints is populated by MSBuild so it will be on the output
|
||||
// folder.
|
||||
var hintPaths = File.ReadAllLines(Path.Combine(
|
||||
Path.GetDirectoryName(mainAssemblyLocation),
|
||||
"referenceHints.txt"));
|
||||
var references = new[]
|
||||
{
|
||||
"Microsoft.AspNetCore.Blazor.Browser.dll",
|
||||
"Microsoft.AspNetCore.Blazor.dll",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
||||
"Microsoft.Extensions.DependencyInjection.dll"
|
||||
}.Select(a => hintPaths.Single(p => Path.GetFileName(p) == a))
|
||||
.ToArray();
|
||||
|
||||
var basePath = Path.GetDirectoryName(typeof(RuntimeDependenciesResolverTest).Assembly.Location);
|
||||
var bclLocations = new []
|
||||
{
|
||||
Path.Combine(basePath, "../../../../../src/mono/dist/optimized/bcl/"),
|
||||
Path.Combine(basePath, "../../../../../src/mono/dist/optimized/bcl/Facades/"),
|
||||
};
|
||||
|
||||
var expectedContents = new[]
|
||||
{
|
||||
/*
|
||||
The current Mono WASM BCL forwards from netstandard.dll to various facade assemblies
|
||||
in which small bits of implementation live, such as System.Xml.XPath.XDocument. So
|
||||
if you reference netstandard, then you also reference System.Xml.XPath.XDocument.dll,
|
||||
even though you're very unlikely to be calling it at runtime. That's why the following
|
||||
list (for a very basic Blazor app) is longer than you'd expect.
|
||||
|
||||
These redundant references could be stripped out during publishing, but it's still
|
||||
unfortunate that in development mode you'd see all these unexpected assemblies get
|
||||
fetched from the server. We should try to get the Mono WASM BCL reorganized so that
|
||||
all the implementation goes into mscorlib.dll, with the facade assemblies existing only
|
||||
in case someone (or some 3rd party assembly) references them directly, but with their
|
||||
implementations 100% forwarding to mscorlib.dll. Then in development you'd fetch far
|
||||
fewer assemblies from the server, and during publishing, illink would remove all the
|
||||
uncalled implementation code from mscorlib.dll anyway.
|
||||
*/
|
||||
"Microsoft.AspNetCore.Blazor.Browser.dll",
|
||||
"Microsoft.AspNetCore.Blazor.dll",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
||||
"Microsoft.Extensions.DependencyInjection.dll",
|
||||
"Mono.Security.dll",
|
||||
"mscorlib.dll",
|
||||
"netstandard.dll",
|
||||
"StandaloneApp.dll",
|
||||
"System.ComponentModel.Composition.dll",
|
||||
"System.Core.dll",
|
||||
"System.Data.dll",
|
||||
"System.Diagnostics.StackTrace.dll",
|
||||
"System.dll",
|
||||
"System.Drawing.dll",
|
||||
"System.Globalization.Extensions.dll",
|
||||
"System.IO.Compression.dll",
|
||||
"System.IO.Compression.FileSystem.dll",
|
||||
"System.Net.Http.dll",
|
||||
"System.Numerics.dll",
|
||||
"System.Runtime.Serialization.dll",
|
||||
"System.Runtime.Serialization.Primitives.dll",
|
||||
"System.Runtime.Serialization.Xml.dll",
|
||||
"System.Security.Cryptography.Algorithms.dll",
|
||||
"System.Security.SecureString.dll",
|
||||
"System.ServiceModel.Internals.dll",
|
||||
"System.Transactions.dll",
|
||||
"System.Web.Services.dll",
|
||||
"System.Xml.dll",
|
||||
"System.Xml.Linq.dll",
|
||||
"System.Xml.XPath.XDocument.dll",
|
||||
}.OrderBy(i => i, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
// Act
|
||||
|
||||
var paths = RuntimeDependenciesResolver
|
||||
.ResolveRuntimeDependenciesCore(
|
||||
mainAssemblyLocation,
|
||||
references,
|
||||
bclLocations);
|
||||
|
||||
var contents = paths
|
||||
.Select(p => Path.GetFileName(p))
|
||||
.OrderBy(i => i, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContents, contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Common.Test
|
||||
{
|
||||
public class CompositeMountedFileProviderTest
|
||||
{
|
||||
private (string, byte[]) TestItem(string name) => (name, Array.Empty<byte>());
|
||||
private (string, byte[]) TestItem(string name, string data) => (name, Encoding.UTF8.GetBytes(data));
|
||||
private IFileProvider TestFileProvider(params string[] paths) => new InMemoryFileProvider(paths.Select(TestItem));
|
||||
|
||||
[Fact]
|
||||
public void MountPointsMustStartWithSlash()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new CompositeMountedFileProvider(
|
||||
("test", TestFileProvider("/something.txt")));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NonRootMountPointsMustNotEndWithSlash()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new CompositeMountedFileProvider(
|
||||
("/test/", TestFileProvider("/something.txt")));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountedFilePathsMustStartWithSlash()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new CompositeMountedFileProvider(
|
||||
("/test", TestFileProvider("something.txt")));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanMountFileProviderAtRoot()
|
||||
{
|
||||
// Arrange
|
||||
IFileProvider childProvider = new InMemoryFileProvider(new[]
|
||||
{
|
||||
TestItem("/rootitem.txt", "Root item contents"),
|
||||
TestItem("/subdir/another", "Another test item"),
|
||||
});
|
||||
var instance = new CompositeMountedFileProvider(("/", childProvider));
|
||||
|
||||
// Act
|
||||
var rootContents = instance.GetDirectoryContents(string.Empty);
|
||||
var subdirContents = instance.GetDirectoryContents("/subdir");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(rootContents,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/rootitem.txt", item.PhysicalPath);
|
||||
Assert.False(item.IsDirectory);
|
||||
Assert.Equal("Root item contents", new StreamReader(item.CreateReadStream()).ReadToEnd());
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/subdir", item.PhysicalPath);
|
||||
Assert.True(item.IsDirectory);
|
||||
});
|
||||
|
||||
Assert.Collection(subdirContents,
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/subdir/another", item.PhysicalPath);
|
||||
Assert.False(item.IsDirectory);
|
||||
Assert.Equal("Another test item", new StreamReader(item.CreateReadStream()).ReadToEnd());
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanMountFileProvidersAtSubPaths()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new CompositeMountedFileProvider(
|
||||
("/dir", TestFileProvider("/first", "/A/second", "/A/third")),
|
||||
("/dir/sub", TestFileProvider("/X", "/B/Y", "/B/Z")),
|
||||
("/other", TestFileProvider("/final")));
|
||||
|
||||
// Act
|
||||
var rootContents = instance.GetDirectoryContents("/");
|
||||
var rootDirContents = instance.GetDirectoryContents("/dir");
|
||||
var rootDirAContents = instance.GetDirectoryContents("/dir/A");
|
||||
var rootDirSubContents = instance.GetDirectoryContents("/dir/sub");
|
||||
var rootDirSubBContents = instance.GetDirectoryContents("/dir/sub/B");
|
||||
var otherContents = instance.GetDirectoryContents("/other");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(rootContents,
|
||||
item => Assert.Equal("/dir", item.PhysicalPath),
|
||||
item => Assert.Equal("/other", item.PhysicalPath));
|
||||
Assert.Collection(rootDirContents,
|
||||
item => Assert.Equal("/dir/first", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/A", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/sub", item.PhysicalPath));
|
||||
Assert.Collection(rootDirAContents,
|
||||
item => Assert.Equal("/dir/A/second", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/A/third", item.PhysicalPath));
|
||||
Assert.Collection(rootDirSubContents,
|
||||
item => Assert.Equal("/dir/sub/X", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/sub/B", item.PhysicalPath));
|
||||
Assert.Collection(rootDirSubBContents,
|
||||
item => Assert.Equal("/dir/sub/B/Y", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/sub/B/Z", item.PhysicalPath));
|
||||
Assert.Collection(otherContents,
|
||||
item => Assert.Equal("/other/final", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanMountMultipleFileProvidersAtSameLocation()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new CompositeMountedFileProvider(
|
||||
("/dir", TestFileProvider("/first")),
|
||||
("/dir", TestFileProvider("/second")));
|
||||
|
||||
// Act
|
||||
var contents = instance.GetDirectoryContents("/dir");
|
||||
|
||||
// Assert
|
||||
Assert.Collection(contents,
|
||||
item => Assert.Equal("/dir/first", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/second", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisallowsOverlappingFiles()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new CompositeMountedFileProvider(
|
||||
("/dir", TestFileProvider("/file")),
|
||||
("/", TestFileProvider("/dir/file")));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Common.Test
|
||||
{
|
||||
public class InMemoryFileProviderTest
|
||||
{
|
||||
private (string, byte[]) TestItem(string name) => (name, Array.Empty<byte>());
|
||||
|
||||
[Fact]
|
||||
public void RequiresPathsToStartWithSlash()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new InMemoryFileProvider(new[] { TestItem("item") });
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RequiresPathsNotToEndWithSlash()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new InMemoryFileProvider(new[] { TestItem("/item/") });
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFileInfosForExistingFiles()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[]
|
||||
{
|
||||
("/dirA/item", Encoding.UTF8.GetBytes("Contents of /dirA/item")),
|
||||
("/dirB/item", Encoding.UTF8.GetBytes("Contents of /dirB/item"))
|
||||
});
|
||||
|
||||
// Act
|
||||
var dirAItem = instance.GetFileInfo("/dirA/item");
|
||||
var dirBItem = instance.GetFileInfo("/dirB/item");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(
|
||||
"Contents of /dirA/item",
|
||||
new StreamReader(dirAItem.CreateReadStream()).ReadToEnd());
|
||||
Assert.Equal(
|
||||
"Contents of /dirB/item",
|
||||
new StreamReader(dirBItem.CreateReadStream()).ReadToEnd());
|
||||
Assert.True(dirAItem.Exists);
|
||||
Assert.False(dirAItem.IsDirectory);
|
||||
Assert.True((DateTime.Now - dirAItem.LastModified).TotalDays < 1); // Exact behaviour doesn't need to be defined (at least not yet) but it should be a valid date
|
||||
Assert.Equal(22, dirAItem.Length);
|
||||
Assert.Equal("item", dirAItem.Name);
|
||||
Assert.Equal("/dirA/item", dirAItem.PhysicalPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFileInfosForNonExistingFiles()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] { TestItem("/dirA/item") });
|
||||
|
||||
// Act
|
||||
var mismatchedCaseItem = instance.GetFileInfo("/dira/item");
|
||||
var dirBItem = instance.GetFileInfo("/dirB/item");
|
||||
|
||||
// Assert
|
||||
Assert.False(mismatchedCaseItem.Exists);
|
||||
Assert.False(dirBItem.Exists);
|
||||
Assert.False(dirBItem.IsDirectory);
|
||||
Assert.Equal("item", dirBItem.Name);
|
||||
Assert.Equal("/dirB/item", dirBItem.PhysicalPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsDirectoryContentsForExistingDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[]
|
||||
{
|
||||
TestItem("/dir/subdir/item1"),
|
||||
TestItem("/dir/subdir/item2"),
|
||||
TestItem("/dir/otherdir/item3")
|
||||
});
|
||||
|
||||
// Act
|
||||
var contents = instance.GetDirectoryContents("/dir/subdir");
|
||||
|
||||
// Assert
|
||||
Assert.True(contents.Exists);
|
||||
Assert.Collection(contents,
|
||||
item => Assert.Equal("/dir/subdir/item1", item.PhysicalPath),
|
||||
item => Assert.Equal("/dir/subdir/item2", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyStringAndSlashAreBothInterpretedAsRootDir()
|
||||
{
|
||||
// Technically this test duplicates checking the behavior asserted
|
||||
// previously (i.e., trailing slashes are ignored), but it's worth
|
||||
// checking that nothing bad happens when the path is an empty string
|
||||
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] { TestItem("/item") });
|
||||
|
||||
// Act/Assert
|
||||
Assert.Collection(instance.GetDirectoryContents(string.Empty),
|
||||
item => Assert.Equal("/item", item.PhysicalPath));
|
||||
Assert.Collection(instance.GetDirectoryContents("/"),
|
||||
item => Assert.Equal("/item", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsDirectoryContentsIfGivenPathEndsWithSlash()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] { TestItem("/dir/subdir/item1") });
|
||||
|
||||
// Act
|
||||
var contents = instance.GetDirectoryContents("/dir/subdir/");
|
||||
|
||||
// Assert
|
||||
Assert.True(contents.Exists);
|
||||
Assert.Collection(contents,
|
||||
item => Assert.Equal("/dir/subdir/item1", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsDirectoryContentsForNonExistingDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] { TestItem("/dir/subdir/item1") });
|
||||
|
||||
// Act
|
||||
var contents = instance.GetDirectoryContents("/dir/otherdir");
|
||||
|
||||
// Assert
|
||||
Assert.False(contents.Exists);
|
||||
Assert.Throws<InvalidOperationException>(() => contents.GetEnumerator());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncludesSubdirectoriesInDirectoryContents()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] {
|
||||
TestItem("/dir/sub/item1"),
|
||||
TestItem("/dir/sub/item2"),
|
||||
TestItem("/dir/sub2/item"),
|
||||
TestItem("/unrelated/item")
|
||||
});
|
||||
|
||||
// Act
|
||||
var contents = instance.GetDirectoryContents("/dir");
|
||||
|
||||
// Assert
|
||||
Assert.True(contents.Exists);
|
||||
Assert.Collection(contents,
|
||||
item =>
|
||||
{
|
||||
// For this example, verify all properties. Don't need to do this for all examples.
|
||||
Assert.True(item.Exists);
|
||||
Assert.True(item.IsDirectory);
|
||||
Assert.Equal(default(DateTimeOffset), item.LastModified);
|
||||
Assert.Throws<InvalidOperationException>(() => item.Length);
|
||||
Assert.Throws<InvalidOperationException>(() => item.CreateReadStream());
|
||||
Assert.Equal("sub", item.Name);
|
||||
Assert.Equal("/dir/sub", item.PhysicalPath);
|
||||
},
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/dir/sub2", item.PhysicalPath);
|
||||
Assert.True(item.IsDirectory);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasAllAncestorDirectoriesForDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var instance = new InMemoryFileProvider(new[] { TestItem("/a/b/c") });
|
||||
|
||||
// Act/Assert
|
||||
Assert.Collection(instance.GetDirectoryContents("/"),
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/a", item.PhysicalPath);
|
||||
Assert.True(item.IsDirectory);
|
||||
});
|
||||
Assert.Collection(instance.GetDirectoryContents("/a"),
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/a/b", item.PhysicalPath);
|
||||
Assert.True(item.IsDirectory);
|
||||
});
|
||||
Assert.Collection(instance.GetDirectoryContents("/a/b"),
|
||||
item =>
|
||||
{
|
||||
Assert.Equal("/a/b/c", item.PhysicalPath);
|
||||
Assert.False(item.IsDirectory);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Blazor.Common\Microsoft.AspNetCore.Blazor.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Blazor.Mono\Microsoft.AspNetCore.Blazor.Mono.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Mono.Test
|
||||
{
|
||||
public class MonoStaticFileProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void SuppliesJsFiles()
|
||||
{
|
||||
// The collection is small enough that we can assert the exact full list
|
||||
|
||||
Assert.Collection(MonoStaticFileProvider.JsFiles.GetDirectoryContents("/").OrderBy(i => i.Name),
|
||||
item => Assert.Equal("/asmjs", item.PhysicalPath),
|
||||
item => Assert.Equal("/wasm", item.PhysicalPath));
|
||||
|
||||
Assert.Collection(MonoStaticFileProvider.JsFiles.GetDirectoryContents("/asmjs").OrderBy(i => i.Name),
|
||||
item => Assert.Equal("/asmjs/mono.asm.js", item.PhysicalPath),
|
||||
item => Assert.Equal("/asmjs/mono.js", item.PhysicalPath),
|
||||
item => Assert.Equal("/asmjs/mono.js.mem", item.PhysicalPath));
|
||||
|
||||
Assert.Collection(MonoStaticFileProvider.JsFiles.GetDirectoryContents("/wasm").OrderBy(i => i.Name),
|
||||
item => Assert.Equal("/wasm/mono.js", item.PhysicalPath),
|
||||
item => Assert.Equal("/wasm/mono.wasm", item.PhysicalPath));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuppliesBclFiles()
|
||||
{
|
||||
Assert.Collection(MonoStaticFileProvider.BclFiles.GetDirectoryContents("/"),
|
||||
item => Assert.Equal("/bcl", item.PhysicalPath));
|
||||
|
||||
// Not an exhaustive list. The full list is long.
|
||||
var actualBclRootFiles = MonoStaticFileProvider.BclFiles.GetDirectoryContents("/bcl");
|
||||
Assert.Contains(actualBclRootFiles, item => item.PhysicalPath == "/bcl/mscorlib.dll");
|
||||
Assert.Contains(actualBclRootFiles, item => item.PhysicalPath == "/bcl/System.Core.dll");
|
||||
Assert.Contains(actualBclRootFiles, item => item.PhysicalPath == "/bcl/System.dll");
|
||||
|
||||
// Not an exhaustive list. The full list is long.
|
||||
var actualFacades = MonoStaticFileProvider.BclFiles.GetDirectoryContents("/bcl/Facades");
|
||||
Assert.Contains(actualFacades, item => item.PhysicalPath == "/bcl/Facades/netstandard.dll");
|
||||
Assert.Contains(actualFacades, item => item.PhysicalPath == "/bcl/Facades/System.Console.dll");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<BlazorLinkOnBuild>true</BlazorLinkOnBuild>
|
||||
|
||||
<!-- Local alternative to <RunArguments>blazor serve</RunArguments> -->
|
||||
<RunCommand>dotnet</RunCommand>
|
||||
|
|
|
|||
Loading…
Reference in New Issue