diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs index ae4473d6cd1..e07ec5e9751 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs @@ -139,6 +139,32 @@ string GetNativeTypeNameFromManagedTypeName (string name) } } + string GetFixupKey (Instruction instruction, string designerFullName) + { + string line = instruction.ToString (); + int idx = line.IndexOf (designerFullName, StringComparison.Ordinal); + if (idx >= 0) { + return line.Substring (idx + designerFullName.Length); + } + if (instruction.Operand is FieldReference fieldRef && + (fieldRef.DeclaringType?.ToString()?.Contains (".Resource/") ?? false)) { + var canResolve = false; + try { + var resolved = fieldRef.Resolve (); + canResolve = resolved != null; + } catch (Exception) { + } + if (canResolve) + return null; + var type = fieldRef.DeclaringType.FullName; + var s = type.LastIndexOf ('/'); + type = type.Substring (s + 1); + var key = type + "::" + fieldRef.Name; + return key; + } + return null; + } + protected override void FixBody (MethodBody body, TypeDefinition designer) { // replace @@ -152,10 +178,8 @@ protected override void FixBody (MethodBody body, TypeDefinition designer) { if (i.OpCode != OpCodes.Ldsfld) continue; - string line = i.ToString (); - int idx = line.IndexOf (designerFullName, StringComparison.Ordinal); - if (idx >= 0) { - string key = line.Substring (idx + designerFullName.Length); + var key = GetFixupKey (i, designerFullName); + if (key != null) { LogMessage ($"Looking for {key}."); var found = lookup.TryGetValue (key, out MethodDefinition method); if (!found) { @@ -163,7 +187,7 @@ protected override void FixBody (MethodBody body, TypeDefinition designer) found = lookupCaseInsensitive.TryGetValue (key, out method); } if (found) { - var importedMethod = designer.Module.ImportReference (method); + var importedMethod = body.Method.Module.ImportReference (method); var newIn = Instruction.Create (OpCodes.Call, importedMethod); instructions.Add (i, newIn); } else { diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs index d8c65f1b573..d999506b286 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs @@ -60,6 +60,37 @@ protected bool FindResourceDesigner (AssemblyDefinition assembly, bool mainAppli } } + + if (string.IsNullOrEmpty(designerFullName)) { + LogMessage ($"Inspecting member references for assembly: {assembly.FullName};"); + var memberRefs = assembly.MainModule.GetMemberReferences (); + foreach (var memberRef in memberRefs) { + string declaringType = memberRef.DeclaringType?.ToString () ?? string.Empty; + if (!declaringType.Contains (".Resource/")) { + continue; + } + if (declaringType.Contains ("_Microsoft.Android.Resource.Designer")) { + continue; + } + var resolved = false; + try { + var def = memberRef.Resolve (); + if (resolved = def != null) { + LogMessage ($"Resolved member `{memberRef?.Name}`"); + } + } catch (Exception ex) { + LogMessage ($"Exception resolving member `{memberRef?.Name}`: {ex}"); + resolved = false; + } + if (!resolved) { + LogMessage ($"Adding _Linker.Generated.Resource to {assembly.Name.Name}. Could not resolve {memberRef?.Name} : {declaringType}"); + designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass); + designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass); + return true; + } + } + } + if (string.IsNullOrEmpty(designerFullName)) return false; diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index a6e903cc6dd..b6553ecdef6 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -1115,5 +1115,58 @@ public void EnableAndroidStripILAfterAOT ([Values (false, true)] bool profiledAO Assert.IsTrue(didLaunch, "Activity should have started."); } + [Test] + public void FixLegacyResourceDesignerStep ([Values (true, false)] bool isRelease) + { + string previousTargetFramework = "net7.0-android"; + + var library1 = new XamarinAndroidLibraryProject { + IsRelease = isRelease, + TargetFramework = previousTargetFramework, + ProjectName = "Library1", + AndroidResources = { + new AndroidItem.AndroidResource (() => "Resources\\values\\strings2.xml") { + TextContent = () => @" + + Hi! +", + }, + }, + }; + var library2 = new XamarinAndroidLibraryProject { + IsRelease = isRelease, + TargetFramework = previousTargetFramework, + ProjectName = "Library2", + OtherBuildItems = { + new BuildItem.Source("Foo.cs") { + TextContent = () => "public class Foo { public static int Hello => Library1.Resource.String.hello; } ", + } + } + }; + library2.AndroidResources.Clear (); + library2.SetProperty ("AndroidGenerateResourceDesigner", "false"); // Disable Android Resource Designer generation + library2.AddReference (library1); + proj = new XamarinAndroidApplicationProject { + IsRelease = isRelease, + ProjectName = "MyApp", + }; + proj.AddReference (library2); + proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", "Console.WriteLine(Foo.Hello);"); + + using (var library1Builder = CreateDllBuilder (Path.Combine ("temp", TestName, library1.ProjectName))) + using (var library2Builder = CreateDllBuilder (Path.Combine ("temp", TestName, library2.ProjectName))) { + builder = CreateApkBuilder (Path.Combine ("temp", TestName, proj.ProjectName)); + Assert.IsTrue (library1Builder.Build (library1, doNotCleanupOnUpdate: true), $"Build of {library1.ProjectName} should have succeeded."); + Assert.IsTrue (library2Builder.Build (library2, doNotCleanupOnUpdate: true), $"Build of {library2.ProjectName} should have succeeded."); + Assert.IsTrue (builder.Build (proj), $"Build of {proj.ProjectName} should have succeeded."); + + RunProjectAndAssert (proj, builder); + + WaitForPermissionActivity (Path.Combine (Root, builder.ProjectDirectory, "permission-logcat.log")); + bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", + Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30); + Assert.IsTrue (didLaunch, "Activity should have started."); + } + } } }