diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets index 9cb93fb2e06..01a64d12687 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.After.targets @@ -33,4 +33,5 @@ This file is imported *after* the Microsoft.NET.Sdk/Sdk.targets. + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.HotReload.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.HotReload.targets new file mode 100644 index 00000000000..b092912c2ef --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.HotReload.targets @@ -0,0 +1,84 @@ + + + + + + + true + + + + + + + <_HotReloadAgentAssemblyName>$([System.IO.Path]::GetFileNameWithoutExtension('$(DotNetHotReloadAgentStartupHook)')) + + + + + + + + + + + + + + + <_HotReloadEnvironment Include="DOTNET_STARTUP_HOOKS=$(_HotReloadAgentAssemblyName)" /> + + + + + <_HotReloadEnvironment Include="@(DotNetHotReloadAgentEnvironment->'%(Identity)=%(Value)')" /> + + + + + + + + + + + + + + + + diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 3c1e2691dd1..13027629fc7 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -1658,5 +1658,109 @@ public void AppStartsWithManagedMarshalMethodsLookupEnabled () Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30); Assert.IsTrue (didLaunch, "Activity should have started."); } + + [Test] + public void DeployHotReloadAgentConfiguration () + { + // Create a library project that contains the startup hook + var startupHookLib = new XamarinAndroidLibraryProject { + ProjectName = "MyStartupHook", + Sources = { + new BuildItem.Source ("StartupHook.cs") { + TextContent = () => @" +using System; + +internal static class StartupHook +{ + public static void Initialize () + { + Console.WriteLine (""HOTRELOAD_TEST_HOOK_INITIALIZED=true""); + } +} +" + } + } + }; + + var proj = new XamarinAndroidApplicationProject { + ProjectName = nameof (DeployHotReloadAgentConfiguration), + IsRelease = false, + Imports = { + // Add a .targets file that simulates what dotnet-watch/IDE would inject + new Import (() => "HotReload.targets") { + TextContent = () => @" + + MyStartupHook + + + + + +" + }, + } + }; + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.AddReference (startupHookLib); + proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", @" + Console.WriteLine (""DOTNET_STARTUP_HOOKS="" + Environment.GetEnvironmentVariable(""DOTNET_STARTUP_HOOKS"")); + Console.WriteLine (""HOTRELOAD_TEST_VAR="" + Environment.GetEnvironmentVariable(""HOTRELOAD_TEST_VAR"")); + Console.WriteLine (""ANOTHER_VAR="" + Environment.GetEnvironmentVariable(""ANOTHER_VAR"")); +"); + + var rootPath = Path.Combine (Root, "temp", TestName); + using var libBuilder = CreateDllBuilder (Path.Combine (rootPath, startupHookLib.ProjectName)); + Assert.IsTrue (libBuilder.Build (startupHookLib), "Library build should have succeeded."); + + using var builder = CreateApkBuilder (Path.Combine (rootPath, proj.ProjectName)); + builder.Save (proj); + + var projectDirectory = Path.Combine (rootPath, proj.ProjectName); + var dotnet = new DotNetCLI (Path.Combine (projectDirectory, proj.ProjectFilePath)); + + // Build normally first + Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); + + // Run the DeployHotReloadAgentConfiguration target (hot reload properties come from HotReload.targets) + Assert.IsTrue (dotnet.Build (target: "DeployHotReloadAgentConfiguration"), + "`dotnet build -t:DeployHotReloadAgentConfiguration` should succeed"); + + // Launch the app using adb + ClearAdbLogcat (); + var result = AdbStartActivity ($"{proj.PackageName}/{proj.JavaPackageName}.MainActivity"); + Assert.IsTrue (result.Contains ("Starting: Intent"), $"Activity should have launched. adb output:\n{result}"); + + bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", + Path.Combine (projectDirectory, "logcat.log"), 30); + Assert.IsTrue (didLaunch, "Activity should have started."); + + var logcatOutput = File.ReadAllText (Path.Combine (projectDirectory, "logcat.log")); + + // Verify the startup hook was set via DOTNET_STARTUP_HOOKS + StringAssert.Contains ( + "DOTNET_STARTUP_HOOKS=MyStartupHook", + logcatOutput, + "DOTNET_STARTUP_HOOKS should be set to MyStartupHook" + ); + + // Verify the startup hook was called + StringAssert.Contains ( + "HOTRELOAD_TEST_HOOK_INITIALIZED=true", + logcatOutput, + "StartupHook.Initialize() should have been called" + ); + + // Verify the additional env vars from DotNetHotReloadAgentEnvironment were set + StringAssert.Contains ( + "HOTRELOAD_TEST_VAR=TestValue123", + logcatOutput, + "HOTRELOAD_TEST_VAR should be set from DotNetHotReloadAgentEnvironment" + ); + StringAssert.Contains ( + "ANOTHER_VAR=AnotherValue456", + logcatOutput, + "ANOTHER_VAR should be set from DotNetHotReloadAgentEnvironment" + ); + } } }