Skip to content

Commit

Permalink
.net8 Android not building because Resources from resx not found.
Browse files Browse the repository at this point in the history
Fixes https://github.com/dotnet/maui/issues/19117

With the release of the new Resource Designer assembly we purposely
removed the existing legacy `Resource.designer.cs` file from the
`@(Compile)` ItemGroup. This is so that we did not get any clashes
with the new system.

Unfortunately the name `Resource.designer.cs` is also used by
developers as the name for the generated class when using `.resx` files.
As a result we got the above bug report because the class was disappearing.

```
error CS0234: The type or namespace name 'Resources' does not exist in the namespace 'StringResourcesBug' (are you missing an assembly reference?)
```

It turns out that the `_RemoveLegacyDesigner` was being a bit too aggressive
with its removal of files. Because it was matching on filename only it was
removing files which had nothing to do with Android Resources. This would
cause the above error.

The fix in this case is to check for additional metadata. In the case of
`.resx` file designers they typically have a `DependentUpon` metadata to
signal that they are generated from the `.resx`. So we should check this
metadata to make sure it is NOT set before removing a file.

A new unit test was added to test this fix and it worked. But it did show
up one other issue. In certain cases we would end up with two `Resource`
classes in the same namespace. This would then cause the following
build error.

```
error CS0260: Missing partial modifier on declaration of type 'Resource'; another partial declaration of this type exists
```

This is because the two classes had different modifiers. We could also get
an error if the access modifiers are different (public vs internal).
So lets add a new MSBuild property `AndroidResourceDesignerClassModifier`.
This property defaults to `public`, but can be overridden by the user to
control the access modifier of the generated class.
  • Loading branch information
dellis1972 committed Oct 8, 2024
1 parent 4429c52 commit b22c3a5
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Documentation/docs-mobile/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@
href: messages/xa1036.md
- name: XA1037
href: messages/xa1037.md
- name: XA1038
href: messages/xa1038.md
- name: "XA2xxx: Linker"
items:
- name: "XA2xxx: Linker"
Expand Down
9 changes: 9 additions & 0 deletions Documentation/docs-mobile/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,15 @@ Specifies the name of the Resource
file to generate. The default template sets this to
`Resource.designer.cs`.

## AndroidResourceDesignerClassModifier

Specifies the class modifier for the intermediate `Resource` class which is
generated. Valid values are `public` and `internal`.

By default this will be `public`.

Added in .NET 9.

## AndroidSdkBuildToolsVersion

The Android SDK
Expand Down
1 change: 1 addition & 0 deletions Documentation/docs-mobile/messages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ or 'Help->Report a Problem' in Visual Studio for Mac.
+ [XA1036](xa1036.md): AndroidManifest.xml //uses-sdk/@android:minSdkVersion '29' does not match the $(SupportedOSPlatformVersion) value '21' in the project file (if there is no $(SupportedOSPlatformVersion) value in the project file, then a default value has been assumed).
Either change the value in the AndroidManifest.xml to match the $(SupportedOSPlatformVersion) value, or remove the value in the AndroidManifest.xml (and add a $(SupportedOSPlatformVersion) value to the project file if it doesn't already exist).
+ [XA1037](xa1037.md): The '{0}' MSBuild property is deprecated and will be removed in .NET {1}. See https://aka.ms/net-android-deprecations for more details.
+ [XA1038](xa1038.md): The '{0}' MSBuild property has an invalid value. Value values are {1}.

## XA2xxx: Linker

Expand Down
21 changes: 21 additions & 0 deletions Documentation/docs-mobile/messages/xa1038.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: .NET for Android error XA1038
description: XA1038 error code
ms.date: 06/27/2024
---
# .NET for Android error XA1038

## Example messages

```
The 'AndroidResourceDesignerClassModifier' MSBuild property has an invalid value of 'foo'. A valid value is one of: 'public', 'internal'.
```

## Solution

Edit your csproj directly and remove the referenced MSBuild property.

Test your project to ensure the new behavior is functionally equivalent.

If not, file an [issue](https://github.com/xamarin/xamarin-android/issues) so a
solution can be found before the deprecated flag is removed.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Copyright (C) 2016 Xamarin. All rights reserved.
<_GenerateResourceDesignerClassFile Condition=" '$(Language)' == 'F#' ">$(_DesignerIntermediateOutputPath)_$(_DesignerAssemblyName).fs</_GenerateResourceDesignerClassFile>
<_GenerateResourceDesignerClassFile Condition=" '$(_GenerateResourceDesignerClassFile)' == '' ">$(_DesignerIntermediateOutputPath)_$(_DesignerAssemblyName).cs</_GenerateResourceDesignerClassFile>
<_GenerateResourceCaseMapFile>$(_DesignerIntermediateOutputPath)case_map.txt</_GenerateResourceCaseMapFile>
<AndroidResourceDesignerClassModifier Condition=" '$(AndroidResourceDesignerClassModifier)' == '' ">public</AndroidResourceDesignerClassModifier>
</PropertyGroup>
<Message Text="_OuterIntermediateOutputPath: $(_OuterIntermediateOutputPath)" />
<Message Text="IntermediateOutputPath: $(IntermediateOutputPath)" />
Expand Down Expand Up @@ -100,9 +101,15 @@ Copyright (C) 2016 Xamarin. All rights reserved.
<!-- Creates a Resource.designer.cs file in the Intermediate output path which derives from the
Designer Assembly, for backward compatability.
-->
<AndroidError Code="XA1038"
ResourceName="XA1038"
FormatArguments="AndroidResourceDesignerClassModifier;$(AndroidResourceDesignerClassModifier);&apos;public&apos;, &apos;internal&apos;"
Condition=" '$(AndroidResourceDesignerClassModifier)' != 'public' And '$(AndroidResourceDesignerClassModifier)' != 'internal' "
/>
<GenerateResourceDesignerIntermediateClass
IsApplication="$(AndroidApplication)"
Namespace="$(AndroidResgenNamespace)"
Modifier="$(AndroidResourceDesignerClassModifier)"
OutputFile="$(_GenerateResourceDesignerClassFile)"
>
</GenerateResourceDesignerIntermediateClass>
Expand Down
7 changes: 7 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,13 @@ To use a custom JDK path for a command line build, set the 'JavaSdkDirectory' MS
<comment>The following are literal names and should not be translated: MSBuild, .NET.
{0} - The deprecated MSBuild property name
{1} - The numeric version of .NET</comment>
</data>
<data name="XA1038" xml:space="preserve">
<value>The '{0}' MSBuild property has an invalid value of '{1}'. A valid value is one of: {2}.</value>
<comment>The following are literal names and should not be translated: MSBuild.
{0} - The MSBuild property name which has invalid values.
{1} - The current value of the property
{2} - The list of valid values for the property these can be Comma-separated.</comment>
</data>
<data name="XA1039" xml:space="preserve">
<value>The Android Support libraries are not supported in .NET 9 and later, please migrate to AndroidX. See https://aka.ms/net-android/androidx for more details.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace %NAMESPACE% {
/// Exposes the Android Resource designer assembly into the project Namespace.
/// </summary>
[GeneratedCode(""%TOOL%"", ""%VERSION%"")]
public partial class Resource : %BASECLASS% {
%MODIFIER% partial class Resource : %BASECLASS% {
}
#pragma warning restore IDE0002
}
Expand All @@ -43,15 +43,17 @@ public partial class Resource : %BASECLASS% {
namespace %NAMESPACE%
[<type:System.CodeDom.Compiler.GeneratedCode(""%TOOL%"", ""%VERSION%"")>]
type Resource = %BASECLASS%
type %MODIFIER% Resource = %BASECLASS%
";

public string Namespace { get; set; }
public string Modifier { get; set; } = "public";
public bool IsApplication { get; set; } = false;
public ITaskItem OutputFile { get; set; }
public override bool RunTask ()
{
string ns = IsApplication ? ResourceDesignerConstants : ResourceDesigner;
string baseClass = IsApplication ? ResourceDesignerConstants : ResourceDesigner;
string modifier = IsApplication ? Modifier : "public";
var extension = Path.GetExtension (OutputFile.ItemSpec);
var language = string.Compare (extension, ".fs", StringComparison.OrdinalIgnoreCase) == 0 ? "F#" : CodeDomProvider.GetLanguageFromExtension (extension);
//bool isVB = string.Equals (extension, ".vb", StringComparison.OrdinalIgnoreCase);
Expand All @@ -63,11 +65,13 @@ public override bool RunTask ()
template = CSharpTemplate.Replace ("%NAMESPACE%", Namespace)
.Replace ("%BASECLASS%", ns)
.Replace ("%VERSION%", version.ToString ())
.Replace ("%MODIFIER%", modifier)
.Replace ("%TOOL%", nameof (GenerateResourceDesignerIntermediateClass));
} else if (isFSharp) {
template = FSharpTemplate.Replace ("%NAMESPACE%", Namespace)
.Replace ("%BASECLASS%", ns)
.Replace ("%VERSION%", version.ToString ())
.Replace ("%MODIFIER%", modifier)
.Replace ("%TOOL%", nameof (GenerateResourceDesignerIntermediateClass));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot, bo
new Package { Id = "System.Text.Json", Version = "8.0.*" },
},
Sources = {
new BuildItem ("EmbeddedResource", "Foo.resx") {
TextContent = () => InlineData.ResxWithContents ("<data name=\"CancelButton\"><value>Cancel</value></data>")
new BuildItem ("EmbeddedResource", "Resource.resx") {
TextContent = () => InlineData.ResxWithContents ("<data name=\"CancelButton\"><value>Cancel</value></data>"),
},
new BuildItem ("EmbeddedResource", "Foo.es.resx") {
new BuildItem ("EmbeddedResource", "Resource.es.resx") {
TextContent = () => InlineData.ResxWithContents ("<data name=\"CancelButton\"><value>Cancelar</value></data>")
},
new AndroidItem.TransformFile ("Transforms.xml") {
Expand All @@ -61,7 +61,8 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot, bo
},
}
};
proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": AndroidX.AppCompat.App.AppCompatActivity");
proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": AndroidX.AppCompat.App.AppCompatActivity")
.Replace ("//${AFTER_ONCREATE}", @"button.Text = Resource.CancelButton;");
proj.SetProperty ("AndroidUseAssemblyStore", usesAssemblyStore.ToString ());
proj.SetProperty ("RunAOTCompilation", aot.ToString ());
proj.OtherBuildItems.Add (new AndroidItem.InputJar ("javaclasses.jar") {
Expand All @@ -70,6 +71,21 @@ public void DotNetBuild (string runtimeIdentifiers, bool isRelease, bool aot, bo
proj.OtherBuildItems.Add (new BuildItem ("JavaSourceJar", "javaclasses-sources.jar") {
BinaryContent = () => ResourceData.JavaSourceJarTestSourcesJar,
});
proj.OtherBuildItems.Add (new BuildItem ("EmbeddedResource", default (Func<string>)) {
Update = () => "Resource.resx",
TextContent = () => InlineData.ResxWithContents ("<data name=\"CancelButton\"><value>Cancel</value></data>"),
Metadata = {
{ "Generator", "ResXFileCodeGenerator" },
{ "LastGenOutput", "Resource.designer.cs" }
},
});
proj.OtherBuildItems.Add (new BuildItem ("Compile", default (Func<string>)) {
TextContent = () => InlineData.DesignerWithContents (proj.ProjectName, "Resource", "public partial", new string[] {"CancelButton"}),
Update = () => "Resource.designer.cs",
Metadata = {
{ "DependentUpon", "Resource.resx" },
},
});
proj.AndroidJavaSources.Add (new AndroidItem.AndroidJavaSource ("JavaSourceTestExtension.java") {
Encoding = Encoding.ASCII,
TextContent = () => ResourceData.JavaSourceTestExtension,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace @projectName@
[System.CodeDom.Compiler.GeneratedCodeAttribute(""System.Resources.Tools.StronglyTypedResourceBuilder"", ""4.0.0.0"")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class @className@ {
@modifier@ class @className@ {
private static System.Resources.ResourceManager resourceMan;
Expand Down Expand Up @@ -87,7 +87,7 @@ public static void AddCultureResourcesToProject (IShortFormProject proj, string
}
}

public static string DesignerWithContents (string projectName, string className, string[] dataNames)
public static string DesignerWithContents (string projectName, string className, string modifier, string[] dataNames)
{
var content = new StringBuilder ();
foreach (string data in dataNames) {
Expand All @@ -97,15 +97,16 @@ public static string DesignerWithContents (string projectName, string className,
}}
}}" + Environment.NewLine, data);
}
return Designer.Replace ("@className@", className)
return Designer.Replace ("@modifier@", modifier)
.Replace ("@className@", className)
.Replace ("@projectName@", projectName)
.Replace ("@content@", content.ToString ());
}

public static void AddCultureResourceDesignerToProject (IShortFormProject proj, string projectName, string className, params string[] dataNames)
{
proj.OtherBuildItems.Add (new BuildItem.Source ($"{className}.Designer.cs") {
TextContent = () => InlineData.DesignerWithContents (projectName, className, dataNames)
TextContent = () => InlineData.DesignerWithContents (projectName, className, "internal", dataNames)
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public static string ToXml (IShortFormProject project)
if (project.OtherBuildItems.Count > 0) {
sb.AppendLine ("\t<ItemGroup>");
foreach (var bi in project.OtherBuildItems) {
if (bi.BuildAction != BuildActions.EmbeddedResource) {
// If its an EmbeddedResource ignore it, unless it has an Update method set.
if (bi.BuildAction != BuildActions.EmbeddedResource || bi.Update != null) {
AppendBuildItem (sb, bi);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<ItemGroup>
<CorrectCasedItem Include="%(Compile.Identity)" Condition=" '$(AndroidUseIntermediateDesignerFile)' == 'true' And '$(AndroidResgenFile)' != '' And '%(Compile.Identity)' == '$(AndroidResgenFile)'"/>
<!-- Forcably Remove the old Resource.designer.cs even if the AndroidResgenFile is not set.-->
<CorrectCasedItem Include="%(Compile.Identity)" Condition=" '$(AndroidUseDesignerAssembly)' == 'True' And '%(Compile.Filename)%(Compile.Extension)' == '$(_AndroidResourceDesigner)' "/>
<CorrectCasedItem Include="%(Compile.Identity)" Condition=" '$(AndroidUseDesignerAssembly)' == 'True' And '%(Compile.Filename)%(Compile.Extension)' == '$(_AndroidResourceDesigner)' And '%(Compile.DependentUpon)' == '' "/>
<Compile Remove="@(CorrectCasedItem)" />
<Compile Include="$(_AndroidResourceDesignerFile)" Condition=" '$(AndroidUseIntermediateDesignerFile)' == 'true' And '$(AndroidUseDesignerAssembly)' != 'true' " />
</ItemGroup>
Expand Down

0 comments on commit b22c3a5

Please sign in to comment.