Skip to content

[TrimmableTypeMap] Add trimmable DotNetRun cases#11486

Merged
jonathanpeppers merged 14 commits into
mainfrom
dev/simonrozsival/11019-trimmable-dotnetrun
Jun 15, 2026
Merged

[TrimmableTypeMap] Add trimmable DotNetRun cases#11486
jonathanpeppers merged 14 commits into
mainfrom
dev/simonrozsival/11019-trimmable-dotnetrun

Conversation

@simonrozsival

@simonrozsival simonrozsival commented May 25, 2026

Copy link
Copy Markdown
Member

Adds trimmable typemap coverage to the DotNetRun device integration test, and fixes two issues in the trimmable typemap targets that were uncovered while bringing those scenarios up.

New test cases

Added to InstallAndRunTests.Get_DotNetRun_Data:

  • Release CoreCLR + trimmable (UseMonoRuntime=false)
  • Debug CoreCLR + trimmable (UseMonoRuntime=false)
  • Release NativeAOT + trimmable

Fixes

dotnet run --no-build for trimmable CoreCLR

The trimmable _GenerateJavaStubs target listed _CleanIntermediateIfNeeded in its DependsOnTargets. On a full build that is redundant — the target already runs early via _AndroidBuildDependsOn in BuildOrder.targets. But during an incremental deploy that does not rebuild (the Install / DeployToDevice chain used by dotnet run without a build), the early Build chain never executes, so this was the only invocation of _CleanIntermediateIfNeeded — and it ran late, after _PrepareAssemblies had already populated obj/<config>/android/assets/<abi>/*.dll. It then deleted obj/<config>/android, and because _PrepareAssemblies had already executed it was skipped on the second pass and did not recreate the assemblies. _GeneratePackageManagerJava then failed with XAGCA7023 (DirectoryNotFoundException on assets/<abi>/Mono.Android.dll).

Removed _CleanIntermediateIfNeeded from the trimmable _GenerateJavaStubs DependsOnTargets so it matches the llvm-ir sibling target, which already omits it. Cleaning still happens early for full builds via BuildOrder.targets.

Parallel multi-RID race in post-trim Java source generation

CoreCLR trimmable builds with multiple RuntimeIdentifiers run per-RID inner builds in parallel. _GeneratePostTrimTrimmableTypeMapJavaSources previously ran inside each inner build and wrote to the same outer intermediate obj/<config>/typemap/linked-java directory, so inner builds raced cleaning and writing the same shared output. CI observed this as XAGTT7024 / "Directory not empty" while one inner build deleted the shared linked-java directory as another was using it.

The trimmed Java sources are RID-independent, so:

  • Moved _GeneratePostTrimTrimmableTypeMapJavaSources to run once in the outer build, after _ResolveAssemblies has collected the per-RID ResolvedFileToPublish items, and before _GenerateJavaStubs / _CompileJava / _CompileToDalvik.
  • Filter @(ResolvedFileToPublish) to a single representative RID (_PostTrimTypeMapFirstRuntimeIdentifier, the first entry in $(RuntimeIdentifiers)), so the target only consumes one RID's linked assemblies as input.
  • Skip the target when running inside an inner per-RID build (_ComputeFilesToPublishForRuntimeIdentifiers != true).

Output remains in the outer intermediate directory where _GenerateJavaStubs expects it.

Fixes #11019
Related to #11604

Add Release and Debug trimmable typemap coverage to the DotNetRun device integration test using CoreCLR.

Fixes #11019

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 25, 2026 14:34

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.

Pull request overview

Adds trimmable typemap coverage to the DotNetRun device integration test matrix, ensuring the trimmable typemap path is exercised under CoreCLR (UseMonoRuntime=false) in both Debug and Release configurations.

Changes:

  • Add Release trimmable DotNetRun test case using AndroidRuntime.CoreCLR.
  • Add Debug trimmable DotNetRun test case using AndroidRuntime.CoreCLR.

@simonrozsival simonrozsival changed the title [tests] Add trimmable DotNetRun cases [TrimmableTypeMap] Add trimmable DotNetRun cases May 25, 2026
@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels May 25, 2026
Add Release NativeAOT coverage for the trimmable typemap DotNetRun device integration test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
Copilot AI and others added 2 commits May 26, 2026 05:00
@simonrozsival

Copy link
Copy Markdown
Member Author

/azp run

@simonrozsival simonrozsival marked this pull request as ready for review June 3, 2026 12:27
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

The dependabot bump (#11562) committed banned-apis.targets with CRLF bytes
stored directly in the git blob. Since .gitattributes declares
`*.targets eol=crlf`, the blob must be stored normalized (LF) and only
converted to CRLF on checkout. The un-normalized blob caused the CI
'Ensure no modified/untracked files' check to fail because git's
renormalization detection flagged the file as modified.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

…-build failure)

The Debug CoreCLR trimmable DotNetRun case fails identically to the
already-ignored Release case: `dotnet run --no-build` re-runs
GenerateNativeApplicationConfigSources, which throws XAGCA7023 because
obj/Debug/android/assets/<rid>/Mono.Android.dll is missing in the no-build
incremental state. Broaden the existing ignore to cover both Debug and
Release CoreCLR trimmable until the underlying no-build issue is fixed.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

The trimmable _GenerateJavaStubs target listed _CleanIntermediateIfNeeded in
its DependsOnTargets. On a full build that is redundant (the target already
runs early via _AndroidBuildDependsOn in BuildOrder.targets). But during an
incremental deploy that does not rebuild (the Install/DeployToDevice chain
used by `dotnet run` without a build), the early Build chain never executes,
so this was the only invocation of _CleanIntermediateIfNeeded -- and it ran
late, *after* _PrepareAssemblies had already populated
obj/<config>/android/assets/<abi>/*.dll. It then deleted obj/<config>/android,
and because _PrepareAssemblies had already executed it was skipped and did not
recreate the assemblies. _GeneratePackageManagerJava then failed with XAGCA7023
(DirectoryNotFoundException on assets/<abi>/Mono.Android.dll).

Remove _CleanIntermediateIfNeeded from the trimmable _GenerateJavaStubs
DependsOnTargets so it matches the llvm-ir sibling target, which already omits
it. Cleaning still happens early for full builds via BuildOrder.targets.

With this fix, the trimmable DotNetRun scenarios (Debug CoreCLR, Release
CoreCLR, NativeAOT) build, deploy, and launch on device, so remove the
Assert.Ignore guard that skipped the CoreCLR trimmable cases.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

GenerateTrimmableTypeMap cleans the post-trim Java source output directory
before copying the Java sources that remain after trimming. On CI this cleanup
can fail transiently with Directory not empty while recursively deleting nested
source directories, which surfaces as XAGTT7024 and fails otherwise valid
CoreCLR trimmable builds.

Use the same retry policy and transient error handling as RemoveDirFixed for
this internal cleanup path, including access denied, sharing violation, and
directory-not-empty retries. The cleanup remains strict: non-transient delete
errors still fail the build.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CoreCLR trimmable builds with multiple RuntimeIdentifiers run per-RID inner
builds in parallel. Those inner builds used the outer intermediate directory for
post-trim Java sources so the packaging phase could compile the shared Java
output, but every RID inner build generated and cleaned the same
obj/<configuration>/typemap/linked-java directory.

That made the post-trim Java source generation race with itself. CI observed
this as XAGTT7024 / Directory not empty while one inner build deleted the shared
linked-java directory as another inner build was also using it. Retrying the
recursive delete masks the symptom but leaves the shared-output race.

Pass the outer RuntimeIdentifiers list into inner builds and let only the first
RID inner build generate the shared post-trim Java sources. The output remains
in the outer intermediate directory where _GenerateJavaStubs expects it, while
parallel inner builds no longer clean or write the same directory.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

The trimmable Java sources are RID-independent, even though the linked assembly
inputs are produced by per-RID inner builds. Generating those Java sources from
inside the parallel RuntimeIdentifier inner builds made multiple inner builds
clean and write the same outer intermediate linked-java directory.

Move post-trim Java source generation to the outer build after _ResolveAssemblies
has collected the per-RID ResolvedFileToPublish items. The target now runs once,
uses a representative RID's linked assemblies as input for the RID-independent
Java sources, and leaves the output in the outer intermediate directory where
_GenerateJavaStubs expects it.

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

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

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

Copy link
Copy Markdown
Member

/review

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

Android PR Reviewer completed successfully!

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Code Review — ✅ LGTM (2 optional 💡 suggestions)

Solid, well-iterated change. Public CI is green, and the test-matrix additions are correct: trimmable is exercised for Release/Debug CoreCLR and Release NativeAOT, with Debug NativeAOT correctly omitted (NativeAOT is Release-only per IgnoreUnsupportedConfiguration).

Severity counts: ❌ 0 · ⚠️ 0 · 💡 2 (documentation only — non-blocking)

Worth flagging for reviewers (not a defect)

Despite the "add DotNetRun cases" framing, this PR also ships production build-logic changes that affect all trimmable CoreCLR builds, not just tests:

  • _GeneratePostTrimTrimmableTypeMapJavaSources moves from AfterTargets="ILLink"AfterTargets="_ResolveAssemblies", keys off @(ResolvedFileToPublish), and is gated on _ComputeFilesToPublishForRuntimeIdentifiers != 'true' so it runs once in the outer build (avoiding the per-RID race fixed in the de245ba/e400d73 commits).
  • New _PostTrimTypeMapFirstRuntimeIdentifier + a first-RID filter to dedup multi-RID publishes.
  • _CleanIntermediateIfNeeded dropped from the _GenerateJavaStubs override. I verified this is safe — it still runs early via $(_AndroidBuildDependsOn), and the override is now consistent with the Microsoft.Android.Sdk.TypeMap.LlvmIr.targets _GenerateJavaStubs, which also omits it.

These look correct and deliberate (they match the #11499 follow-up work), but the PR description doesn't mention them. A line in the description/changelog would help future archaeology.

Validation caveat

The new DotNetRun cases run on the device-integration pipeline, which isn't part of the green public CI checks visible here — so green public CI doesn't by itself prove the new cases pass on-device. The earlier resolved review thread confirms an on-device DotNetRun(True,"trimmable",CoreCLR) failure was the original motivation, so please confirm the on-device run is green before merge.

Nice work 👍

  • Good incremental device coverage for the trimmable typemap.
  • Correct handling of NativeAOT's Release-only constraint.
  • Reuses the existing $([System.String]::Copy(...).Split(';')[0]) idiom already used for _TypeMapFirstAbi in the same file.

Two inline 💡 suggestions below — both are documentation-only and entirely optional.

Generated by Android PR Reviewer for issue #11486 · 1.1K AIC · ⌖ 29.7 AIC · ⊞ 37.6K
Comment /review to run again

Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs

@jonathanpeppers jonathanpeppers left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I updated PR desc with copilot, merging.

@jonathanpeppers jonathanpeppers merged commit 5986690 into main Jun 15, 2026
40 checks passed
@jonathanpeppers jonathanpeppers deleted the dev/simonrozsival/11019-trimmable-dotnetrun branch June 15, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot `copilot-cli` or other AIs were used to author this ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] Add trimmable typemap test cases to DotNetRun device integration test

4 participants