Skip to content

Conversation

…rget (#10576)

Context: https://github.com/dotnet/sdk/blob/2b9fc02a265c735f2132e4e3626e94962e48bdf5/documentation/specs/dotnet-run-for-maui.md

This implements the first couple of steps to support new `dotnet run`
behavior in .NET 11.

* `$(Device)` MSBuild property to specify target device (passed from
  `dotnet run --device <id>`). This will simply set `$(AdbTarget)` for
  now.

* `ComputeAvailableDevices` MSBuild target to get the list of
  connected Android devices/emulators using `adb devices` command:

    Target ComputeAvailableDevices  55 ms
      Using "GetAvailableAndroidDevices" task from assembly "D:\src\xamarin-android\bin\Debug\lib\packs\Microsoft.Android.Sdk.Windows\36.1.99\targets\..\tools\Xamarin.Android.Build.Tasks.dll".
      Task GetAvailableAndroidDevices  55 ms
        Assembly = D:\src\xamarin-android\bin\Debug\lib\packs\Microsoft.Android.Sdk.Windows\36.1.99\tools\Xamarin.Android.Build.Tasks.dll
        Parameters
          16:47:08.2643378  C:\Program Files (x86)\Android\android-sdk\platform-tools\adb.exe devices -l
          16:47:08.2867570  List of devices attached
          16:47:08.2867734  0A041FDD400327  device product:redfin model:Pixel_5 device:redfin transport_id:2
          16:47:08.2867765  emulator-5554  device product:sdk_gphone64_x86_64 model:sdk_gphone64_x86_64 device:emu64xa transport_id:1
          16:47:08.2920363  Running process: C:\Program Files (x86)\Android\android-sdk\platform-tools\adb.exe -s emulator-5554 emu avd name
          16:47:08.3172534  pixel_7_-_api_36
          16:47:08.3172747  OK
          16:47:08.3183905  Running process: exit code == 0
          16:47:08.3185099  Found 2 Android device(s)/emulator(s)
        OutputItems
          TargetOutputs
            0A041FDD400327
              Status = Online
              Type = Device
              Device = redfin
              TransportId = 2
              Description = Pixel 5
              Product = redfin
              Model = Pixel_5
            emulator-5554
              Status = Online
              Type = Emulator
              Device = emu64xa
              TransportId = 1
              Description = pixel 7 - api 36
              Product = sdk_gphone64_x86_64
              Model = sdk_gphone64_x86_64

Some of the extra MSBuild item metadata was completely optional, I
just left anything that `adb devices -l` reports to be in here.

Added unit tests for `GetAvailableAndroidDevices` task, that mostly
test input/output parsing without actually running `adb`.
Context: dotnet/sdk#51337
Context: dotnet/sdk#51914

In 63f7cba, we added the `ComputeAvailableDevices` MSBuild target,
which I was able to test end-to-end:

    D:\src\helloandroid> D:\src\dotnet\sdk\artifacts\bin\redist\Debug\dotnet\dotnet.exe run -bl
    Select a device to run on:

    > 0A041FDD400327 - Pixel 5
    emulator-5554 - pixel 7 - api 36

    Type to search

Unfortunately, the AVD name is returned from `adb emu avd name`, which
simply returns the property:

    > adb -s emulator-5554 shell getprop | grep avd_name
    [ro.boot.qemu.avd_name]: [pixel_7_-_api_36]

We can call `TextInfo.ToTitleCase()`, replace underscores with spaces,
and replace "Api" with "API" to make the AVD name more user-friendly.

The only other alternative I considered was parsing the
`~/.android/avd/<name>.ini` file to get the `displayname` property,
but it would still require calling `adb emu avd name` to *get* the
path to this `.ini` file. It felt more straightforward to just format
the AVD name directly.
Context: https://github.com/dotnet/sdk/blob/c164a9bc1246c48191fb780992530f0fe975141b/documentation/specs/dotnet-run-for-maui.md

Implements the `DeployToDevice` MSBuild target per the `dotnet run` spec
for .NET MAUI scenarios. This target allows deploying an already-built
app to a device without rebuilding, supporting the `dotnet run`
workflow.

The target reuses existing deployment logic but skips the full build,
enabling faster iteration when only deployment is needed e.g.
`--no-build`.

Also consolidates `$(SignAndroidPackageDependsOn)`,
`$(InstallDependsOnTargets)`, and `$(UninstallDependsOnTargets)`
properties into `BuildOrder.targets` to keep all target dependency
ordering in one place.

Adds test coverage to verify the target deploys successfully and the
app can be launched via adb commands.
In cd88d9c, we introduced a new `DeployToDevice` target for new
`dotnet run` support.

However, the following `Condition` doesn't work due to build order:

    <DeployToDeviceDependsOnTargets Condition=" '$(_XASupportsFastDev)' == 'true' ">
      $(_MinimalSignAndroidPackageDependsOn);
      _Upload;
    </DeployToDeviceDependsOnTargets>
    <DeployToDeviceDependsOnTargets Condition=" '$(_XASupportsFastDev)' != 'true' ">
      $(_MinimalSignAndroidPackageDependsOn);
      _DeployApk;
      _DeployAppBundle;
    </DeployToDeviceDependsOnTargets>

* `Microsoft.Android.Sdk.BuildOrder.targets` is imported right before
  `Xamarin.Android.Common.targets` where `$(_XASupportsFastDev)` is set.

To fix this:

* Let's rename `$(_XASupportsFastDev)` to `$(_AndroidFastDeploymentSupported)`
  to just modernize the name.

* Move `$(_AndroidFastDeploymentSupported)` to
  `Microsoft.Android.Sdk.DefaultProperties.targets` that is imported
  early on.

* Update a test to verify the proper targets run in `DeployToDevice`
  target.
Context: dotnet/sdk#52046
Context: https://github.com/dotnet/sdk/blob/c164a9bc1246c48191fb780992530f0fe975141b/documentation/specs/dotnet-run-for-maui.md

When the `DeployToDevice` target is run by the `dotnet run` pipeline,
we will no longer need to make the `ComputeRunArguments` target deploy
anything.

We are thinking this feature can ship in future .NET 11 and 10.0.200
SDKs.
Fixes: #10645

For alignment with other platforms, including `dotnet new console`,
`dotnet run` for Android should:

* Show console output for the launched process
* Exit if the app closes
* Close the app on Ctrl+C

A one-liner like this is *close*:

    adb shell 'am start -S -W -n "PACKAGE/ACTIVITY"; pid=$(pidof PACKAGE); logcat --pid=$pid'

But then Ctrl+C does not close the app, so to wire this all together,
add a simple console app to be invoked by `dotnet run`.

We do not initially intend for this app to be run directly by users,
so it is placed in the `tools` folder of the SDK pack and not a .NET
global tool.

New MSBuild properties:

`$(WaitForExit)`

* Default: (empty)
* When `false`, allow users to disable waiting for the app to exit,
which is useful for our existing tests.

`$(_AndroidRunPath)`

* Default: `$(MSBuildThisFileDirectory)..\tools\Microsoft.Android.Run.dll`
* Allows overriding the path to the `Microsoft.Android.Run` assembly.

`$(_AndroidRunExtraArgs)`
* Default: (empty)
* Allows extra args like `--verbose` passed in
This simplifies my work on the dotnet/sdk repo, as the development
branch is still .NET 10 at release/10.0.2xx (or 3xx).

This tool is simple, so it seems fine to use
`$(DotNetStableTargetFramework)` for now and allow it to roll forward.
@jonathanpeppers jonathanpeppers added the use-merge-commit Normally we squash-and-merge PRs. Use this label so we instead use a merge commit. label Jan 21, 2026
Context: #10656
Partial backport of: 9297282

**Minor Build Task Fix:**

* Simplified the `AdbTarget` property in
  `Xamarin.Android.Common.targets` by removing unnecessary quotation
  marks around the device name.
@jonathanpeppers jonathanpeppers marked this pull request as ready for review January 23, 2026 18:46
@jonathanpeppers jonathanpeppers merged commit 1c410c1 into release/10.0.1xx Jan 23, 2026
2 checks passed
@jonathanpeppers jonathanpeppers deleted the dev/peppers/dotnet-run-backport branch January 23, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

use-merge-commit Normally we squash-and-merge PRs. Use this label so we instead use a merge commit.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants