Skip to content

[release/10.0.1xx] [xabt] Fix wrong-TFM assembly loading -> empty .jlo.xml files#11248

Draft
jonathanpeppers wants to merge 6 commits intorelease/10.0.1xxfrom
jonathanpeppers/backport-to-10-0-1xx
Draft

[release/10.0.1xx] [xabt] Fix wrong-TFM assembly loading -> empty .jlo.xml files#11248
jonathanpeppers wants to merge 6 commits intorelease/10.0.1xxfrom
jonathanpeppers/backport-to-10-0-1xx

Conversation

@jonathanpeppers
Copy link
Copy Markdown
Member

Backport of #11208 (7bc3bbb) to release/10.0.1xx.

Fixes: #10858

The cherry-pick had a minor conflict in the test file's using statements (the release branch was missing using Xamarin.Android.Tasks;). Comments referencing net11.0 were updated to net10.0 for this branch.


Original PR description:

The fix pre-loads all ResolvedAssemblies into the resolver cache from their exact ItemSpec paths during resolver setup. This ensures that both GetAssembly (which checks the cache first) and Cecil's lazy reference resolution always find the correct TFM version.

Fixes: #10858

In .NET 9, `GenerateJavaStubs` scanned for Java types using
`XAJavaTypeScanner.GetJavaTypes`, which called
`resolver.Load(asmItem.ItemSpec)` to load each assembly from its
exact file path. This ensured the correct TFM version was always used.

In ea399ed (#9893), JCW scanning was moved into
`AssemblyModifierPipeline` as `FindJavaObjectsStep`. The pipeline's
`RunPipeline` method uses `resolver.GetAssembly(source.ItemSpec)`,
which strips the path and resolves by assembly **name** through search
directories. When a multi-TFM library (e.g. `net11.0;net11.0-android`)
is transitively referenced through a `net11.0`-only intermediary, the
intermediary's output directory contains a CopyLocal'd `net11.0` copy
of the assembly. If that directory appears in the resolver's search path
before the correct `net11.0-android` directory, `GetAssembly` loads
the wrong version — one without any Android types — producing empty
`.jlo.xml` files with 0 JCW types.

The fix pre-loads all `ResolvedAssemblies` into the resolver cache
from their exact `ItemSpec` paths during resolver setup. This ensures
that both `GetAssembly` (which checks the cache first) and Cecil's
lazy reference resolution always find the correct TFM version.

A regression test `MultiTfmTransitiveReference` reproduces the exact
scenario: App -> CoreLib (net11.0) -> MultiTfmLib (net11.0;
net11.0-android), where MultiTfmLib has an Android `BroadcastReceiver`
behind `#if ANDROID`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 29, 2026 14:48
@jonathanpeppers jonathanpeppers changed the base branch from main to release/10.0.1xx April 29, 2026 14:48
@jonathanpeppers

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

Copy link
Copy Markdown
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

Backport to release/10.0.1xx addressing wrong-TFM assembly resolution in AssemblyModifierPipeline, which could cause JCW scanning to load a non-Android assembly variant and generate empty .jlo.xml outputs (issue #10858).

Changes:

  • Pre-load all ResolvedAssemblies into DirectoryAssemblyResolver’s cache using their exact paths during pipeline setup, so GetAssembly/Cecil lazy resolution consistently finds the correct TFM assembly.
  • Add a regression test modeling the transitive multi-TFM reference scenario that previously produced empty .jlo.xml files.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs Pre-load resolved assemblies into the resolver cache to prevent wrong-TFM resolution during pipeline execution.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildWithLibraryTests.cs Adds a regression test for the multi-TFM transitive reference scenario and updates using directives.

…ce test

Use default Mono runtime instead of parameterizing across all runtimes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

…amework

LatestDotNetTargetFramework does not exist on the release/10.0.1xx branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers jonathanpeppers marked this pull request as draft April 29, 2026 18:43
The pre-loading approach caused FileNotFoundException for netstandard.dll
on the 10.0 branch, where some dependencies (e.g. AppCompat) still target
netstandard2.1. Pre-loading ALL assemblies triggered Cecil lazy reference
resolution for assemblies that reference netstandard.dll.

Instead, use resolver.Load(source.ItemSpec) in RunPipeline to load each
assembly from its exact path. This ensures the correct TFM version is
loaded without eagerly loading all assemblies upfront.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Wrong-TFM assembly loading, producing empty .jlo.xml files (0 JCW types)

2 participants