Allow runtimeconfig StartupHooks and Environment StartupHooks to both be present...
authorTom de Goede <tom_de_goede@hotmail.com>
Fri, 19 Nov 2021 16:59:10 +0000 (17:59 +0100)
committerGitHub <noreply@github.com>
Fri, 19 Nov 2021 16:59:10 +0000 (08:59 -0800)
Allow both env. variable `DOTNET_STARTUP_HOOKS` and the `.runtimeconfig.json` property `STARTUP_HOOKS` to be specified at the same time. The new behavior is to concatenate the two lists, with the env. variable content going first.

src/installer/tests/HostActivation.Tests/StartupHooks.cs
src/native/corehost/hostpolicy/hostpolicy_context.cpp

index a0533a6..3e16853 100644 (file)
@@ -13,6 +13,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
     {
         private SharedTestState sharedTestState;
         private string startupHookVarName = "DOTNET_STARTUP_HOOKS";
+        private string startupHookRuntimeConfigName = "STARTUP_HOOKS";
         private string startupHookSupport = "System.StartupHookProvider.IsSupported";
 
         public StartupHooks(StartupHooks.SharedTestState fixture)
@@ -105,6 +106,64 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
                 .And.HaveStdOutContaining("Hello World");
         }
 
+        [Fact]
+        public void Muxer_activation_of_RuntimeConfig_StartupHook_Succeeds()
+        {
+            var fixture = sharedTestState.PortableAppFixture.Copy();
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            var startupHookFixture = sharedTestState.StartupHookFixture.Copy();
+            var startupHookDll = startupHookFixture.TestProject.AppDll;
+
+            RuntimeConfig.FromFile(fixture.TestProject.RuntimeConfigJson)
+                .WithProperty(startupHookRuntimeConfigName, startupHookDll)
+                .Save();
+
+            // RuntimeConfig defined startup hook
+            dotnet.Exec(appDll)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should().Pass()
+                .And.HaveStdOutContaining("Hello from startup hook!")
+                .And.HaveStdOutContaining("Hello World");
+        }
+
+        [Fact]
+        public void Muxer_activation_of_RuntimeConfig_And_Environment_StartupHooks_SucceedsInExpectedOrder()
+        {
+            var fixture = sharedTestState.PortableAppFixture.Copy();
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            var startupHookFixture = sharedTestState.StartupHookFixture.Copy();
+            var startupHookDll = startupHookFixture.TestProject.AppDll;
+
+            RuntimeConfig.FromFile(fixture.TestProject.RuntimeConfigJson)
+                .WithProperty(startupHookRuntimeConfigName, startupHookDll)
+                .Save();
+
+            var startupHook2Fixture = sharedTestState.StartupHookWithDependencyFixture.Copy();
+            var startupHook2Dll = startupHook2Fixture.TestProject.AppDll;
+
+            // include any char to counter output from other threads such as in #57243
+            const string wildcardPattern = @"[\r\n\s.]*";
+
+            // RuntimeConfig and Environment startup hooks in expected order
+            dotnet.Exec(appDll)
+                .EnvironmentVariable(startupHookVarName, startupHook2Dll)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should().Pass()
+                .And.HaveStdOutMatching("Hello from startup hook with dependency!" +
+                                        wildcardPattern +
+                                        "Hello from startup hook!" +
+                                        wildcardPattern +
+                                        "Hello World");
+        }
+
         // Empty startup hook variable
         [Fact]
         public void Muxer_activation_of_Empty_StartupHook_Variable_Succeeds()
index 324c70e..22d26f2 100644 (file)
@@ -281,11 +281,16 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
     pal::string_t startup_hooks;
     if (pal::getenv(_X("DOTNET_STARTUP_HOOKS"), &startup_hooks))
     {
-        if (!coreclr_properties.add(common_property::StartUpHooks, startup_hooks.c_str()))
+        const pal::char_t *config_startup_hooks;
+        if (coreclr_properties.try_get(common_property::StartUpHooks, &config_startup_hooks))
         {
-            log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::StartUpHooks));
-            return StatusCode::LibHostDuplicateProperty;
+            // env startup hooks shoold have precedence over config startup hooks
+            // therefore append config_startup_hooks AFTER startup_hooks
+            startup_hooks.push_back(PATH_SEPARATOR);
+            startup_hooks.append(config_startup_hooks);
         }
+        
+        coreclr_properties.add(common_property::StartUpHooks, startup_hooks.c_str());
     }
 
     // Single-File Bundle Probe
@@ -297,7 +302,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
 
         if (!coreclr_properties.add(common_property::BundleProbe, ptr_stream.str().c_str()))
         {
-            log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::StartUpHooks));
+            log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::BundleProbe));
             return StatusCode::LibHostDuplicateProperty;
         }
     }
@@ -312,7 +317,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
 
         if (!coreclr_properties.add(common_property::PInvokeOverride, ptr_stream.str().c_str()))
         {
-            log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::StartUpHooks));
+            log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::PInvokeOverride));
             return StatusCode::LibHostDuplicateProperty;
         }
     }
@@ -321,7 +326,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a
 #if defined(HOSTPOLICY_EMBEDDED)
     if (!coreclr_properties.add(common_property::HostPolicyEmbedded, _X("true")))
     {
-        log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::StartUpHooks));
+        log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::HostPolicyEmbedded));
         return StatusCode::LibHostDuplicateProperty;
     }
 #endif