Skip to content

Commit 94451e6

Browse files
authored
[Xamarin.Android.Build.Tasks] case-insensitive Legacy Designer fixup (#8376)
Context: 628d6cb Context: dc3ccf2 We have a very odd situation with the [`Xamarin.CommunityToolkit.MauiCompat` NuGet package][0]: The NuGet package ships with a file `.net/__res_name_case_map.txt` (628d6cb) in `Xamarin.CommunityToolkit.MauiCompat.aar` which contains the case map changes for the `@(AndroidResource)` items. The purpose of this file is to allow the user to use PascalCase (or any case) names in C# code, while the Android Resource compilation is left as all lowercase. This is because Android requires that file-backed resource names consist of only lowercase letters. The contents of this file includes: Resources/Layout/CameraFragment.axml;layout/camerafragment.xml which tells us that the Android `R.layout.camerafragment` resource id should be "bound" as `Resouce.Layout.CameraFragment`, which allows [`CameraFragment.android.cs` to build][1]: public override AView? OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) => inflater.Inflate(Resource.Layout.CameraFragment, null); However, when we look at the IL that comes from the NuGet package we see the only lowercase `camerafragment`: ikdasm ~/.nuget/packages/xamarin.communitytoolkit.mauicompat/2.0.2-preview1013/lib/net6.0-android31.0/Xamarin.CommunityToolkit.MauiCompat.dll| grep Layout::camerafragment … IL_0131: stsfld int32 Xamarin.CommunityToolkit.MauiCompat.Resource/Layout::camerafragment … IL_0001: ldsfld int32 Xamarin.CommunityToolkit.MauiCompat.Resource/Layout::camerafragment For some reason as part of the XamarinCommunityToolkit build [the following script][2] was run on the code: sed -i '' 's/MauiCompat.Resource.Layout.CameraFragment/MauiCompat.Resource.Layout.camerafragment/g' ./src/CommunityToolkit/Xamarin.CommunityToolkit.MauiCompat/**/CameraFragment.android.cs This changes `Resource.Layout.CameraFragment` to `Resource.Layout.camerafragment`, apparently because *something* is renaming [`CameraFragment.axml`][3] to `camerafragment.axml` *after* `__res_name_case_map.txt` is generated, but before `CameraFragment.android.cs` is compiled. The NuGet still includes the `.aar` with the case map changes, which are now inconsistent with the assembly contents. I'm not sure why XamarinCommunityToolkit does all this. The result of the above XamarinCommunityToolkit build and packaging behavior is that it interacts badly with the new net8.0-android Resource Designer Assembly (dc3ccf2) work, with a build-time error: error XA8000: Could not find Android Resource ‘@layout/camerafragment’. Please update @(AndroidResource) to add the missing resource. This is because we are looking for the lowercase version of the resource in the Designer assembly property list, but because of the remap file we only know about the PascalCase version. As a workaround, fallback to a *case-insensitive* resource name lookup if a case-sensitive lookup fails. [0]: https://www.nuget.org/packages/Xamarin.CommunityToolkit.MauiCompat/ [1]: https://github.com/xamarin/XamarinCommunityToolkit/blob/5a6062f3c3543acda3c36ca4683cd8fc7fe86ba7/src/CommunityToolkit/Xamarin.CommunityToolkit/Views/CameraView/Android/CameraFragment.android.cs#L129 [2]: https://github.com/xamarin/XamarinCommunityToolkit/blob/5a6062f3c3543acda3c36ca4683cd8fc7fe86ba7/MauiCompat.sh#L561 [3]: https://github.com/xamarin/XamarinCommunityToolkit/blob/5a6062f3c3543acda3c36ca4683cd8fc7fe86ba7/src/CommunityToolkit/Xamarin.CommunityToolkit/Resources/Layout/CameraFragment.axml
1 parent a423246 commit 94451e6

File tree

1 file changed

+12
-3
lines changed

1 file changed

+12
-3
lines changed

src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class FixLegacyResourceDesignerStep : LinkDesignerBase
3030
AssemblyDefinition designerAssembly = null;
3131
TypeDefinition designerType = null;
3232
Dictionary<string, MethodDefinition> lookup;
33+
Dictionary<string, MethodDefinition> lookupCaseInsensitive;
3334

3435
protected override void EndProcess ()
3536
{
@@ -61,7 +62,7 @@ protected override void LoadDesigner ()
6162
LogMessage ($" Did not find {DesignerAssemblyNamespace}.Resource type. It was probably linked out.");
6263
return;
6364
}
64-
lookup = BuildResourceDesignerPropertyLookup (designerType);
65+
lookup = BuildResourceDesignerPropertyLookup (designerType, out lookupCaseInsensitive);
6566
} finally {
6667
designerLoaded = true;
6768
}
@@ -105,10 +106,11 @@ internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
105106
return true;
106107
}
107108

108-
Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDefinition type)
109+
Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDefinition type, out Dictionary<string, MethodDefinition> caseInsensitiveLookup)
109110
{
110111
LogMessage ($" Building Designer Lookups for {type.FullName}");
111112
var output = new Dictionary<string, MethodDefinition> (StringComparer.Ordinal);
113+
caseInsensitiveLookup = new Dictionary<string, MethodDefinition> (StringComparer.OrdinalIgnoreCase);
112114
foreach (TypeDefinition definition in type.NestedTypes)
113115
{
114116
foreach (PropertyDefinition property in definition.Properties)
@@ -117,7 +119,9 @@ Dictionary<string, MethodDefinition> BuildResourceDesignerPropertyLookup (TypeDe
117119
if (output.ContainsKey (key)) {
118120
LogMessage ($" Found duplicate {key}");
119121
} else {
122+
LogMessage ($" Adding {key}");
120123
output.Add (key, property.GetMethod);
124+
caseInsensitiveLookup [key] = property.GetMethod;
121125
}
122126
}
123127
}
@@ -153,7 +157,12 @@ protected override void FixBody (MethodBody body, TypeDefinition designer)
153157
if (idx >= 0) {
154158
string key = line.Substring (idx + designerFullName.Length);
155159
LogMessage ($"Looking for {key}.");
156-
if (lookup.TryGetValue (key, out MethodDefinition method)) {
160+
var found = lookup.TryGetValue (key, out MethodDefinition method);
161+
if (!found) {
162+
LogMessage ($"DEBUG! Failed to find {key}! Trying case insensitive lookup.");
163+
found = lookupCaseInsensitive.TryGetValue (key, out method);
164+
}
165+
if (found) {
157166
var importedMethod = designer.Module.ImportReference (method);
158167
var newIn = Instruction.Create (OpCodes.Call, importedMethod);
159168
instructions.Add (i, newIn);

0 commit comments

Comments
 (0)