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 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) public static void Main(string[] args)
{ {

View File

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

View File

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

View File

@ -45,11 +45,24 @@ export const monoPlatform: Platform = {
return methodHandle; 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 // 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 // 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). // 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); monoPlatform.callMethod(entryPointMethod, null, args);
}, },

View File

@ -1,7 +1,7 @@
export interface Platform { export interface Platform {
start(loadAssemblyUrls: string[]): Promise<void>; 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; findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle;
callMethod(method: MethodHandle, target: System_Object | null, args: System_Object[]): System_Object; 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.AspNetCore.Blazor.Internal.Common.FileProviders;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Mono.Cecil;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -50,8 +51,17 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.FileSystem
{ {
var template = File.ReadAllText(path); var template = File.ReadAllText(path);
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
var assemblyEntryPoint = string.Empty;
var binFiles = frameworkFileProvider.GetDirectoryContents("/_bin"); 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; return true;
} }
} }

View File

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

View File

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

View File

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

View File

@ -11,9 +11,9 @@ using Xunit;
namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
{ {
public class StandaloneAppTest 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) : base(browserFixture, serverFixture)
{ {
Navigate("/", noReload: true); Navigate("/", noReload: true);