dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.92k stars 526 forks source link

[regression/8.0.3] [Bug] .net8 Android not building because Resources from resx not found. #9163

Open Larhei opened 9 months ago

Larhei commented 9 months ago

Description

While migrating from .net7 to .net8 i was facing the issue that my app is not compiling in vs code and vs4mac on my mac.

I get a lot of errors like: error CS0234: The type or namespace name 'Resources' does not exist in the namespace 'StringResourcesBug' (are you missing an assembly reference?)

caused by code like this: var x = StringResourcesBug.Resources.Strings.Resource.Test;

After a lot of trial an error, I found out the reason for this is the name of the class in the Resource.Designer.cs file. If you create a rest file with the name Resource.resx you are running into this issue. Renaming the file to Resources.resx and the Class to Resources fixes the issue

But the project was building on .net6 and .net7. Also iOs version in .net8 with Resouce.resx is building and is able to run. So I looks like a Android specific thing to me.

Steps to Reproduce

  1. Clone the Repo
  2. Open the Solution in VSCode or VS4Mac
  3. Compile the Solution while Targeting Android Emulator

Link to public reproduction project repository

https://github.com/Larhei/Maui-Issues/tree/main/StringResourcesBug

Version with bug

8.0.3

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

7.0.101

Affected platforms

Android

Affected platform versions

all android

Did you find any workaround?

Renaming the resx file and the name of the class in *.Designer.cs

Relevant log output

No response

mattleibow commented 9 months ago

@jonathanpeppers thoughts?

jonathanpeppers commented 9 months ago

Having a Resource.resx could certainly conflict with the Android Resource.designer.cs:

https://github.com/Larhei/Maui-Issues/blob/main/StringResourcesBug/StringResourcesBug/Resources/Strings/Resource.resx

This is the same as if you created a plain C# Resource class yourself with the same namespace.

I don't think this actually changed in .NET 8 at all, would have happened back in Xamarin.Android as well? @dellis1972 do you think the same?

Larhei commented 9 months ago

@jonathanpeppers

That my git diff tells me the changes to my csproj are: `

net7.0-android;net7.0-ios

changed to

net8.0-android;net8.0-ios

`

added `

` and changed

`

True
  <AutoGen>True</AutoGen>
  <DependentUpon>Resource.resx</DependentUpon>
</Compile>

PublicResXFileCodeGenerator
  <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>`

to

`

Resources.resx
</Compile>

ResXFileCodeGenerator
  <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>`

And the old Code was building on Mac with .net7 and is in Android and iOS store.

jonathanpeppers commented 9 months ago

@Larhei can you share a .binlog of when it worked (and doesn't now?) https://aka.ms/binlog

Larhei commented 9 months ago

@jonathanpeppers

Well I will try my best ;-)

ghost commented 9 months ago

Hi @Larhei. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

Larhei commented 9 months ago

@jonathanpeppers

here we go Binlogs.zip

Same Machine Same VS Same Project Only difference is TFM 7 and TFM 8

dellis1972 commented 9 months ago

Probably conflicting with obj/Debug/net8.0-android/__Microsoft.Android.Resource.Designer.cs. Which in itself is a partial class, so if the resx code is also partial it should have worked.

jonathanpeppers commented 9 months ago

I think both .NET 7/8 were partial, though:

> cat .\obj\Debug\net7.0-android\Resource.designer.cs | select -First 25
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

[assembly: global::Android.Runtime.ResourceDesignerAttribute("testlib.Resource", IsApplication=false)]

namespace testlib
{

        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.1.99.94")]
        public partial class Resource

vs:

> cat obj\Debug\net8.0-android\__Microsoft.Android.Resource.Designer.cs
//------------------------------------------------------------------------------
// <auto-generated>
//      This code was generated by a tool. DO NOT EDIT
// </auto-generated>
//------------------------------------------------------------------------------
using System;

namespace testlib {
        #pragma warning disable IDE0002
        public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant {
        }
        #pragma warning restore IDE0002
}

It's weird that 7 works somehow?

dellis1972 commented 9 months ago

I know. Its the inheritance, doh šŸ¤¦

dellis1972 commented 9 months ago

I bet the resx class, does not inherit from anything. That would throw off the compiler maybe.

jonathanpeppers commented 9 months ago

Here is the example:

https://github.com/Larhei/Maui-Issues/blob/72b0ba302cdaebf1e438318895c1667b02fe7385/StringResourcesBug/StringResourcesBug/Resources/Strings/Resource.Designer.cs#L10C49-L22

dellis1972 commented 9 months ago

actually that should not clash as its in a different namespace StringResourcesBug.Resources.Strings. The android designer will be in StringResourcesBug.Resource.

the-gozo commented 9 months ago

Do you have .NET Upgrade Assistant extension installed in VS? What is the build propety of your .resx files? It should be MauiResource or something similar. For me I had a similar issue: the Assistant extension somehow set my resources files build property to AndroidResource. If this is the case, chek all your other resources(images, ttf, icons).

last-Programmer commented 9 months ago

I am also having this issue with Android resources. Resource.Designer.cs file is not beging created for Android resources. I am getting the error in the IDE CS0117 but when i build it it is building. I have the old Resource.designer.cs from xamarin android project and it is never getting updated with newly address reosurces.

dellis1972 commented 9 months ago

@last-Programmer if you are targetting .net 8 the old Resource.designer.cs is now redundant and can be deleted. See https://devblogs.microsoft.com/dotnet/android-resource-designer-dotnet-8/ for details.

last-Programmer commented 9 months ago

@dellis1972 Thank You very much. I missed that part. Wont i get the intellisense for resource ids any more? because in vs for mac i get the ide error CS0117 at present. Is there a way to get around this. ? In the article it does not say anything about intellisense or error CS0117

dellis1972 commented 9 months ago

@last-Programmer if you have the full details of the CS0117 error please post them here.

last-Programmer commented 9 months ago

@dellis1972 It seems to be that the error CS0117 is happening only in VS for MAC and not in VS for Windows with newly created .net8 Android application

Steps to reproduce in VS for Mac.

Create a new net8.0 android application and open MainActivity.cs and you can see that at Resource.Layout.* error CS0117 is shown. However it builds fine. I suspect because .net 8.0 is only preview support in VS for Mac and it is not supported fully. Any workarounds?

dellis1972 commented 9 months ago

@last-Programmer not that I'm aware of. Its likely that VSForMac is not handling the new system, it might still be trying to make use of the old system. I'll mention it to my collegue on the IDE team see if they can take a look.

ninachen03 commented 6 months ago

I used the Repo project and reproduced the problem in 17.6.9 build (415 )on android platform. image

jonpryor commented 3 months ago

@dellis1972, @moljac

dellis1972 commented 2 months ago

//Removed as it was incorrect

Larhei commented 2 months ago

@dellis1972 But my Resource.resx generates the namespace namespace StringResourcesBug.Resources.Strings

and __Microsoft.Android.Resource.Designer namespace StringResourcesBug

for me this looks like 2 different namespaces

so i would say that there are 2 different classes named Resource in separate namespaces..

dellis1972 commented 2 months ago

Ok ignore ALL of what I said. This is the actual problem

Target "_RemoveLegacyDesigner: (TargetId:213)" in file "/usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/34.0.79/tools/Xamarin.Android.Common.targets" from project "/Users/dean/Documents/Sandbox/Repos/customer-repoductions/StringResourcesBug/C/C.csproj" (target "UpdateGeneratedFiles" depends on it):
                   Added Item(s): CorrectCasedItem=Resources/Strings/Resource.Designer.cs
                   Removed Item(s): 
                       Compile=
                           Resources/Strings/Resource.Designer.cs
                                   DependentUpon=Resource.resx

The new system has a target which removes the old legacy resource designer. In this case it is incorrectly removing the one which is for the Resource.resx from the Compile group.

dellis1972 commented 2 months ago

Its because they have the same filename. We need to make the target a bit less agressive and ignore items which have the DependentUpon. metadata I think.

dellis1972 commented 2 months ago

So fixing the issue by checking for DependentUpon metadata works but then you end up with the following error

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

This is because the two Resource classes have different declarations public partial Resource vs internal Resource. The Resource.designer.cs will need to have its modifier changed to public partial in order for the app to compile. Once that is done it will work as expected.

Larhei commented 2 months ago

@dellis1972

Sorry to bother you again. But something seems to be fishy here. The documentation of CS0260 states:

This error also occurs if you declare a class and accidentally give it the same name as a partial class that's declared elsewhere in the same namespace.

When doing some sample like this:

{
    private static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
        var x = new Test.A();
        var y = new Test1.A();
        Console.WriteLine($"{x.Test} {x.Test1}");
        Console.WriteLine($"{y.Test} {y.Test1}");
    }
}

namespace Test
{
    public partial class A
    {
        public string Test { get; set; } = "ATest";
    }

    partial class A
    {

        public string Test1 { get; set; } = "ATest1";
    }
}

namespace Test1
{
    internal partial class A
    {
        public string Test { get; set; } = "A1Test";
    }

    internal partial class A
    {

        public string Test1 { get; set; } = "A1Test1";
    }
}

It compiles without an error. If i understand it correct, My internal partial class Resource is in an other namespace. So I donĀ“t understand why we get CA0260.

If making my class public solves the issue. Fine. But I donĀ“t understand why I should have to do that.

dellis1972 commented 2 months ago

This represents what the situation is.

{
    private static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
        var y = new Test1.A();
        Console.WriteLine($"{y.Test} {y.Test1}");
    }
}

namespace Test
{
// this represents the code in the auto generated _Microsoft.Android.Resource.Designer.dll
    public partial class Base1
    {
        public string Test { get; set; } = "ATest";
    }
}

namespace Test1
{
    public partial class A : Base1 // this represents the code in the auto generated __Microsoft.Android.Resource.Designer.cs file
    {
        public string Test { get; set; } = "A1Test";
    }

    internal class A // This is the Resource.designer.cs for the resx
    {

        public string Test1 { get; set; } = "A1Test1";
    }
}
Larhei commented 2 months ago

@dellis1972 What is even more interesting, in my sample my Resource class is not even a partial class. it is

namespace StringResourcesBug.Resources.Strings {
internal class Resource {
}
}

vs

namespace StringResourcesBug {
public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant {
}
}
dellis1972 commented 2 months ago

I'm adding a new MSbuild properrty to allow the access modifier of the Resource : _Microsoft.Android.Resource.Designer.ResourceConstant class to be changed. It will still be partail, but it could be made internal or private or public in the application project. For Library projects it will always need to be public for legacy reasons.

That said if someone wants to change it an it breaks their code because they did Library1.Resource.Id.foo from their main application and the Library1.Resource class is no longer public (because they changed it) then its not really our issue.

dellis1972 commented 2 months ago

@Larhei actually given your classes are in different namespaces it might be ok for you. It was just something that I came up agsinst while trying to implement a unit test.

Larhei commented 2 months ago

@dellis1972 So if i got you correct

My

namespace StringResourcesBug.Resources.Strings {
internal class Resource {
}
}

Gets an generated

namespace StringResourcesBug.Resources.Strings {
public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant{
}
}

Correct?

If so. ThatĀ“s explaining why we get CS0260.

If this is the case.. On other option would be to generate all Propties from the internal class Resource into the public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant and remove internal class Resource from MsBuild... But this is also giving me a headache...

dellis1972 commented 2 months ago

@dellis1972 So if i got you correct

My

namespace StringResourcesBug.Resources.Strings {
internal class Resource {
}
}

Gets an generated

namespace StringResourcesBug.Resources.Strings {
public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant{
}
}

Correct?

If so. ThatĀ“s explaining why we get CS0260.

If this is the case.. On other option would be to generate all Propties from the internal class Resource into the public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant and remove internal class Resource from MsBuild... But this is also giving me a headache...

Sorry there was some confusion, you probably won't hit the CS0260 (but I was in my unit test)

you will be getting

namespace StringResourcesBug.Resources.Strings {
internal class Resource {
}
}

Gets an generated (note the namespace is different)

namespace StringResourcesBug{
public partial class Resource : _Microsoft.Android.Resource.Designer.ResourceConstant{
}
}

Which will work once I get the fix in. My test was testing classes in the same namespace, which is why I was getting the CS0260.

Larhei commented 2 months ago

@dellis1972 Tanks for clarifications