Adding ability to qualify the entry point of the main assembly

This commit is contained in:
Eugene Bekker 2018-02-10 19:37:36 -05:00 committed by Steve Sanderson
parent 1e0836167d
commit 1653e56b98
10 changed files with 72 additions and 24 deletions

View File

@ -5,7 +5,15 @@ using Microsoft.AspNetCore.Blazor.Browser.Rendering;
namespace StandaloneApp
{
public class Program
public class ProgramX
{
public static void Main(string[] args)
{
new BrowserRenderer().AddComponent<Home>("app");
}
}
public class ProgramY
{
public static void Main(string[] args)
{

View File

@ -6,6 +6,7 @@
<!-- Local alternative to <RunArguments>blazor serve</RunArguments> -->
<RunCommand>dotnet</RunCommand>
<RunArguments>run --project ../../src/Microsoft.AspNetCore.Blazor.DevHost serve</RunArguments>
<StartupObject>StandaloneApp.ProgramY</StartupObject>
</PropertyGroup>
<ItemGroup>

View File

@ -7,11 +7,13 @@ async function boot() {
// Read startup config from the <script> element that's importing this file
const allScriptElems = document.getElementsByTagName('script');
const thisScriptElem = document.currentScript || allScriptElems[allScriptElems.length - 1];
const entryPoint = thisScriptElem.getAttribute('main');
if (!entryPoint) {
throw new Error('Missing "main" attribute on Blazor Config script tag.');
const entryPointDll = thisScriptElem.getAttribute('main');
if (!entryPointDll) {
throw new Error('Missing "main" attribute on Blazor script tag.');
}
const entryPointAssemblyName = getAssemblyNameFromUrl(entryPoint);
// TODO: should method be a required, non-empty field or just an optional *hint*?
const entryPointMethod = thisScriptElem.getAttribute('entry-point');
const entryPointAssemblyName = getAssemblyNameFromUrl(entryPointDll);
const referenceAssembliesCommaSeparated = thisScriptElem.getAttribute('references') || '';
const referenceAssemblies = referenceAssembliesCommaSeparated
.split(',')
@ -19,7 +21,7 @@ async function boot() {
.filter(s => !!s);
// Determine the URLs of the assemblies we want to load
const loadAssemblyUrls = [entryPoint]
const loadAssemblyUrls = [entryPointDll]
.concat(referenceAssemblies)
.map(filename => `/_framework/_bin/${filename}`);
@ -30,7 +32,7 @@ async function boot() {
}
// Start up the application
platform.callEntryPoint(entryPointAssemblyName, []);
platform.callEntryPoint(entryPointAssemblyName, entryPointMethod, []);
}
boot();

View File

@ -45,11 +45,24 @@ export const monoPlatform: Platform = {
return methodHandle;
},
callEntryPoint: function callEntryPoint(assemblyName: string, args: System_Object[]): void {
callEntryPoint: function callEntryPoint(assemblyName: string, methodName: string | null, args: System_Object[]): void {
// TODO: There should be a proper way of running whatever counts as the entrypoint without
// having to specify what method it is, but I haven't found it. The code here assumes
// that the entry point is "<assemblyname>.Program.Main" (i.e., namespace == assembly name).
const entryPointMethod = monoPlatform.findMethod(assemblyName, assemblyName, 'Program', 'Main');
if (!methodName)
methodName = assemblyName + ".Program::Main";
var classAndMethod = methodName.split("::");
if (classAndMethod.length != 2)
throw new Error("malformed entry point method name; could not resolve class name and method name");
methodName = classAndMethod[1];
const nsAndClass = classAndMethod[0];
const lastDot = nsAndClass.lastIndexOf(".");
// It's possible the entry point method has no namespace
const namespace = lastDot > -1 ? classAndMethod[0].substring(0, lastDot) : "";
const className = lastDot > -1 ? classAndMethod[0].substring(lastDot + 1) : classAndMethod[0];
const entryPointMethod = monoPlatform.findMethod(assemblyName, namespace, className, methodName);
monoPlatform.callMethod(entryPointMethod, null, args);
},

View File

@ -1,7 +1,7 @@
export interface Platform {
start(loadAssemblyUrls: string[]): Promise<void>;
callEntryPoint(assemblyName: string, args: System_Object[]);
callEntryPoint(assemblyName: string, assemblyMethod: string | null, args: System_Object[]);
findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle;
callMethod(method: MethodHandle, target: System_Object | null, args: System_Object[]): System_Object;

View File

@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Blazor.Internal.Common.FileProviders;
using Microsoft.Extensions.FileProviders;
using Mono.Cecil;
using System.Collections.Generic;
using System.IO;
@ -50,8 +51,17 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
{
var template = File.ReadAllText(path);
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
var assemblyEntryPoint = string.Empty;
var binFiles = frameworkFileProvider.GetDirectoryContents("/_bin");
result = new IndexHtmlFileProvider(template, assemblyName, binFiles);
using (var asmDef = AssemblyDefinition.ReadAssembly(assemblyPath))
{
var ep = asmDef.EntryPoint;
if (ep != null)
assemblyEntryPoint = $"{ep.DeclaringType.FullName}::{ep.Name}";
}
result = new IndexHtmlFileProvider(template, assemblyName, assemblyEntryPoint, binFiles);
return true;
}
}

View File

@ -15,16 +15,23 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
{
internal class IndexHtmlFileProvider : InMemoryFileProvider
{
public IndexHtmlFileProvider(string htmlTemplate, string assemblyName, IEnumerable<IFileInfo> binFiles)
: base(ComputeContents(htmlTemplate, assemblyName, binFiles))
public IndexHtmlFileProvider(
string htmlTemplate,
string assemblyName,
string assemblyEntryPoint,
IEnumerable<IFileInfo> binFiles) : base(ComputeContents(htmlTemplate, assemblyName, assemblyEntryPoint, binFiles))
{
}
private static IEnumerable<(string, byte[])> ComputeContents(string htmlTemplate, string assemblyName, IEnumerable<IFileInfo> binFiles)
private static IEnumerable<(string, byte[])> ComputeContents(
string htmlTemplate,
string assemblyName,
string assemblyEntryPoint,
IEnumerable<IFileInfo> binFiles)
{
if (htmlTemplate != null)
{
var html = GetIndexHtmlContents(htmlTemplate, assemblyName, binFiles);
var html = GetIndexHtmlContents(htmlTemplate, assemblyName, assemblyEntryPoint, binFiles);
var htmlBytes = Encoding.UTF8.GetBytes(html);
yield return ("/index.html", htmlBytes);
}
@ -48,7 +55,11 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
/// responsible for completing the Blazor boot process.
/// </para>
/// </remarks>
private static string GetIndexHtmlContents(string htmlTemplate, string assemblyName, IEnumerable<IFileInfo> binFiles)
private static string GetIndexHtmlContents(
string htmlTemplate,
string assemblyName,
string assemblyEntryPoint,
IEnumerable<IFileInfo> binFiles)
{
var resultBuilder = new StringBuilder();
@ -86,6 +97,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
AppendScriptTagWithBootConfig(
resultBuilder,
assemblyName,
assemblyEntryPoint,
binFiles,
tag.Attributes);
@ -123,6 +135,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
private static void AppendScriptTagWithBootConfig(
StringBuilder resultBuilder,
string assemblyName,
string assemblyEntryPoint,
IEnumerable<IFileInfo> binFiles,
List<KeyValuePair<string, string>> attributes)
{
@ -136,6 +149,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
attributesDict.Remove("type");
attributesDict["src"] = "/_framework/blazor.js";
attributesDict["main"] = assemblyNameWithExtension;
attributesDict["entry-point"] = assemblyEntryPoint;
attributesDict["references"] = referencesAttribute;
resultBuilder.Append("<script");

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
{
// Arrange
var instance = new IndexHtmlFileProvider(
null, "fakeassembly", Enumerable.Empty<IFileInfo>());
null, "fakeassembly", null, Enumerable.Empty<IFileInfo>());
// Act
var file = instance.GetFileInfo("/index.html");
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
// Arrange
var htmlTemplate = "test";
var instance = new IndexHtmlFileProvider(
htmlTemplate, "fakeassembly", Enumerable.Empty<IFileInfo>());
htmlTemplate, "fakeassembly", null, Enumerable.Empty<IFileInfo>());
// Act
var file = instance.GetFileInfo("/index.html");
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
// Arrange
var htmlTemplate = "test";
var instance = new IndexHtmlFileProvider(
htmlTemplate, "fakeassembly", Enumerable.Empty<IFileInfo>());
htmlTemplate, "fakeassembly", null, Enumerable.Empty<IFileInfo>());
// Act
var directory = instance.GetDirectoryContents(string.Empty);
@ -86,7 +86,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
new TestFileInfo("MyApp.ClassLib.dll"),
};
var instance = new IndexHtmlFileProvider(
htmlTemplate, "MyApp.Entrypoint", dependencies);
htmlTemplate, "MyApp.Entrypoint", null, dependencies);
// Act
var file = instance.GetFileInfo("/index.html");
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
new TestFileInfo("MyApp.ClassLib.dll"),
};
var instance = new IndexHtmlFileProvider(
htmlTemplate, "MyApp.Entrypoint", dependencies);
htmlTemplate, "MyApp.Entrypoint", null, dependencies);
// Act
var file = instance.GetFileInfo("/index.html");

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Blazor.Server.Test
public void FindsReferencedAssemblyGraphRealistic()
{
// Arrange
var standaloneAppAssembly = typeof(StandaloneApp.Program).Assembly;
var standaloneAppAssembly = typeof(StandaloneApp.ProgramY).Assembly;
var provider = new ReferencedAssemblyFileProvider(
standaloneAppAssembly.GetName().Name,
new ReferencedAssemblyResolver(

View File

@ -11,9 +11,9 @@ using Xunit;
namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
{
public class StandaloneAppTest
: ServerTestBase<DevHostServerFixture<StandaloneApp.Program>>
: ServerTestBase<DevHostServerFixture<StandaloneApp.ProgramY>>
{
public StandaloneAppTest(BrowserFixture browserFixture, DevHostServerFixture<StandaloneApp.Program> serverFixture)
public StandaloneAppTest(BrowserFixture browserFixture, DevHostServerFixture<StandaloneApp.ProgramY> serverFixture)
: base(browserFixture, serverFixture)
{
Navigate("/", noReload: true);