From 30e5081d186aaec79f53a8386c43a7fa8f9d01ef Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 5 Jun 2018 11:37:08 -0500 Subject: [PATCH] Fix issue with framework-dependent apphost launched from own directory (dotnet/core-setup#4191) Commit migrated from https://github.com/dotnet/core-setup/commit/6ff3b8f91e2b5fb34e834bb64f421fdb50d6d72d --- src/installer/corehost/cli/libhost.cpp | 34 ++++++----- .../GivenThatICareAboutPortableAppActivation.cs | 68 +++++++++++++++++++++- 2 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/installer/corehost/cli/libhost.cpp b/src/installer/corehost/cli/libhost.cpp index 1fe63e6..82c4c33 100644 --- a/src/installer/corehost/cli/libhost.cpp +++ b/src/installer/corehost/cli/libhost.cpp @@ -51,23 +51,31 @@ void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& na host_mode_t detect_operating_mode(const host_startup_info_t& host_info) { - if (coreclr_exists_in_dir(host_info.dotnet_root) || pal::file_exists(host_info.app_path)) + if (coreclr_exists_in_dir(host_info.dotnet_root)) { - pal::string_t own_deps_json = host_info.dotnet_root; - pal::string_t own_deps_filename = host_info.get_app_name() + _X(".deps.json"); - pal::string_t own_config_filename = host_info.get_app_name() + _X(".runtimeconfig.json"); - append_path(&own_deps_json, own_deps_filename.c_str()); - if (trace::is_enabled()) - { - trace::info(_X("Detecting mode... CoreCLR present in dotnet root [%s] and checking if [%s] file present=[%d]"), - host_info.dotnet_root.c_str(), own_deps_filename.c_str(), pal::file_exists(own_deps_json)); - } - return ((pal::file_exists(own_deps_json) || !pal::file_exists(own_config_filename)) && pal::file_exists(host_info.app_path)) ? host_mode_t::apphost : host_mode_t::split_fx; + // Detect between standalone apphost or legacy split mode (specifying --depsfile and --runtimeconfig) + + pal::string_t deps_in_dotnet_root = host_info.dotnet_root; + pal::string_t deps_filename = host_info.get_app_name() + _X(".deps.json"); + append_path(&deps_in_dotnet_root, deps_filename.c_str()); + bool deps_exists = pal::file_exists(deps_in_dotnet_root); + + trace::info(_X("Detecting mode... CoreCLR present in dotnet root [%d] and checking if [%s] file present=[%d]"), + host_info.dotnet_root.c_str(), deps_filename.c_str(), deps_exists); + + // Name of runtimeconfig file; since no path is included here the check is in the current working directory + pal::string_t config_in_cwd = host_info.get_app_name() + _X(".runtimeconfig.json"); + + return (deps_exists || !pal::file_exists(config_in_cwd)) && pal::file_exists(host_info.app_path) ? host_mode_t::apphost : host_mode_t::split_fx; } - else + + if (pal::file_exists(host_info.app_path)) { - return host_mode_t::muxer; + // Framework-dependent apphost + return host_mode_t::apphost; } + + return host_mode_t::muxer; } void try_patch_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_ver_t& start_ver, pal::string_t* max_str) diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutPortableAppActivation.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutPortableAppActivation.cs index 83df4be..53bce8f 100644 --- a/src/installer/test/HostActivationTests/GivenThatICareAboutPortableAppActivation.cs +++ b/src/installer/test/HostActivationTests/GivenThatICareAboutPortableAppActivation.cs @@ -1,14 +1,17 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; using System.Text.RegularExpressions; using Xunit; -using FluentAssertions; -using Microsoft.DotNet.CoreSetup.Test; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.PortableApp { @@ -276,6 +279,65 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.PortableApp .Fail(); } + [Fact] + public void Framework_Dependent_AppHost_Succeeds() + { + var fixture = PreviouslyPublishedAndRestoredPortableTestProjectFixture + .Copy(); + + // Since SDK doesn't support building framework dependent apphost yet, emulate that behavior + // by creating the executable from apphost.exe + var appExe = fixture.TestProject.AppExe; + var appDllName = Path.GetFileName(fixture.TestProject.AppDll); + + string hostExeName = $"apphost{Constants.ExeSuffix}"; + string builtAppHost = Path.Combine(RepoDirectories.HostArtifacts, hostExeName); + string appDir = Path.GetDirectoryName(appExe); + string appDirHostExe = Path.Combine(appDir, hostExeName); + + // Make a copy of apphost first, replace hash and overwrite app.exe, rather than + // overwrite app.exe and edit in place, because the file is opened as "write" for + // the replacement -- the test fails with ETXTBSY (exit code: 26) in Linux when + // executing a file opened in "write" mode. + File.Copy(builtAppHost, appDirHostExe, true); + using (var sha256 = SHA256.Create()) + { + // Replace the hash with the managed DLL name. + var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes("foobar")); + var hashStr = BitConverter.ToString(hash).Replace("-", "").ToLower(); + AppHostExtensions.SearchAndReplace(appDirHostExe, Encoding.UTF8.GetBytes(hashStr), Encoding.UTF8.GetBytes(appDllName), true); + } + File.Copy(appDirHostExe, appExe, true); + + // Get the framework location that was built + string builtDotnet = fixture.BuiltDotnet.BinPath; + + // Verify running with the default working directory + Command.Create(appExe) + .CaptureStdErr() + .CaptureStdOut() + .EnvironmentVariable("DOTNET_ROOT", builtDotnet) + .EnvironmentVariable("DOTNET_ROOT(x86)", builtDotnet) + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("Hello World"); + + // Verify running from within the working directory + Command.Create(appExe) + .WorkingDirectory(fixture.TestProject.OutputDirectory) + .EnvironmentVariable("DOTNET_ROOT", builtDotnet) + .EnvironmentVariable("DOTNET_ROOT(x86)", builtDotnet) + .CaptureStdErr() + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("Hello World"); + } + private void MoveDepsJsonToSubdirectory(TestProjectFixture testProjectFixture) { var subdirectory = Path.Combine(testProjectFixture.TestProject.ProjectDirectory, "d"); @@ -314,7 +376,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.PortableApp testProjectFixture.TestProject.RuntimeConfigJson = destRuntimeConfig; } - private string CreateAStore(TestProjectFixture testProjectFixture) + private string CreateAStore(TestProjectFixture testProjectFixture) { var storeoutputDirectory = Path.Combine(testProjectFixture.TestProject.ProjectDirectory, "store"); if (!Directory.Exists(storeoutputDirectory)) -- 2.7.4