Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,19 @@ public override bool RunTask ()

var resolver = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: ReadSymbols, loadReaderParameters: readerParameters);

// Add SearchDirectories for the current architecture's ResolvedAssemblies
// Add SearchDirectories and pre-load ResolvedAssemblies into the resolver cache.
// Pre-loading ensures the correct TFM version is cached before any Cecil lazy
// reference resolution can find wrong-TFM copies from search directories (e.g.,
// a net11.0 copy in a referencing project's output directory).
foreach (var kvp in perArchAssemblies [sourceArch]) {
ITaskItem assembly = kvp.Value;
var path = Path.GetFullPath (Path.GetDirectoryName (assembly.ItemSpec));
if (!resolver.SearchDirectories.Contains (path)) {
resolver.SearchDirectories.Add (path);
}
if (resolver.Load (assembly.ItemSpec) == null) {
Log.LogDebugMessage ($"Could not pre-load assembly '{assembly.ItemSpec}' into resolver cache.");
}
Comment on lines +115 to +117
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This might regress build performance, but I'm retrying:

Image

}

// Set up the FixAbstractMethodsStep and AddKeepAlivesStep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using NUnit.Framework;
using Xamarin.ProjectTools;
using Xamarin.Android.Tasks;
using Xamarin.Android.Tools;

namespace Xamarin.Android.Build.Tests
{
Expand Down Expand Up @@ -1074,5 +1075,130 @@ public void DotNetLibraryAarChanges ([Values] AndroidRuntime runtime)
}
}

/// <summary>
/// Regression test for https://github.com/dotnet/android/issues/10858
///
/// When a solution has the structure:
/// App (net11.0-android) → CoreLib (net11.0) → MultiTfmLib (net11.0;net11.0-android)
///
/// And MultiTfmLib contains Java-interop types (e.g. BroadcastReceiver) that only
/// exist in the net11.0-android TFM, the build tasks must load the Android-TFM assembly
/// and generate JCWs for those types. Previously, the net11.0 (non-Android) assembly
/// could be loaded instead, causing FindJavaObjectsStep to report "Found 0 Java types"
/// and producing empty .jlo.xml files.
/// </summary>
[Test]
public void MultiTfmTransitiveReference ([Values] AndroidRuntime runtime)
{
if (IgnoreUnsupportedConfiguration (runtime, release: false)) {
return;
}

var path = Path.Combine ("temp", TestName);
var dotnetVersion = XABuildConfig.LatestDotNetTargetFramework;

// 1. Multi-TFM library (net11.0 + net11.0-android) with a BroadcastReceiver
var multiTfmLib = new DotNetStandard {
ProjectName = "MultiTfmLib",
Sdk = "Microsoft.NET.Sdk",
Sources = {
new BuildItem.Source ("MyReceiver.cs") {
TextContent = () =>
@"#if ANDROID
using Android.Content;

namespace MultiTfmLib
{
public class MyReceiver : BroadcastReceiver
{
public override void OnReceive (Context? context, Intent? intent) { }
}
}
#endif
"
},
new BuildItem.Source ("SharedClass.cs") {
TextContent = () =>
@"namespace MultiTfmLib
{
public class SharedClass
{
public string Name => ""Hello"";
}
}
"
},
},
};
multiTfmLib.TargetFrameworks = $"{dotnetVersion};{dotnetVersion}-android";
multiTfmLib.SetProperty ("Nullable", "enable");
multiTfmLib.SetProperty ("AndroidGenerateResourceDesigner", "false");
multiTfmLib.SetProperty ("SupportedOSPlatformVersion", "24",
"$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'");
if (runtime != AndroidRuntime.NativeAOT) {
multiTfmLib.SetRuntime (runtime);
}

// 2. Non-Android library (net11.0 only) referencing the multi-TFM library
var coreLib = new DotNetStandard {
ProjectName = "CoreLib",
Sdk = "Microsoft.NET.Sdk",
Sources = {
new BuildItem.Source ("CoreClass.cs") {
TextContent = () =>
@"namespace CoreLib
{
public class CoreClass
{
public MultiTfmLib.SharedClass Shared => new ();
}
}
"
},
},
};
coreLib.TargetFramework = dotnetVersion;
coreLib.AddReference (multiTfmLib);

// 3. Android app that references CoreLib (NOT MultiTfmLib directly)
var app = new XamarinAndroidApplicationProject {
ProjectName = "MyApp",
Sources = {
new BuildItem.Source ("Usage.cs") {
TextContent = () =>
@"public class Usage
{
public CoreLib.CoreClass Core => new ();
}
"
},
},
};
app.SetRuntime (runtime);
app.AddReference (coreLib);

using var multiTfmBuilder = CreateDllBuilder (Path.Combine (path, multiTfmLib.ProjectName));
Assert.IsTrue (multiTfmBuilder.Build (multiTfmLib), $"{multiTfmLib.ProjectName} should build");

using var coreBuilder = CreateDllBuilder (Path.Combine (path, coreLib.ProjectName));
Assert.IsTrue (coreBuilder.Build (coreLib), $"{coreLib.ProjectName} should build");

using var appBuilder = CreateApkBuilder (Path.Combine (path, app.ProjectName));
Assert.IsTrue (appBuilder.Build (app), $"{app.ProjectName} should build");

// Verify: MultiTfmLib.jlo.xml should NOT be empty (i.e. the assembly was scanned as an Android assembly)
var jloXml = appBuilder.Output.GetIntermediaryPath (
Path.Combine ("android", "assets", "arm64-v8a", "MultiTfmLib.jlo.xml"));
FileAssert.Exists (jloXml);
Comment thread
jonathanpeppers marked this conversation as resolved.

var jloXmlInfo = new FileInfo (jloXml);
Assert.IsTrue (jloXmlInfo.Length > 0,
"MultiTfmLib.jlo.xml should not be empty — the Android-TFM assembly was not loaded (wrong TFM loaded instead)");

var jloContent = File.ReadAllText (jloXml);
Assert.IsTrue (jloContent.Contains ("MyReceiver"),
$"MultiTfmLib.jlo.xml should contain the MyReceiver JCW type, but got: {jloContent}");
}

}
}