Skip to content

Commit 3ec1b15

Browse files
authored
[Xamarin.Android.Build.Tasks] _Microsoft.Android.Resource.Designer namespace (#7681)
Context: dc3ccf2 Context: dotnet/maui#12520 (comment) Context: https://discord.com/channels/732297728826277939/732297837953679412/1062137297438519296 Context: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0002 Consider this C# fragment: namespace Android.Content { public class Intent { } } namespace Microsoft.Maui.Example { public class Helper { public static void UseIntent (Android.Content.Intent intent) {} } } Given a C# *namespace_or_type_name* such as `Android.Content.Intent`, [ECMA 334 §7.8.1][0] will be used to determine what the type refers to, resolving it to the Assembly-Qualified Name `Android.Content.Intent, Mono.Android`. All is good. However, once dc3ccf2 enters the picture, we now have: // via dc3ccf2 & _Microsoft.Android.Resource.Designer.dll namespace Microsoft.Android.Resource.Designer { public class Resource { } } namespace Android.Content { public class Intent { } } namespace Microsoft.Maui.Example { public class Helper { // CS0234 on the following line: public static void UseIntent (Android.Content.Intent intent) {} } } …and it fails to build with [error CS0234][1]: error CS0234: The type or namespace name 'Content' does not exist in the namespace 'Microsoft.Android' (are you missing an assembly reference?) This only happens if the usage of `Android.Content.Intent` happens *within* a `Microsoft.`-prefixed namespace. Which is most of MAUI. The cause of the error is that while attempting to resolve `Android.Content.Intent`, search starts from the *current* namespace `Microsoft.Maui.Example`, and the compiler will attempt to resolve the types: * `Microsoft.Maui.Example.Android.Content.Intent` (because we're in the `Microsoft.Maui.Example` namespace.) * `Microsoft.Maui.Android.Content.Intent` (because `Microsoft.Maui` is a parent namespace) * `Microsoft.Android.Content.Intent` (because `Microsoft` is a parent parent namespace) These are the *only* types that are resolved, and none of those exist. This results in the CS0234 error. There are two workarounds that the "offending" code can employ: 1. Use `global`: namespace Microsoft.Maui.Example { public class Helper { public static void UseIntent (global::Android.Content.Intent intent) {} } } 2. Add a `using Android.Content` and use `Intent` using Android.Content; namespace Microsoft.Maui.Example { public class Helper { public static void UseIntent (Intent intent) {} } } Both of these require changing things *outside* of xamarin-android. Rephrasing & simplifying: commit dc3ccf2 constitutes an *API break*, as code which *previously* compiled *no longer compiles*. Fix this by updating the `<GenerateResourceDesignerAssembly/>` task to emit types into the `_Microsoft.Android.Resource.Designer` namespace; note `_` prefix. No One™ should have their code in a `_Microsoft.*` namespace, so this shouldn't break anybody. Additionally, update `_Microsoft.Android.Resource.Designer.dll` so that [`EditorBrowsableAttribute`][2] is placed on all the types, a'la: namespace _Microsoft.Android.Resource.Designer; [EditorBrowsable (EditorBrowsableState.Never)] public partial class Resource { } // In App project builds [EditorBrowsable (EditorBrowsableState.Never)] internal partial class ResourceConstant { } Finally, update the Source Compatibility types so that the `Resource` type disables the IDE0002 warning: namespace %CompatibilityNamespace% { #pragma warning disable IDE0002 public partial class Resource : _Microsoft.Android.Resource.Designer.Resource { } #pragma warning restore IDE0002 } [0]: https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/basic-concepts.md#78-namespace-and-type-names [1]: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0234 [2]: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.editorbrowsableattribute?view=net-7.0 [3]: https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0002
1 parent ca5022b commit 3ec1b15

File tree

3 files changed

+29
-7
lines changed

3 files changed

+29
-7
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace MonoDroid.Tuner
2020
public class FixLegacyResourceDesignerStep : LinkDesignerBase
2121
{
2222
internal const string DesignerAssemblyName = "_Microsoft.Android.Resource.Designer";
23-
internal const string DesignerAssemblyNamespace = "Microsoft.Android.Resource.Designer";
23+
internal const string DesignerAssemblyNamespace = "_Microsoft.Android.Resource.Designer";
2424

2525
bool designerLoaded = false;
2626
AssemblyDefinition designerAssembly = null;

src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerAssembly.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,25 @@ bool Run(DirectoryAssemblyResolver res)
103103
} else {
104104
// Add the InternalsVisibleToAttribute so the app can access ResourceConstant
105105
if (!string.IsNullOrEmpty (AssemblyName)) {
106-
MethodReference internalsVisibleToAttributeConstructor = ImportCustomAttributeConstructor ("System.Runtime.CompilerServices.InternalsVisibleToAttribute", module, netstandardDef.MainModule);
106+
MethodReference internalsVisibleToAttributeConstructor = ImportCustomAttributeConstructor ("System.Runtime.CompilerServices.InternalsVisibleToAttribute", module, netstandardDef.MainModule, argCount: 1);
107107
var ar = new CustomAttribute (internalsVisibleToAttributeConstructor);
108108
ar.ConstructorArguments.Add (new CustomAttributeArgument (module.TypeSystem.String, AssemblyName));
109109
module.Assembly.CustomAttributes.Add (ar);
110110
}
111111
}
112112

113-
MethodReference targetFrameworkConstructor = ImportCustomAttributeConstructor ("System.Runtime.Versioning.TargetFrameworkAttribute", module, netstandardDef.MainModule);
113+
MethodReference targetFrameworkConstructor = ImportCustomAttributeConstructor ("System.Runtime.Versioning.TargetFrameworkAttribute", module, netstandardDef.MainModule, argCount: 1);
114114

115115
var attr = new CustomAttribute (targetFrameworkConstructor);
116116
attr.ConstructorArguments.Add (new CustomAttributeArgument (module.TypeSystem.String, $".NETStandard,Version=v2.1"));
117117
attr.Properties.Add (new CustomAttributeNamedArgument ("FrameworkDisplayName", new CustomAttributeArgument (module.TypeSystem.String, "")));
118118
module.Assembly.CustomAttributes.Add (attr);
119119

120+
MethodReference editorBrowserConstructor = ImportCustomAttributeConstructor ("System.ComponentModel.EditorBrowsableAttribute", module, netstandardDef.MainModule, argCount: 1);
121+
TypeReference e = ImportType ("System.ComponentModel.EditorBrowsableState", module, netstandardDef.MainModule);
122+
var editorBrowserAttr = new CustomAttribute (editorBrowserConstructor);
123+
editorBrowserAttr.ConstructorArguments.Add (new CustomAttributeArgument (e, System.ComponentModel.EditorBrowsableState.Never));
124+
120125
var att = TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Public | TypeAttributes.BeforeFieldInit;
121126

122127
intArray = new ArrayType (module.TypeSystem.Int32);
@@ -131,6 +136,7 @@ bool Run(DirectoryAssemblyResolver res)
131136
objectRef
132137
);
133138
CreateCtor (resourceDesigner, module);
139+
resourceDesigner.CustomAttributes.Add (editorBrowserAttr);
134140
module.Types.Add (resourceDesigner);
135141
TypeDefinition constDesigner = null;
136142
if (IsApplication) {
@@ -143,6 +149,7 @@ bool Run(DirectoryAssemblyResolver res)
143149
objectRef
144150
);
145151
CreateCtor (constDesigner, module);
152+
constDesigner.CustomAttributes.Add (editorBrowserAttr);
146153
module.Types.Add (constDesigner);
147154
}
148155

@@ -196,11 +203,16 @@ bool Run(DirectoryAssemblyResolver res)
196203
return !Log.HasLoggedErrors;
197204
}
198205

199-
MethodReference ImportCustomAttributeConstructor (string type, ModuleDefinition module, ModuleDefinition sourceModule = null)
206+
MethodReference ImportCustomAttributeConstructor (string type, ModuleDefinition module, ModuleDefinition sourceModule = null, int argCount = 0)
200207
{
201208
var tr = module.ImportReference ((sourceModule ?? module).ExportedTypes.First(x => x.FullName == type).Resolve ());
202209
var tv = tr.Resolve();
203-
return module.ImportReference (tv.Methods.First(x => x.IsConstructor));
210+
return module.ImportReference (tv.Methods.First(x => x.IsConstructor && (x.Parameters?.Count ?? 0) == argCount));
211+
}
212+
213+
TypeReference ImportType (string type, ModuleDefinition module, ModuleDefinition sourceModule = null)
214+
{
215+
return module.ImportReference ((sourceModule ?? module).ExportedTypes.First(x => x.FullName == type).Resolve ());
204216
}
205217

206218
void CreateIntProperty (string resourceClass, string propertyName, int value, TypeDefinition resourceDesigner, ModuleDefinition module,

src/Xamarin.Android.Build.Tasks/Tasks/GenerateResourceDesignerIntermediateClass.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,25 @@ public class GenerateResourceDesignerIntermediateClass : AndroidTask
1515
private const string ResourceDesigner = $"{FixLegacyResourceDesignerStep.DesignerAssemblyNamespace}.Resource";
1616
private const string ResourceDesignerConstants = $"{FixLegacyResourceDesignerStep.DesignerAssemblyNamespace}.ResourceConstant";
1717

18-
private const string CSharpTemplate = @"// This is an Auto Generated file DO NOT EDIT
18+
private const string CSharpTemplate = @"//------------------------------------------------------------------------------
19+
// <auto-generated>
20+
// This code was generated by a tool. DO NOT EDIT
21+
// </auto-generated>
22+
//------------------------------------------------------------------------------
1923
using System;
2024
2125
namespace %NAMESPACE% {
26+
#pragma warning disable IDE0002
2227
public partial class Resource : %BASECLASS% {
2328
}
29+
#pragma warning restore IDE0002
2430
}
2531
";
26-
private const string FSharpTemplate = @"// This is an Auto Generated file DO NOT EDIT
32+
private const string FSharpTemplate = @"//------------------------------------------------------------------------------
33+
// <auto-generated>
34+
// This code was generated by a tool. DO NOT EDIT
35+
// </auto-generated>
36+
//------------------------------------------------------------------------------
2737
namespace %NAMESPACE%
2838
2939
type Resource = %BASECLASS%

0 commit comments

Comments
 (0)