From 2f0cf8d3e9283b3f481e5f29d7553a9fa5540e6d Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 14 Aug 2020 14:59:59 -0700 Subject: [PATCH 1/4] Add support for views + SingleFileExe --- .../RelatedAssemblyAttribute.cs | 34 +++++++++++++++---- .../Sdk.Razor.CurrentVersion.targets | 2 ++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs b/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs index 0c89c0e49da2..5bdfb076c0b0 100644 --- a/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs +++ b/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Loader; using Microsoft.AspNetCore.Mvc.Core; namespace Microsoft.AspNetCore.Mvc.ApplicationParts @@ -16,7 +17,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class RelatedAssemblyAttribute : Attribute { - private static readonly Func AssemblyLoadFileDelegate = Assembly.LoadFile; + private static readonly Func LoadFromAssemblyPathDelegate = + AssemblyLoadContext.GetLoadContext(typeof(RelatedAssemblyAttribute).Assembly).LoadFromAssemblyPath; /// /// Initializes a new instance of . @@ -50,7 +52,7 @@ public static IReadOnlyList GetRelatedAssemblies(Assembly assembly, bo throw new ArgumentNullException(nameof(assembly)); } - return GetRelatedAssemblies(assembly, throwOnError, File.Exists, AssemblyLoadFileDelegate); + return GetRelatedAssemblies(assembly, throwOnError, File.Exists, LoadFromAssemblyPathDelegate); } internal static IReadOnlyList GetRelatedAssemblies( @@ -66,7 +68,7 @@ internal static IReadOnlyList GetRelatedAssemblies( // MVC will specifically look for related parts in the same physical directory as the assembly. // No-op if the assembly does not have a location. - if (assembly.IsDynamic || string.IsNullOrEmpty(assembly.Location)) + if (assembly.IsDynamic) { return Array.Empty(); } @@ -78,8 +80,10 @@ internal static IReadOnlyList GetRelatedAssemblies( } var assemblyName = assembly.GetName().Name; - var assemblyLocation = assembly.Location; - var assemblyDirectory = Path.GetDirectoryName(assemblyLocation); + // Assembly.Location may be null for a single-file exe. In this case, attempt to look for related parts in the app's base directory + var assemblyDirectory = string.IsNullOrEmpty(assembly.Location) ? + AppContext.BaseDirectory : + Path.GetDirectoryName(assembly.Location); var relatedAssemblies = new List(); for (var i = 0; i < attributes.Length; i++) @@ -91,6 +95,22 @@ internal static IReadOnlyList GetRelatedAssemblies( Resources.FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf(nameof(RelatedAssemblyAttribute), assemblyName)); } + var relatedAssemblyName = new AssemblyName(attribute.AssemblyFileName); + Assembly relatedAssembly; + try + { + // Perform a cursory check to determine if the Assembly has already been loaded + // before going to disk. In the ordinary case, related parts that are part of + // application's reference closure should already be loaded. + relatedAssembly = Assembly.Load(relatedAssemblyName); + relatedAssemblies.Add(relatedAssembly); + continue; + } + catch (IOException) + { + // The assembly isn't already loaded. Patience, we'll attempt to load it from disk next. + } + var relatedAssemblyLocation = Path.Combine(assemblyDirectory, attribute.AssemblyFileName + ".dll"); if (!fileExists(relatedAssemblyLocation)) { @@ -106,7 +126,7 @@ internal static IReadOnlyList GetRelatedAssemblies( } } - var relatedAssembly = loadFile(relatedAssemblyLocation); + relatedAssembly = loadFile(relatedAssemblyLocation); relatedAssemblies.Add(relatedAssembly); } diff --git a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets index 277c0f002180..24865730c8e5 100644 --- a/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets +++ b/src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets @@ -720,10 +720,12 @@ Copyright (c) .NET Foundation. All rights reserved. %(Filename)%(Extension) PreserveNewest + true %(Filename)%(Extension) PreserveNewest + true From ad8a927e8526b4720bc069d54feb4ef46c0fedcd Mon Sep 17 00:00:00 2001 From: Pranav K Date: Tue, 18 Aug 2020 17:52:17 -0700 Subject: [PATCH 2/4] Changes per discussion. Add a test --- .../RelatedAssemblyAttribute.cs | 54 +++++++------- .../RelatedAssemblyPartTest.cs | 50 +++++++++---- .../Infrastructure/GenerateTestProps.targets | 2 +- src/ProjectTemplates/Shared/AspNetProcess.cs | 19 ++++- src/ProjectTemplates/Shared/Project.cs | 8 ++- .../Infrastructure/GenerateTestProps.targets | 2 +- src/ProjectTemplates/test/MvcTemplateTest.cs | 71 +++++++++++++++++++ 7 files changed, 158 insertions(+), 48 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs b/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs index 5bdfb076c0b0..bda87a8b3e30 100644 --- a/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs +++ b/src/Mvc/Mvc.Core/src/ApplicationParts/RelatedAssemblyAttribute.cs @@ -17,9 +17,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class RelatedAssemblyAttribute : Attribute { - private static readonly Func LoadFromAssemblyPathDelegate = - AssemblyLoadContext.GetLoadContext(typeof(RelatedAssemblyAttribute).Assembly).LoadFromAssemblyPath; - /// /// Initializes a new instance of . /// @@ -52,14 +49,15 @@ public static IReadOnlyList GetRelatedAssemblies(Assembly assembly, bo throw new ArgumentNullException(nameof(assembly)); } - return GetRelatedAssemblies(assembly, throwOnError, File.Exists, LoadFromAssemblyPathDelegate); + var loadContext = AssemblyLoadContext.GetLoadContext(assembly) ?? AssemblyLoadContext.Default; + return GetRelatedAssemblies(assembly, throwOnError, File.Exists, new AssemblyLoadContextWrapper(loadContext)); } internal static IReadOnlyList GetRelatedAssemblies( Assembly assembly, bool throwOnError, Func fileExists, - Func loadFile) + AssemblyLoadContextWrapper assemblyLoadContext) { if (assembly == null) { @@ -95,42 +93,46 @@ internal static IReadOnlyList GetRelatedAssemblies( Resources.FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf(nameof(RelatedAssemblyAttribute), assemblyName)); } - var relatedAssemblyName = new AssemblyName(attribute.AssemblyFileName); Assembly relatedAssembly; - try + var relatedAssemblyLocation = Path.Combine(assemblyDirectory, attribute.AssemblyFileName + ".dll"); + if (fileExists(relatedAssemblyLocation)) { - // Perform a cursory check to determine if the Assembly has already been loaded - // before going to disk. In the ordinary case, related parts that are part of - // application's reference closure should already be loaded. - relatedAssembly = Assembly.Load(relatedAssemblyName); - relatedAssemblies.Add(relatedAssembly); - continue; + relatedAssembly = assemblyLoadContext.LoadFromAssemblyPath(relatedAssemblyLocation); } - catch (IOException) + else { - // The assembly isn't already loaded. Patience, we'll attempt to load it from disk next. - } - - var relatedAssemblyLocation = Path.Combine(assemblyDirectory, attribute.AssemblyFileName + ".dll"); - if (!fileExists(relatedAssemblyLocation)) - { - if (throwOnError) + try { - throw new FileNotFoundException( - Resources.FormatRelatedAssemblyAttribute_CouldNotBeFound(attribute.AssemblyFileName, assemblyName, assemblyDirectory), - relatedAssemblyLocation); + var relatedAssemblyName = new AssemblyName(attribute.AssemblyFileName); + relatedAssembly = assemblyLoadContext.LoadFromAssemblyName(relatedAssemblyName); } - else + catch when (!throwOnError) { + // Ignore assembly load failures when throwOnError = false. continue; } } - relatedAssembly = loadFile(relatedAssemblyLocation); relatedAssemblies.Add(relatedAssembly); } return relatedAssemblies; } + + internal class AssemblyLoadContextWrapper + { + private readonly AssemblyLoadContext _loadContext; + + public AssemblyLoadContextWrapper(AssemblyLoadContext loadContext) + { + _loadContext = loadContext; + } + + public virtual Assembly LoadFromAssemblyName(AssemblyName assemblyName) + => _loadContext.LoadFromAssemblyName(assemblyName); + + public virtual Assembly LoadFromAssemblyPath(string assemblyPath) + => _loadContext.LoadFromAssemblyPath(assemblyPath); + } } } diff --git a/src/Mvc/Mvc.Core/test/ApplicationParts/RelatedAssemblyPartTest.cs b/src/Mvc/Mvc.Core/test/ApplicationParts/RelatedAssemblyPartTest.cs index 33d5e9d0e515..5e80484c66c5 100644 --- a/src/Mvc/Mvc.Core/test/ApplicationParts/RelatedAssemblyPartTest.cs +++ b/src/Mvc/Mvc.Core/test/ApplicationParts/RelatedAssemblyPartTest.cs @@ -1,10 +1,12 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.IO; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Loader; using Xunit; namespace Microsoft.AspNetCore.Mvc.ApplicationParts @@ -43,7 +45,6 @@ public void GetRelatedAssemblies_ThrowsIfRelatedAttributeReferencesSelf() public void GetRelatedAssemblies_ThrowsIfAssemblyCannotBeFound() { // Arrange - var expected = $"Related assembly 'DoesNotExist' specified by assembly 'MyAssembly' could not be found in the directory {AssemblyDirectory}. Related assemblies must be co-located with the specifying assemblies."; var assemblyPath = Path.Combine(AssemblyDirectory, "MyAssembly.dll"); var assembly = new TestAssembly { @@ -51,27 +52,32 @@ public void GetRelatedAssemblies_ThrowsIfAssemblyCannotBeFound() }; // Act & Assert - var ex = Assert.Throws(() => RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true)); - Assert.Equal(expected, ex.Message); - Assert.Equal(Path.Combine(AssemblyDirectory, "DoesNotExist.dll"), ex.FileName); + Assert.Throws(() => RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true)); } [Fact] - public void GetRelatedAssemblies_LoadsRelatedAssembly() + public void GetRelatedAssemblies_ReadsAssemblyFromLoadContext_IfItAlreadyExists() { // Arrange - var destination = Path.Combine(AssemblyDirectory, "RelatedAssembly.dll"); + var expected = $"Related assembly 'DoesNotExist' specified by assembly 'MyAssembly' could not be found in the directory {AssemblyDirectory}. Related assemblies must be co-located with the specifying assemblies."; + var assemblyPath = Path.Combine(AssemblyDirectory, "MyAssembly.dll"); + var relatedAssembly = typeof(RelatedAssemblyPartTest).Assembly; var assembly = new TestAssembly { - AttributeAssembly = "RelatedAssembly", + AttributeAssembly = "RelatedAssembly" }; - var relatedAssembly = typeof(RelatedAssemblyPartTest).Assembly; - - var result = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true, file => true, file => + var loadContext = new TestableAssemblyLoadContextWrapper { - Assert.Equal(file, destination); - return relatedAssembly; - }); + Assemblies = + { + ["RelatedAssembly"] = relatedAssembly, + } + }; + + // Act + var result = RelatedAssemblyAttribute.GetRelatedAssemblies(assembly, throwOnError: true, file => false, loadContext); + + // Assert Assert.Equal(new[] { relatedAssembly }, result); } @@ -94,5 +100,21 @@ public override object[] GetCustomAttributes(Type attributeType, bool inherit) return new[] { attribute }; } } + + private class TestableAssemblyLoadContextWrapper : RelatedAssemblyAttribute.AssemblyLoadContextWrapper + { + public TestableAssemblyLoadContextWrapper() : base(AssemblyLoadContext.Default) + { + } + + public Dictionary Assemblies { get; } = new Dictionary(); + + public override Assembly LoadFromAssemblyPath(string assemblyPath) => throw new NotSupportedException(); + + public override Assembly LoadFromAssemblyName(AssemblyName assemblyName) + { + return Assemblies[assemblyName.Name]; + } + } } } diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/GenerateTestProps.targets b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/GenerateTestProps.targets index a97cf0dbd283..10ce80aec393 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/GenerateTestProps.targets +++ b/src/ProjectTemplates/BlazorTemplates.Tests/Infrastructure/GenerateTestProps.targets @@ -35,7 +35,7 @@ MicrosoftNETSdkRazorPackageVersion=$(MicrosoftNETSdkRazorPackageVersion); MicrosoftAspNetCoreAppRefPackageVersion=$(MicrosoftAspNetCoreAppRefPackageVersion); MicrosoftAspNetCoreAppRuntimePackageVersion=@(_RuntimePackageVersionInfo->'%(PackageVersion)'); - SupportedRuntimeIdentifiers=$(SupportedRuntimeIdentifiers); + SupportedRuntimeIdentifiers=$(SupportedRuntimeIdentifiers.Trim()); DefaultNetCoreTargetFramework=$(DefaultNetCoreTargetFramework); RepoRoot=$(RepoRoot); Configuration=$(Configuration); diff --git a/src/ProjectTemplates/Shared/AspNetProcess.cs b/src/ProjectTemplates/Shared/AspNetProcess.cs index 7412e3b3697b..afc34a3f30d4 100644 --- a/src/ProjectTemplates/Shared/AspNetProcess.cs +++ b/src/ProjectTemplates/Shared/AspNetProcess.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -59,9 +60,21 @@ public AspNetProcess( output.WriteLine("Running ASP.NET Core application..."); - var arguments = published ? $"exec {dllPath}" : "run --no-build"; + string process; + string arguments; + if (published) + { + // When publishingu used the app host to run the app. This makes it easy to consistently run for regular and single-file publish + process = OperatingSystem.IsWindows() ? dllPath + ".exe" : dllPath; + arguments = null; + } + else + { + process = DotNetMuxer.MuxerPathOrDefault(); + arguments = "run --no-build"; + } - logger?.LogInformation($"AspNetProcess - process: {DotNetMuxer.MuxerPathOrDefault()} arguments: {arguments}"); + logger?.LogInformation($"AspNetProcess - process: {process} arguments: {arguments}"); var finalEnvironmentVariables = new Dictionary(environmentVariables) { @@ -69,7 +82,7 @@ public AspNetProcess( ["ASPNETCORE_Kestrel__Certificates__Default__Password"] = _developmentCertificate.CertificatePassword, }; - Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: finalEnvironmentVariables); + Process = ProcessEx.Run(output, workingDirectory, process, arguments, envVars: finalEnvironmentVariables); logger?.LogInformation("AspNetProcess - process started"); diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs index cb28d482f428..fbfeee80c497 100644 --- a/src/ProjectTemplates/Shared/Project.cs +++ b/src/ProjectTemplates/Shared/Project.cs @@ -110,14 +110,16 @@ internal async Task RunDotNetNewAsync( } } - internal async Task RunDotNetPublishAsync(IDictionary packageOptions = null, string additionalArgs = null) + internal async Task RunDotNetPublishAsync(IDictionary packageOptions = null, string additionalArgs = null, bool noRestore = true) { Output.WriteLine("Publishing ASP.NET Core application..."); // Avoid restoring as part of build or publish. These projects should have already restored as part of running dotnet new. Explicitly disabling restore // should avoid any global contention and we can execute a build or publish in a lock-free way - using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish --no-restore -c Release /bl {additionalArgs}", packageOptions); + var restoreArgs = noRestore ? "--no-restore" : null; + + using var result = ProcessEx.Run(Output, TemplateOutputDir, DotNetMuxer.MuxerPathOrDefault(), $"publish {restoreArgs} -c Release /bl {additionalArgs}", packageOptions); await result.Exited; CaptureBinLogOnFailure(result); return new ProcessResult(result); @@ -188,7 +190,7 @@ internal AspNetProcess StartPublishedProjectAsync(bool hasListeningUri = true) ["ASPNETCORE_Logging__Console__FormatterOptions__IncludeScopes"] = "true", }; - var projectDll = $"{ProjectName}.dll"; + var projectDll = Path.Combine(TemplatePublishDir, ProjectName); return new AspNetProcess(Output, TemplatePublishDir, projectDll, environment, published: true, hasListeningUri: hasListeningUri); } diff --git a/src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets b/src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets index a97cf0dbd283..10ce80aec393 100644 --- a/src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets +++ b/src/ProjectTemplates/test/Infrastructure/GenerateTestProps.targets @@ -35,7 +35,7 @@ MicrosoftNETSdkRazorPackageVersion=$(MicrosoftNETSdkRazorPackageVersion); MicrosoftAspNetCoreAppRefPackageVersion=$(MicrosoftAspNetCoreAppRefPackageVersion); MicrosoftAspNetCoreAppRuntimePackageVersion=@(_RuntimePackageVersionInfo->'%(PackageVersion)'); - SupportedRuntimeIdentifiers=$(SupportedRuntimeIdentifiers); + SupportedRuntimeIdentifiers=$(SupportedRuntimeIdentifiers.Trim()); DefaultNetCoreTargetFramework=$(DefaultNetCoreTargetFramework); RepoRoot=$(RepoRoot); Configuration=$(Configuration); diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index 15a1c873007c..9136d267178a 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -223,6 +223,77 @@ public async Task MvcTemplate_IndividualAuth(bool useLocalDB) } } + [ConditionalFact] + [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + public async Task MvcTemplate_SingleFileExe() + { + // This test verifies publishing an MVC app as a single file exe works. We'll limit testing + // this to a few operating systems to make our lives easier. + string runtimeIdentifer; + if (OperatingSystem.IsWindows()) + { + runtimeIdentifer = "win-x64"; + } + else if (OperatingSystem.IsLinux()) + { + runtimeIdentifer = "linux-x64"; + } + else + { + return; + } + + Project = await ProjectFactory.GetOrCreateProject("mvcindividualuld", Output); + Project.RuntimeIdentifier = runtimeIdentifer; + + var createResult = await Project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: true); + Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); + + var publishResult = await Project.RunDotNetPublishAsync(additionalArgs: $"/p:PublishSingleFile=true -r {runtimeIdentifer}", noRestore: false); + Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult)); + + var pages = new[] + { + new Page + { + // Verify a view from the app works + Url = PageUrls.HomeUrl, + Links = new [] + { + PageUrls.HomeUrl, + PageUrls.RegisterUrl, + PageUrls.LoginUrl, + PageUrls.HomeUrl, + PageUrls.PrivacyUrl, + PageUrls.DocsUrl, + PageUrls.PrivacyUrl + } + }, + new Page + { + // Verify a view from a RCL (in this case IdentityUI) works + Url = PageUrls.RegisterUrl, + Links = new [] + { + PageUrls.HomeUrl, + PageUrls.RegisterUrl, + PageUrls.LoginUrl, + PageUrls.HomeUrl, + PageUrls.PrivacyUrl, + PageUrls.ExternalArticle, + PageUrls.PrivacyUrl + } + }, + }; + + using var aspNetProcess = Project.StartPublishedProjectAsync(); + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process)); + + await aspNetProcess.AssertPagesOk(pages); + } + [Fact] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/23993")] public async Task MvcTemplate_RazorRuntimeCompilation_BuildsAndPublishes() From 57db5937f8cb6ae1f7dd00b3a79d191021df0b89 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 19 Aug 2020 11:23:02 -0700 Subject: [PATCH 3/4] Fixup test --- src/ProjectTemplates/Shared/AspNetProcess.cs | 15 ++++++++++++--- src/ProjectTemplates/Shared/Project.cs | 6 +++--- src/ProjectTemplates/test/MvcTemplateTest.cs | 4 ++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ProjectTemplates/Shared/AspNetProcess.cs b/src/ProjectTemplates/Shared/AspNetProcess.cs index afc34a3f30d4..13a9281c5620 100644 --- a/src/ProjectTemplates/Shared/AspNetProcess.cs +++ b/src/ProjectTemplates/Shared/AspNetProcess.cs @@ -42,6 +42,7 @@ public AspNetProcess( IDictionary environmentVariables, bool published, bool hasListeningUri = true, + bool usePublishedAppHost = false, ILogger logger = null) { _developmentCertificate = DevelopmentCertificate.Create(workingDirectory); @@ -64,9 +65,17 @@ public AspNetProcess( string arguments; if (published) { - // When publishingu used the app host to run the app. This makes it easy to consistently run for regular and single-file publish - process = OperatingSystem.IsWindows() ? dllPath + ".exe" : dllPath; - arguments = null; + if (usePublishedAppHost) + { + // When publishingu used the app host to run the app. This makes it easy to consistently run for regular and single-file publish + process = Path.ChangeExtension(dllPath, OperatingSystem.IsWindows() ? ".exe" : null); + arguments = null; + } + else + { + process = DotNetMuxer.MuxerPathOrDefault(); + arguments = $"exec {dllPath}"; + } } else { diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs index fbfeee80c497..e1ce4b2a4164 100644 --- a/src/ProjectTemplates/Shared/Project.cs +++ b/src/ProjectTemplates/Shared/Project.cs @@ -179,7 +179,7 @@ internal AspNetProcess StartBuiltProjectAsync(bool hasListeningUri = true, ILogg return new AspNetProcess(Output, TemplateOutputDir, projectDll, environment, published: false, hasListeningUri: hasListeningUri, logger: logger); } - internal AspNetProcess StartPublishedProjectAsync(bool hasListeningUri = true) + internal AspNetProcess StartPublishedProjectAsync(bool hasListeningUri = true, bool usePublishedAppHost = false) { var environment = new Dictionary { @@ -190,8 +190,8 @@ internal AspNetProcess StartPublishedProjectAsync(bool hasListeningUri = true) ["ASPNETCORE_Logging__Console__FormatterOptions__IncludeScopes"] = "true", }; - var projectDll = Path.Combine(TemplatePublishDir, ProjectName); - return new AspNetProcess(Output, TemplatePublishDir, projectDll, environment, published: true, hasListeningUri: hasListeningUri); + var projectDll = Path.Combine(TemplatePublishDir, $"{ProjectName}.dll"); + return new AspNetProcess(Output, TemplatePublishDir, projectDll, environment, published: true, hasListeningUri: hasListeningUri, usePublishedAppHost: usePublishedAppHost); } internal async Task RunDotNetEfCreateMigrationAsync(string migrationName) diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index 9136d267178a..ee33c7034e74 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -243,7 +243,7 @@ public async Task MvcTemplate_SingleFileExe() return; } - Project = await ProjectFactory.GetOrCreateProject("mvcindividualuld", Output); + Project = await ProjectFactory.GetOrCreateProject("mvcsinglefileexe", Output); Project.RuntimeIdentifier = runtimeIdentifer; var createResult = await Project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: true); @@ -286,7 +286,7 @@ public async Task MvcTemplate_SingleFileExe() }, }; - using var aspNetProcess = Project.StartPublishedProjectAsync(); + using var aspNetProcess = Project.StartPublishedProjectAsync(usePublishedAppHost: true); Assert.False( aspNetProcess.Process.HasExited, ErrorMessages.GetFailedProcessMessageOrEmpty("Run published project", Project, aspNetProcess.Process)); From 6a9241b9209c13dc4f92c71905a7a8806155f7d6 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 20 Aug 2020 14:59:26 -0700 Subject: [PATCH 4/4] Skip test for now --- src/ProjectTemplates/test/MvcTemplateTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index ee33c7034e74..e3fdf66b3aab 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -223,7 +223,7 @@ public async Task MvcTemplate_IndividualAuth(bool useLocalDB) } } - [ConditionalFact] + [ConditionalFact(Skip = "https://github.com/dotnet/aspnetcore/issues/25103")] [SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] public async Task MvcTemplate_SingleFileExe() {