NightOwl888 / ICU4N

International Components for Unicode for .NET
Apache License 2.0
27 stars 7 forks source link

.NET 7/.NET 8 MAU projects don't build if NuGet package referencing ICU4N.Resources is added: alleged same target path for e.g. zh-HK\ICU4N.resources.dll #65

Closed martin-honnen closed 1 month ago

martin-honnen commented 10 months ago

I have found that .NET 7 or .NET 8 MAUI projects stop building with VS 2022 with errors like shown below as soon as I add SaxonCS as a NuGet package to the project; SaxonCS (12.4) references ICU4N.Resources 60.1.0-alpha.402.

My translation of the (German) error message VS is giving below (repeatedly) is "Resource data contains several files with the same target path"

Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-HK\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-HK\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-HK\ICU4N.resources.dll   MauiNet7SaxonCS12Test1  C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.2.221209.1\buildTransitive\Microsoft.Build.Msix.Packaging.targets  1504        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-SG\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hans-SG\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-SG\ICU4N.resources.dll    MauiNet7SaxonCS12Test1  C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.2.221209.1\buildTransitive\Microsoft.Build.Msix.Packaging.targets  1504        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-MO\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-MO\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-MO\ICU4N.resources.dll    MauiNet7SaxonCS12Test1  C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.2.221209.1\buildTransitive\Microsoft.Build.Msix.Packaging.targets  1504        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-TW\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-TW\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-TW\ICU4N.resources.dll    MauiNet7SaxonCS12Test1  C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.2.221209.1\buildTransitive\Microsoft.Build.Msix.Packaging.targets  1504

I am not sure what is causing this, some other .NET 8/VS 2022 project types like WPF build/work fine with SaxonCS referencing ICU4N.Resources 60.1.0-alpha.402.

Any idea whether that is something you can fix with ICU4N.Resources or whether I need to try to file that as a bug on .NET 7/8 MAUI/VS 2022?

martin-honnen commented 10 months ago

I have made some progress to identify when things started to break in terms of referencing SaxonCS, with 12.1 which references ICUN 60.1.0alpha.354 a .NET 8 MAUI project builds and works, starting with SaxonCS 12.2 the build fails with errors as given above.

NightOwl888 commented 10 months ago

Thanks for the report.

The Saxon team has reported an issue with getting build warnings when using ICU4N.Resources, but this seems to be a different issue.

Background

So, what is known about the issue so far is that NuGet seems perfectly happy with having resource files with names that .NET doesn't recognize. However, MSBuild does not automatically copy resource files for names it does not recognize (many of these resource files are 3-character resource names). If you notice in the ~/.nuget/packages/icu4n/60.1.0-alpha.402/lib/netstandard1.0/ folder, there are 768 resource folders, plus the main ICU4N.resources file, but when doing a build (on .NET 7.0) there are around ~600 resources that are copied into the project by MSBuild.

I am hesitant to report this to Microsoft as a bug, because it isn't a very common use case to create resource files with names that .NET doesn't recognize. Most projects would use the built-in .resx functionality to build resource files using officially supported culture names.

I think the solution will ultimately be to use officially supported .NET culture names and pack the non-matching names into the most relevant related culture (there is a 3-letter to 2-letter conversion function in ICU that will do most of the heavy lifting).

I know there are some hard coded rules in .NET around Chinese cultures, and that may be contributing to the issue.

I haven't had a chance to work out how to close the gap between .NET culture names and ICU culture names. I know Microsoft maintains a custom build of ICU4C for use in .NET Core here: https://www.nuget.org/packages/Microsoft.ICU.ICU4C.Runtime, but I haven't dug into the source (of dotnet/runtime, MAUI or ICU4C) to see how this culture name mapping happens.

Potential Workaround

A couple of things you can try get the build to function:

Use an explicit reference to ICU4N.Resources

First, remove the transitive dependency on ICU4N.Resources. There are a couple of options here.

Option 1

This is supposed to work with MSBuild to remove the transitive dependency. If not, that is a bug that will need to be reported to Microsoft.

<ItemGroup>
    <PackageReference Include="ICU4N" Version="60.1.0-alpha.402" ExcludeAssets="buildTransitive" />
</ItemGroup>

Option 2

In your build file, use <IncludeICU4NResources>false</IncludeICU4NResources>.

Either of these options should remove the PackageReference to ICU4N.Resources from your project. Then, you can explicitly add a reference to it.

<PackageReference Include="ICU4N.Resources" Version="60.1.0-alpha.402" PrivateAssets="all" />

Have your build copy all of the ICU4N.Resources.dll files into your project

If MSBuild still isn't happy with an explicit reference to ICU4N.Resources, you can setup your build to copy the resource files to the build output. This is more advanced and will require you to do some scripting which may vary depending on what build tools you use. As previously mentioned, the files are in the ~/.nuget/packages/icu4n/60.1.0-alpha.402/lib/netstandard1.0/ folder. You can either work out how to copy them from there or move them to another location within the solution to do the copy.

This is how we do it internally, but it isn't a great solution:

<ItemGroup Label="Specifies generated sattelite assemblies should be copied to the output folder (and dependent projects as well)">
  <None Include="$(ICU4NSatelliteAssemblyOutputDir)/**/*.resources.dll" CopyToOutputDirectory="PreserveNewest" Visible="false" />
</ItemGroup>

Of course, more work may be needed to determine how to deploy the resource files after you get the build working.

NightOwl888 commented 10 months ago

I have made some progress to identify when things started to break in terms of referencing SaxonCS, with 12.1 which references ICUN 60.1.0alpha.354 a .NET 8 MAUI project builds and works, starting with SaxonCS 12.2 the build fails with errors as given above.

Yeah, that works because it was before we split the resource files out into satellite assemblies. It unblocks you from having a build failure, but doesn't help get us closer to a working solution with satellite assemblies.

martin-honnen commented 10 months ago

Following your advice with option 1 and SaxonCS 12.4 I still get errors about several files with the same target path:

Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-HK\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-HK\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-HK\ICU4N.resources.dll   MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-SG\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hans-SG\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-SG\ICU4N.resources.dll    MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-MO\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-MO\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-MO\ICU4N.resources.dll    MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler  APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-TW\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-TW\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-TW\ICU4N.resources.dll    MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500    

I will try your suggestions on "Have your build copy all of the ICU4N.Resources.dll files into your project" later this evening or tomorrow and report back whether it helped.

NightOwl888 commented 10 months ago

Following your advice with option 1 and SaxonCS 12.4 I still get errors about several files with the same target path:

Fehler    APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-HK\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-HK\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-HK\ICU4N.resources.dll MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler    APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-SG\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hans-SG\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-SG\ICU4N.resources.dll  MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler    APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-MO\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-MO\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-MO\ICU4N.resources.dll  MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500        
Fehler    APPX1101    Die Nutzdaten enthalten mehrere Dateien mit dem gleichen Zielpfad "zh-TW\ICU4N.resources.dll". Quelldateien: 
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-Hant-TW\ICU4N.resources.dll
C:\Users\marti\.nuget\packages\icu4n.resources\60.1.0-alpha.402\lib\netstandard1.0\zh-TW\ICU4N.resources.dll  MauiNet8SaxonCS121Test1 C:\Users\marti\.nuget\packages\microsoft.windowsappsdk\1.3.230724000\buildTransitive\Microsoft.Build.Msix.Packaging.targets 1500    

Well, there are no duplicate culture names in the NuGet cache, so this definitely sounds like a bug in MSBuild for MAUI.

But looking at the list, there are some grandfathered culture names in there that may be interfering with the file copy logic.

If you package the resources into your own NuGet package and exclude the Chinese cultures that have been superseded, I suspect it will work. MAUI is apparently not expecting both current and legacy file names, but one or the other. The effect will be that the legacy culture names won't work in ICU4N, but there probably aren't many users who are using them, anyway.

If that works, then the fix we have already planned - to eliminate the grandfathered and unknown culture names from the set of resource files and include the resources in the most appropriate resource file - will fix this issue as well.