Skip to content

[Microsoft.Android.Build.Tasks] Move FastDeploy task - #13458

Open
simonrozsival wants to merge 5 commits into
mainfrom
simonrozsival-migrate-fastdeploy
Open

simonrozsival wants to merge 5 commits into
mainfrom
simonrozsival-migrate-fastdeploy

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Move the FastDeploy task (all partials plus FastDeployJsonSerializerContext) from Xamarin.Android.Build.Debugging.Tasks.dll (netstandard2.0) to Microsoft.Android.Build.Tasks.dll (net12.0), in the Microsoft.Android.Tasks namespace like the other tasks there. The task stays an AsyncTask, and its deployment behavior is unchanged. <FastDeploy/> keeps resolving; the fully qualified name is now Microsoft.Android.Tasks.FastDeploy (previously Xamarin.Android.Tasks.FastDeploy).

  • Registration follows the existing MABT pattern: TaskHostFactory + Runtime="NET" on .NET Framework MSBuild, and Runtime="NET" on .NET MSBuild.
  • Dependencies: AndroidHelper.cs (still used by GetPrimaryCpuAbi/DetectIfAppWasUninstalled) is compile-linked into MABT. The debugging Resources*.resx are linked with their original manifest name, so XA#### messages stay localized; the satellites ship through the existing tools/net/<lang>/Microsoft.Android.Build.Tasks.resources.dll entries. No new non-framework assemblies.
  • System.Text.Json: the manifest now uses the framework STJ. FastDeploy was the only STJ consumer in DebuggingTasks, so that PackageReference is removed.
  • CA1305: MABT enforces it (DebuggingTasks suppressed it), so the implicit current-culture formatting is now explicit. Output is identical.
  • Device cache: AndroidHelper cached devices with RegisterTaskObjectAssemblyLocal as AdbDeviceInfo instances. The key includes the BaseTasks path (tools/ vs tools/net/), so FastDeploy would have missed the device GetPrimaryCpuAbi already selected and run an extra adb devices -l. The cache now stores every AdbDeviceInfo field as framework types under a non-assembly-local key, so both copies of AndroidHelper share it as before. On .NET Framework MSBuild, FastDeploy runs in a TaskHostFactory process and cannot share this in-process cache.
  • Tests: the FastDeploy unit tests and FastDeployTypeMapTests move to Microsoft.Android.Build.Tasks.Tests. AppDetectionTests imports the debugging targets on their own, so its fixture now sets _MicrosoftAndroidBuildTasksAssembly next to _XamarinAndroidBuildTasksAssembly.

Not in scope: [MSBuildMultiThreadableTask] support. Strict -mt builds run FastDeploy in a sidecar task host, as with other non-MT tasks.

Testing (macOS arm64, local SDK)

  • dotnet test src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/Microsoft.Android.Build.Tasks.Tests.csproj --filter "FullyQualifiedName~FastDeployTests|FullyQualifiedName~FastDeployTypeMapTests": 65/65 passed. These are the 63 cases that previously ran in Xamarin.Android.Build.Tests plus 2 new device-cache tests, which fail without the cache change.
  • bin/Debug/dotnet/dotnet build Microsoft.Android.slnx plus -t:BuildExtraApiLevels: 0 errors. Packaged tools/net/Microsoft.Android.Build.Tasks.dll contains FastDeploy and the debugging resources; Xamarin.Android.Build.Debugging.Tasks.dll no longer contains FastDeploy.
  • Packaged-SDK smoke against a fake adb, invoking both <FastDeploy/> and <Microsoft.Android.Tasks.FastDeploy/> through the shipped Xamarin.Android.Common.Debugging.targets:
    • ./dotnet-local.sh msbuild … -m -nr:false: succeeded with 0 warnings/0 errors. The fake adb's parent was the main MSBuild process, so FastDeploy ran in-process.
    • … -m -mt -nr:false -warnaserror:MSB4286,MSB4287,MSB4288: succeeded with 0 warnings/0 errors, running in a task-host node.
  • ./dotnet-local.sh test bin/TestDebug/net11.0/Xamarin.Android.Build.Tests.dll --filter "FullyQualifiedName~AppDetectionTests|FullyQualifiedName~DebuggingTasksTests|Name~AndroidEnableFastDeployment|Name~FastDeploymentDoesNotAddContentProvider": 53 passed, 1 skipped. That includes all 9 AppDetectionTests (DeviceSelectionSurvivesDeviceOrderChanges failed with MSB4022 in the previous CI run) and the 9 FastDeploy build/packaging tests, which are host-only and do not run FastDeploy.
  • Cross-task fixture (GetPrimaryCpuAbi → FastDeploy) against a counting fake adb, 10 interleaved warm pairs comparing main with this PR. Per deploy, both make 1 adb devices -l call and 7 adb calls total, with 1 cache hit; every paired adb command log is identical.

Not validated locally: .NET Framework (MSBuildRuntimeType == 'Full') hosting, Windows, and on-device deployment (MSBuildDeviceIntegration). CI covers these.

@simonrozsival
simonrozsival marked this pull request as ready for review November 10, 2026 15:26
Copilot AI balanced review requested due to automatic review settings November 10, 2026 15:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new task registration requires an assembly path missing from the AppDetectionTests fixture, causing MSB4022 during project evaluation and breaking all three cases before their targets run.

3 open issues
What changed in this PR

Moves FastDeploy into the modern .NET task assembly, continuing the task migrations in #13382 and #13386 and removing the debugging assembly’s dependency on the System.Text.Json package.

Changes:

  • Relocates FastDeploy and its unit tests to the modern task projects.
  • Links shared debugging helpers and localized resources, and updates Full/Core MSBuild registrations.
  • Preserves deployment parameters and updates the task documentation link.
File Description
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​DebuggingTasksTests.cs Removes FastDeploy tests relocated to the modern suite.
src/​Xamarin.Android.Build.Debugging.Tasks/​Xamarin.Android.Common.Debugging.targets Registers the modern task for Full and Core MSBuild.
src/​Xamarin.Android.Build.Debugging.Tasks/​Xamarin.Android.Build.Debugging.Tasks.csproj Removes the System.Text.Json package reference.
src/​Xamarin.Android.Build.Debugging.Tasks/​Utilities/​AndroidHelper.cs Supports linked compilation and explicit diagnostic formatting culture.
src/​Microsoft.Android.Build.Tasks/​Tests/​Microsoft.Android.Build.Tasks.Tests/​FastDeployTypeMapTests.cs Updates the moved tests’ task namespace.
src/​Microsoft.Android.Build.Tasks/​Tests/​Microsoft.Android.Build.Tasks.Tests/​FastDeployTests.FakeAdb.cs Moves fake-ADB tests into the FastDeploy fixture.
src/​Microsoft.Android.Build.Tasks/​Tests/​Microsoft.Android.Build.Tasks.Tests/​FastDeployTests.cs Houses migrated deployment tests and helpers.
src/​Microsoft.Android.Build.Tasks/​Tasks/​FastDeployJsonSerializerContext.cs Moves JSON source generation into the modern assembly.
src/​Microsoft.Android.Build.Tasks/​Tasks/​FastDeploy.WarmState.cs Relocates warm-state handling.
src/​Microsoft.Android.Build.Tasks/​Tasks/​FastDeploy.Manifest.cs Relocates manifest handling and uses invariant tick formatting.
src/​Microsoft.Android.Build.Tasks/​Tasks/​FastDeploy.cs Moves the task entry point and uses invariant header formatting.
src/​Microsoft.Android.Build.Tasks/​Tasks/​FastDeploy.Adb.cs Relocates ADB command handling.
src/​Microsoft.Android.Build.Tasks/​Microsoft.Android.Build.Tasks.csproj Links the shared Android helper and debugging resources.
Documentation/​guides/​FastDeploy.md Points to the task’s new source location.

Review effort: Balanced

<UsingTask TaskName="Xamarin.Android.Tasks.GetPrimaryCpuAbi" AssemblyFile="Xamarin.Android.Build.Debugging.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.FastDeploy" AssemblyFile="Xamarin.Android.Build.Debugging.Tasks.dll" />
<UsingTask TaskName="Microsoft.Android.Tasks.FastDeploy" AssemblyFile="$(_MicrosoftAndroidBuildTasksAssembly)" Runtime="NET" TaskFactory="TaskHostFactory" Condition=" '$(MSBuildRuntimeType)' == 'Full' " />
<UsingTask TaskName="Microsoft.Android.Tasks.FastDeploy" AssemblyFile="$(_MicrosoftAndroidBuildTasksAssembly)" Runtime="NET" Condition=" '$(MSBuildRuntimeType)' == 'Core' " />
Comment on lines +92 to +94
<EmbeddedResource Include="..\Xamarin.Android.Build.Debugging.Tasks\Properties\Resources*.resx" Link="Properties\Debugging%(Filename)%(Extension)">
<ManifestResourceName>Xamarin.Android.Build.Debugging.Tasks.Properties.%(Filename)</ManifestResourceName>
</EmbeddedResource>
<UsingTask TaskName="Xamarin.Android.Tasks.GetAndroidActivityName" AssemblyFile="Xamarin.Android.Build.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.GetPrimaryCpuAbi" AssemblyFile="Xamarin.Android.Build.Debugging.Tasks.dll" />
<UsingTask TaskName="Xamarin.Android.Tasks.FastDeploy" AssemblyFile="Xamarin.Android.Build.Debugging.Tasks.dll" />
<UsingTask TaskName="Microsoft.Android.Tasks.FastDeploy" AssemblyFile="$(_MicrosoftAndroidBuildTasksAssembly)" Runtime="NET" TaskFactory="TaskHostFactory" Condition=" '$(MSBuildRuntimeType)' == 'Full' " />
Move the `FastDeploy` task (all partial declarations and its
`FastDeployJsonSerializerContext`) from
`Xamarin.Android.Build.Debugging.Tasks.dll` (netstandard2.0) to
`Microsoft.Android.Build.Tasks.dll` (net12.0).  The task keeps its
`Xamarin.Android.Tasks` namespace, so both `<FastDeploy/>` and
`<Xamarin.Android.Tasks.FastDeploy/>` continue to resolve.

  * Register the task from `$(_MicrosoftAndroidBuildTasksAssembly)`
    using the existing Full/Core pattern: `TaskHostFactory` +
    `Runtime="NET"` on .NET Framework MSBuild, `Runtime="NET"` alone on
    .NET MSBuild.
  * Compile-link `AndroidHelper.cs` (still used by `GetPrimaryCpuAbi` and
    `DetectIfAppWasUninstalled`) into MABT as `internal`, and link the
    debugging `Resources.resx` files with their original manifest name so
    XA#### messages stay localized.
  * The manifest now uses the framework System.Text.Json; the
    `System.Text.Json` PackageReference in DebuggingTasks had no other
    consumer and is removed.
  * Make the implicit current-culture formatting explicit to satisfy
    CA1305, which MABT enforces; behavior is unchanged.
  * Move the FastDeploy unit tests (and `FastDeployTypeMapTests`) to
    `Microsoft.Android.Build.Tasks.Tests`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival force-pushed the simonrozsival-migrate-fastdeploy branch from a241ce3 to 72786e0 Compare November 10, 2026 22:57
simonrozsival and others added 4 commits November 11, 2026 00:16
`AndroidHelper` caches the device selected by `GetPrimaryCpuAbi` with
`RegisterTaskObjectAssemblyLocal`, whose key includes the
Microsoft.Android.Build.BaseTasks path. FastDeploy now loads from
`tools/net/` instead of `tools/`, so it missed that cache and ran an
additional `adb devices -l` on every deploy.

Also publish the selected serial (`""` for no device) under a
framework-typed, non-assembly-local key, and read it only from the
Microsoft.Android.Build.Tasks copy. FastDeploy only uses
`AdbDeviceInfo.Serial`.

`Microsoft.Android.Build.Tasks.csproj` did not define
`MICROSOFT_ANDROID_BUILD_TASKS`, so the linked `AndroidHelper` was
compiled as `public` there. Define it so the copy is `internal`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the assembly-local device cache plus serial side-channel with a
single cache whose values are framework types, so the copies of
`AndroidHelper` in Xamarin.Android.Build.Debugging.Tasks.dll and
Microsoft.Android.Build.Tasks.dll share every `AdbDeviceInfo` field
(an empty array means no device was found), as they did when both tasks
lived in one assembly.

Keep `AndroidHelper` public in both copies and drop the
`MICROSOFT_ANDROID_BUILD_TASKS` define.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move `FastDeploy` and `FastDeployJsonSerializerContext` into
`Microsoft.Android.Tasks`, like the other tasks in
Microsoft.Android.Build.Tasks.dll, and register the task as
`Microsoft.Android.Tasks.FastDeploy`. `<FastDeploy/>` keeps resolving;
the old qualified `<Xamarin.Android.Tasks.FastDeploy/>` name does not.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
`AppDetectionTests` imports `Xamarin.Android.Common.Debugging.targets`
without `Microsoft.Android.Sdk.targets`, so FastDeploy's `<UsingTask/>`
evaluated `$(_MicrosoftAndroidBuildTasksAssembly)` to an empty string
(MSB4022) and `DeviceSelectionSurvivesDeviceOrderChanges` failed before
the detector ran. Set the property alongside
`_XamarinAndroidBuildTasksAssembly`.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #13458

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code assessment: no blocking findings — 0 ❌ errors, 0 ⚠️ warnings, 1 💡 non-blocking inline suggestion.

Reviewed all 16 changed files in full, the task callers, project references, resource naming/packaging, and the shared cache's producer/consumer paths. The task relocation preserves the deployment logic, the registrations match the existing modern-task hosting pattern, and the primitive cache representation avoids sharing assembly-specific AdbDeviceInfo identities. The existing FastDeploy tests move with the implementation, and the standalone-target fixture receives the new assembly property.

CI: GitHub reports all 40 head-commit checks successful, including the aggregate dotnet-android check for Azure build 1674826. Independent Azure build/timeline verification is incomplete: az devops invoke refused the read because authentication is required. No local build or tests were run in this review; the available dotnet reports SDK 10.0.403, while the moved task/test project targets net12.0. No confirmed regression was found by source inspection.

Generated by Android PR Reviewer for #13458 · copilot · gpt61 · 51.7 AIC · ⌖ 8.01 AIC · ⊞ 19.5K
Comment /review to run again

{
string adbToolPath = Path.Combine (Path.GetTempPath (), "missing-" + Guid.NewGuid ().ToString ("N"));
IBuildEngine4 engine = new MockBuildEngine (TestContext.Out);
engine.RegisterTaskObject (DeviceCacheKey ("-s emulator-5554", Path.Combine (adbToolPath, "adb")), new object [] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 💡 Testing — Consider checking in the cross-task counting-fake-adb fixture described in the PR: let the actual GetPrimaryCpuAbi task populate the cache, then run FastDeploy through the shipped targets and assert that only one adb devices -l invocation occurred. This test hand-registers the expected payload and calls only the modern assembly's AndroidHelper, so it verifies cache consumption but cannot catch producer-side key/payload drift or assembly-loading differences between the two task assemblies. The manual smoke test is good evidence for this revision; preserving it as a regression test would protect the main reason for the shared-cache change.

{Rule: Test cross-assembly behavior at the consuming boundary}

@simonrozsival simonrozsival added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Nov 11, 2026

This branch has not been deployed

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

Labels

ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants