Skip to content

Fix emulator boot and deploy flow for dotnet run#10969

Open
rmarinho wants to merge 5 commits intomainfrom
dev/ensure-device-booted-deploy
Open

Fix emulator boot and deploy flow for dotnet run#10969
rmarinho wants to merge 5 commits intomainfrom
dev/ensure-device-booted-deploy

Conversation

@rmarinho
Copy link
Member

@rmarinho rmarinho commented Mar 18, 2026

Summary

Running dotnet run --project <app>.csproj with a stopped Android emulator fails to boot the emulator and deploy the app, producing adb: no devices/emulators found or adb: device '<avd>' not found errors.

This PR fixes the MSBuild target ordering so _EnsureDeviceBooted fires reliably during dotnet run deploy.

Fixes #10965

Problem & Fix

_EnsureDeviceBooted not firing during DeployToDevice

The .NET SDK's dotnet run invokes DeployToDevice via ProjectInstance.Build(["DeployToDevice"]) in-process. In this execution mode, BeforeTargets hooks are not reliably triggered — only DependsOnTargets chains are respected.

Fix: Add _EnsureDeviceBooted to DeployToDeviceDependsOnTargets in BuildOrder.targets, and to _AndroidComputeRunArgumentsDependsOn in Application.targets, ensuring the emulator boots via the dependency chain during both the main build and the deploy step.

Note: The BootAndroidEmulator.cs task itself was already updated to delegate to EmulatorRunner.BootEmulatorAsync in #10949 (now merged). This PR only addresses the MSBuild target wiring.

A related issue where Device was not passed as a global property to the deploy step was fixed in the .NET SDK (dotnet/sdk#53018).

Changes

  • Microsoft.Android.Sdk.Application.targets — Added _EnsureDeviceBooted to _AndroidComputeRunArgumentsDependsOn, simplified BeforeTargets to just _GetPrimaryCpuAbi (commercial builds), updated comments
  • Microsoft.Android.Sdk.BuildOrder.targets — Added _EnsureDeviceBooted to both DeployToDeviceDependsOnTargets variants (fast-deploy and non-fast-deploy)
  • BuildOrderTests.cs — Added DeployToDeviceDependsOn_ContainsEnsureDeviceBooted test

Testing

  • Unit test: DeployToDeviceDependsOn_ContainsEnsureDeviceBooted validates the target chain
  • Manual E2E: Verified on macOS with emulator 36.4.10 — emulator boots from cold, app deploys and launches with logcat streaming

Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem on the description sounds like:

Which should be in main, if you have at least:

@rmarinho rmarinho marked this pull request as ready for review March 18, 2026 20:37
Copilot AI review requested due to automatic review settings March 18, 2026 20:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes dotnet run --project <app>.csproj deploy failures when the selected Android emulator is stopped by ensuring the emulator-boot target runs via DependsOnTargets, and improving emulator detection/boot polling in the BootAndroidEmulator MSBuild task.

Changes:

  • Add _EnsureDeviceBooted into DeployToDeviceDependsOnTargets and _AndroidComputeRunArgumentsDependsOn so it runs reliably during in-proc ProjectInstance.Build(["DeployToDevice"]).
  • Improve AVD name detection by preferring adb shell getprop ro.boot.qemu.avd_name, with fallback to adb emu avd name.
  • Handle macOS emulator launcher “fork then exit 0” behavior by continuing to poll adb devices on exit code 0.
  • Add a unit test asserting _EnsureDeviceBooted is included in DeployToDeviceDependsOnTargets.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets Ensures _EnsureDeviceBooted participates in run-argument computation and clarifies execution rationale.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets Adds _EnsureDeviceBooted to deploy dependency chains (fast deploy + non-fast deploy).
src/Xamarin.Android.Build.Tasks/Tasks/BootAndroidEmulator.cs Updates AVD name detection and improves macOS emulator process exit handling.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildOrderTests.cs Adds coverage to validate deploy dependency chain includes _EnsureDeviceBooted.

You can also share your feedback on Copilot code review. Take the survey.

@rmarinho rmarinho force-pushed the dev/ensure-device-booted-deploy branch 2 times, most recently from aa3f844 to d188a4b Compare March 19, 2026 16:27
@rmarinho rmarinho force-pushed the dev/ensure-device-booted-deploy branch 2 times, most recently from 62a458e to a2787ca Compare March 19, 2026 22:45
rmarinho and others added 5 commits March 23, 2026 15:12
Address multiple issues preventing 'dotnet run --project' from
successfully booting an Android emulator and deploying the app.

Fixes: #10965

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The dotnet/sdk fix (dotnet/sdk#53018) now properly passes the Device
global property to DeployToDevice via InvalidateGlobalProperties.
Remove the device.props write/read cache that was working around this.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The DeployToDeviceDependsOnTargets property expands to multiple log
lines because it includes $(_MinimalSignAndroidPackageDependsOn).
The test was only checking the first line, which never contained
_EnsureDeviceBooted.

Switch to MSBuild property functions (Contains/IndexOf) to validate
directly in MSBuild, avoiding log parsing entirely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the readable log-parsing approach (fixed by joining all output
lines for multi-line property matching) and add a separate MSBuild
property function test as a safety net.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The IndexOf check for _EnsureDeviceBooted vs _DeployApk was scanning the
entire build output, not just the property value. _DeployApk appears as
a target reference earlier in the log, causing a false positive failure.

Ordering is already validated reliably by DeployToDeviceDependsOn_MSBuildValidation
which checks the property value directly via MSBuild functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rmarinho rmarinho force-pushed the dev/ensure-device-booted-deploy branch from 54bfb68 to 17d3f5f Compare March 23, 2026 15:13
Copy link
Member

@jonathanpeppers jonathanpeppers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather this have a single end-to-end test that emulates the dotnet run pipeline:

  • dotnet build
  • dotnet build --no-restore -t:DeployToDevice
  • Assert DeployToDevice ran _EnsureDeviceBooted target
  • Assert the app is installed

You'll need to put this in tests/MSBuildDeviceIntegration, so you'll have access to a running emulator.

An emulator will already be booted, but that is OK. I think the bug here is that DeployToApk doesn't run _EnsureDeviceBooted.

Check there isn't an existing test that runs DeployToApk -- you could maybe update the existing test instead of adding new.

Comment on lines +49 to +62
[Test]
public void DeployToDeviceDependsOn_ContainsEnsureDeviceBooted ()
{
// _EnsureDeviceBooted must be in DeployToDeviceDependsOnTargets so that it fires
// when the .NET SDK calls ProjectInstance.Build(["DeployToDevice"]) in-process,
// where BeforeTargets hooks are not reliably triggered.
var checkTargets = new Import (() => "CheckDeployOrder.targets") {
TextContent = () => """
<Project>
<Target Name="_CheckDeployOrder">
<Message Text="DeployToDeviceDependsOnTargets=$(DeployToDeviceDependsOnTargets)" Importance="high" />
</Target>
</Project>
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is kind of pointless? It just checks a target named _EnsureDeviceBooted is in $(DeployToDeviceDependsOnTargets)?

That isn't really testing anything.

Comment on lines +111 to +113
using var builder = CreateApkBuilder ();
Assert.IsTrue (builder.RunTarget (proj, "_CheckDeployOrder"),
"Build should have succeeded — _EnsureDeviceBooted must be in DeployToDeviceDependsOnTargets before _DeployApk.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this test, it's not really testing anything. It just checks a target named _EnsureDeviceBooted is in a property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

_EnsureDeviceBooted target does not fire during dotnet run deploy step

3 participants