Fix issue with framework-dependent apphost launched from own directory (dotnet/core...
authorSteve Harter <steveharter@users.noreply.github.com>
Tue, 5 Jun 2018 16:37:08 +0000 (11:37 -0500)
committerGitHub <noreply@github.com>
Tue, 5 Jun 2018 16:37:08 +0000 (11:37 -0500)
Commit migrated from https://github.com/dotnet/core-setup/commit/6ff3b8f91e2b5fb34e834bb64f421fdb50d6d72d

src/installer/corehost/cli/libhost.cpp
src/installer/test/HostActivationTests/GivenThatICareAboutPortableAppActivation.cs

index 1fe63e6..82c4c33 100644 (file)
@@ -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)
index 83df4be..53bce8f 100644 (file)
@@ -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))