NuGet / Home

Repo for NuGet Client issues
Other
1.49k stars 252 forks source link

[Bug]: ContentFiles not working as expected in .Net Framework project using PackageReference #11978

Open ds27680 opened 2 years ago

ds27680 commented 2 years ago

NuGet Product Used

NuGet.exe, Visual Studio Package Management UI

Product Version

NuGet: 5.4.0.6315, VisualStudio: 2022

Worked before?

Not really

Impact

It's more difficult to complete my work

Repro Steps & Context

For the purpose of better managing translations, we are looking into moving resource files out of one of our projects to a repository where translators have access.

We would like to have the resource files only within that repository.

Currently, the resources are typically split within a project following this type of pattern:

TypicalProjectStructure

One basically has folders ResourcesA, ResourcesB a.s.o. containing Resources.resx and their localizations.

The idea would be then, to generate NuGet packages from the separate repository, containing the resources as contentFiles, remove the resource files from our projects in the original repository, and simply reference the NuGet packages created from the repository the packages were moved to.

The original project containing the resources is a .Net Framework .csproj (currently targeting 4.8) using PackageReferences.

Generating NuGet packages is done using a Nuspec in the lines of:

            <?xml version="1.0"?>
            <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
                <metadata>
                    <id>SomeClassLibrary.Resources</id>
                    <version>1.0.0</version>
                    <title>Common resources NuGet package for reproducing contentFiles Issues in .Net Framework Projects.</title>
                    <authors>Stefan Duca</authors>
                    <owners>Stefan Duca</owners>
                    <description>Common resources NuGet package for the Riem project (retina workplace).</description>
                    <releaseNotes>Initial release.</releaseNotes>
                    <copyright>Free for all.</copyright>
                    <tags>NuGet, Issues, ContentFiles, Resources, .NET</tags>
                    <dependencies/>

                    <contentFiles>
                        <files include="**/ResourcesA/*.resx" buildAction="EmbeddedResource" />
                        <files include="**/ResourcesB/*.resx" buildAction="EmbeddedResource" />
                    </contentFiles>

                </metadata>

                <files>
                  <file src=".\ResourcesA\*.resx" target="contentFiles\any\any\ResourcesA" /> 
                  <file src=".\ResourcesB\*.resx" target="contentFiles\any\any\ResourcesB" />     
                </files>

            </package>

Removing the *.resx files from the original project and referencing the NuGet package created using the above Nuspec leads to a build error:

Severity Code Description Project File Line Suppression State Error Two output file names resolved to the same output path: "obj\Debug\SomeClassLibrary.Resources.resources" SomeClassLibrary C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\amd64\Microsoft.Common.CurrentVersion.targets 3262

Indeed, looking at the MSBuild output one has:

1>Task "AssignTargetPath" (TaskId:13) 1> Task Parameter:RootFolder=C:\Temp\NugetContentSample\TestContentPackage-WithPackageResourcesRemoved\SomeClassLibrary (TaskId:13) 1> Task Parameter: 1> Files= 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.es.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.es.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 (TaskId:13) 1> Output Item(s): 1> _Temporary= 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.es.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> OriginalItemSpec=C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.es.resx 1> TargetPath=Resources.es.resx 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> OriginalItemSpec=C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesA\Resources.resx 1> TargetPath=Resources.resx 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.es.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> OriginalItemSpec=C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.es.resx 1> TargetPath=Resources.es.resx 1> C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.resx 1> NuGetItemType=EmbeddedResource 1> NuGetPackageId=SomeClassLibrary.Resources 1> NuGetPackageVersion=1.0.0 1> OriginalItemSpec=C:\Users\mssdu.nuget\packages\someclasslibrary.resources\1.0.0\contentFiles\any\any\ResourcesB\Resources.resx 1> TargetPath=Resources.resx (TaskId:13)

So, it seems that the TargetPath gets flattened out, although this is not requested in the nuspec. Searched and found also this: #7255.

However:

  1. The logs indicate that an attempt is being made to honor the contentFiles properly, and I did not find any indication in the documentation that the functionality does not work in .Net framework project with PackageReferences.

  2. The NuGet package works as expected in a .Net Core project.

  3. Even if I only have just the resources in ResourceA in the NuGet package I still get a build error (so the flattening of the folder structure is not the issue here), I have the impression it is more a conflict between the embedded resources from the NuGet and the default assembly manifest resources getting basically the same *.resource output names

I attached an archive to this report. Please see:

NugetContentSample-21.07.2022.zip

The archive contains:

ZipContents

Where:

NuGetContent: Contains the files and the Nuspec for generating the resources NuGet package. One can generate/re-generate the package by opening a cmd prompt in the folder and issuing: > nuget pack SomeClassLibrary.nuspec -OutputDirectory ../NuGetPackages

NuGetPackages: Contains the generated NuGet package. The projects contained in TestContentPackage-WithPackageResourcesRemoved and TestContentPackage-.NetCore configure this directory as the only Nuget package source avaiable for simplicity.

Screenshots: Contains some of the screenshots in the report.

TestContentPackage-Start: Contains the original "sample" .Net Framework project with the resource files in. Builds and works as expected. It contains a console project and a library (which contains the resources). The console can be executed with "en-US" or "es" as parameter and gets and prints out two resource strings one from the ResourcesA and one from ResourcesB, to illustrate that all works "as expected"

TestContentPackage-WithPackageResourcesRemoved: Contains a copy of the "sample" .Net Framework project where the resx files were removed from the library and the library references the resources NuGet package. It does not build, gives error message described above. Console app is also present and is identical to the one in TestContentPackage-Start.

TestContentPackage-.NetCore: A .Net Core project that similarly to the TestContentPackage-WithPackageResourcesRemoved contains a library where the resx files were removed and the library references the resources NuGet package. It builds and works as expected. It contains a console project that works in the same way as the one in "TestContentPackage-Start".

I know I could maybe "fix" my problem by using a .targets file that does copy the *.resx files to the projects before build, but I would rather avoid doing this.

Currently I'm still hoping that I am doing something something wrong...

Verbose Logs

No response

dominoFire commented 2 years ago

@ds27680 Have you taken a look at https://github.com/Humanizr/Humanizer/tree/main/NuSpecs ?

It seems this repo organization might solve how to ship localized packages without relying on contentFiles; unless you want to ship some contentFiles functionality for your package consumers.

nkolev92 commented 2 years ago

Team Triage: Pulling into this sprint to analyze in more detail whether there's truly a bug here with .NET Framework projects, or whether the package can be authored differently.