Open bergmeister opened 5 years ago
Team triage: I'd like to dig in on some of the changes required here. This may turn into an SDK feature to make it easier to get strongly typed resources.
Yes, please, ideally the csproj should just pick it up automatically via convention over configuration if the resx file name matches the csproj name. Please make sure it works both from commandline and VS
@rainersigwald Any updates? Building using dotnet build
works now with the described scenario but both VS and VS-Code get confused and show compiler warnings and errors (VS-Code somehow sees the class name as ambiguous and VS fails to build). Also, at runtime, I get the following exception:
MissingManifestResourceException: Could not find the resource "Strings.resources" among the resources "Engine.Strings.resources" embedded in the assembly "Microsoft.Windows.PowerShell.ScriptAnalyzer", nor among the resources in any satellite assemblies for the specified culture. Perhaps the resources were embedded with an incorrect name.
I created the following branch with my changes: https://github.com/bergmeister/PSScriptAnalyzer/tree/netcore3_resgen
Any updates @rainersigwald ? This would be good to be fixed in 3.1 as it will be LTS
I am trying to move PowerShell Core projects to the generator.
With @rainersigwald sample I was able to compile most of csproj-s but not last with UseWPF enabled.
With
I could be able compile PowerShell Core with some workarounds. See https://github.com/PowerShell/PowerShell/pull/12355
I was successful for a while doing this:
<ItemGroup>
<EmbeddedResource Update="TracerMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TracerMessages.Designer.cs</LastGenOutput>
<StronglyTypedFileName>TracerMessages.Designer.cs</StronglyTypedFileName>
<StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
<StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace>
<StronglyTypedClassName>TracerMessages</StronglyTypedClassName>
</EmbeddedResource>
</ItemGroup>
Note the LastGenOutput
and StronglyTypedFileName
match. Unfortunately in a recent .NET Core SDK update (I'm now running 3.1.302 on Mac) this started yielding a CS2002 warning:
CSC : warning CS2002: Source file 'TracerMessages.Designer.cs' specified multiple times [/Users/tillig/dev/autofac/Autofac.Diagnostics.DotGraph/src/Autofac.Diagnostics.DotGraph/Autofac.Diagnostics.DotGraph.csproj]
The only way to work around it was to remove the checked-in Designer.cs file and switch to the $(IntermediatePath)
in the StronglyTypedFileName
as seen in the initial issue comment.
<ItemGroup>
<EmbeddedResource Update="TracerMessages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TracerMessages.Designer.cs</LastGenOutput>
<StronglyTypedFileName>$(IntermediateOutputPath)/TracerMessages.Designer.cs</StronglyTypedFileName>
<StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
<StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace>
<StronglyTypedClassName>TracerMessages</StronglyTypedClassName>
</EmbeddedResource>
</ItemGroup>
This issue is the only place I've found any of this documented. It'd be nice if this was more straightforward to work with in a non-Visual-Studio (i.e., VS Code / all-command-line) environment.
I was successful for a while doing this:
<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
Note the
LastGenOutput
andStronglyTypedFileName
match. Unfortunately in a recent .NET Core SDK update (I'm now running 3.1.302 on Mac) this started yielding a CS2002 warning:
CSC : warning CS2002: Source file 'TracerMessages.Designer.cs' specified multiple times [/Users/tillig/dev/autofac/Autofac.Diagnostics.DotGraph/src/Autofac.Diagnostics.DotGraph/Autofac.Diagnostics.DotGraph.csproj]
The only way to work around it was to remove the checked-in Designer.cs file and switch to the
$(IntermediatePath)
in theStronglyTypedFileName
as seen in the initial issue comment.<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>$(IntermediateOutputPath)/TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
This issue is the only place I've found any of this documented. It'd be nice if this was more straightforward to work with in a non-Visual-Studio (i.e., VS Code / all-command-line) environment.
While this solution removes the warning on running dotnet build
and project compiles normally, it then makes Visual Studio Code show errors in the places that used the strongly typed generated class indicating that it can't be found.
Is there no other workaround?
I was successful for a while doing this:
<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
Note the
LastGenOutput
andStronglyTypedFileName
match. Unfortunately in a recent .NET Core SDK update (I'm now running 3.1.302 on Mac) this started yielding a CS2002 warning:CSC : warning CS2002: Source file 'TracerMessages.Designer.cs' specified multiple times [/Users/tillig/dev/autofac/Autofac.Diagnostics.DotGraph/src/Autofac.Diagnostics.DotGraph/Autofac.Diagnostics.DotGraph.csproj]
The only way to work around it was to remove the checked-in Designer.cs file and switch to the$(IntermediatePath)
in theStronglyTypedFileName
as seen in the initial issue comment.<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>$(IntermediateOutputPath)/TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
This issue is the only place I've found any of this documented. It'd be nice if this was more straightforward to work with in a non-Visual-Studio (i.e., VS Code / all-command-line) environment.
While this solution removes the warning on running
dotnet build
and project compiles normally, it then makes Visual Studio Code show errors in the places that used the strongly typed generated class indicating that it can't be found.Is there no other workaround?
Try changing $(IntermediateOutputPath)
to:
<StronglyTypedFileName>Properties/Something.Designer.cs</StronglyTypedFileName>
.
I was successful for a while doing this:
<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
Note the
LastGenOutput
andStronglyTypedFileName
match. Unfortunately in a recent .NET Core SDK update (I'm now running 3.1.302 on Mac) this started yielding a CS2002 warning:CSC : warning CS2002: Source file 'TracerMessages.Designer.cs' specified multiple times [/Users/tillig/dev/autofac/Autofac.Diagnostics.DotGraph/src/Autofac.Diagnostics.DotGraph/Autofac.Diagnostics.DotGraph.csproj]
The only way to work around it was to remove the checked-in Designer.cs file and switch to the$(IntermediatePath)
in theStronglyTypedFileName
as seen in the initial issue comment.<ItemGroup> <EmbeddedResource Update="TracerMessages.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>TracerMessages.Designer.cs</LastGenOutput> <StronglyTypedFileName>$(IntermediateOutputPath)/TracerMessages.Designer.cs</StronglyTypedFileName> <StronglyTypedLanguage>CSharp</StronglyTypedLanguage> <StronglyTypedNamespace>Autofac.Diagnostics.DotGraph</StronglyTypedNamespace> <StronglyTypedClassName>TracerMessages</StronglyTypedClassName> </EmbeddedResource> </ItemGroup>
This issue is the only place I've found any of this documented. It'd be nice if this was more straightforward to work with in a non-Visual-Studio (i.e., VS Code / all-command-line) environment.
While this solution removes the warning on running
dotnet build
and project compiles normally, it then makes Visual Studio Code show errors in the places that used the strongly typed generated class indicating that it can't be found. Is there no other workaround?Try changing
$(IntermediateOutputPath)
to:<StronglyTypedFileName>Properties/Something.Designer.cs</StronglyTypedFileName>
.
That will again cause the CSC : warning CS2002: Source file 'Properties/Something.Designer.cs' specified multiple times error again.
I think this issue should probably be renamed and escalated.
There is currently no method to have some developers use VSCode and some developers use Visual Studio on the same codebase and still use "resx" files.
They are just simply incompatible.
VisualStudio will attempt to overwrite the designer files whenever it feels like it and those updated designer files will always be different than what the ResXFileCodeGenerator produces (it has a version in it that it doesn't normally). They will always be in the same location as the resx file itself and there is no way to change the behavior Visual Studio.
The suggested work around in this issue won't work in that case either as VS will always generate the files next to the resx causing this issue.
Unfortunately this is a problem spread across three different projects with inconsistent behavior leading me to believe they will never get fixed unless they all just duplicate the behavior of VisualStudio
There's a nice write-up of this issue by @tillig at https://www.paraesthesia.com/archive/2022/09/30/strongly-typed-resources-with-net-core/. It would be good to see an SDK feature for this to get simple, consistent behavior in Visual Studio and VS Code.
There's a nice write-up of this issue by @tillig at https://www.paraesthesia.com/archive/2022/09/30/strongly-typed-resources-with-net-core/. It would be good to see an SDK feature for this to get simple, consistent behavior in Visual Studio and VS Code.
I'll add to this. Changing the line <Generator>ResXFileCodeGenerator</Generator>
to <Generator>MSBuild:Compile</Generator>
then adding <CoreCompileDependsOn>PrepareResources;$(CompileDependsOn)</CoreCompileDependsOn>
in the properties fully delegates the job to MSBuild and prevents conflicts with Visual Studio
I updated my blog article with the info from @Arthri - thanks! https://www.paraesthesia.com/archive/2022/09/30/strongly-typed-resources-with-net-core/
I wonder if it'd be interesting/helpful to have some sort of "current workaround" complete code example pinned in here somehow. It's really hard to mentally apply all the incremental changes/updates noted here to get to a "complete solution" that works. (Which, I guess, is the whole point of this issue, but scrolling through this issue is almost as hard as the issue itself.)
@tillig I updated the OP with @Arthri's changes.
Thanks for the updates. I now have a PR open that makes it work, which I am very happy about. However, there is a difference between the update of @rainersigwald and the blog post by @tillig , which is the LastGenOutput
part. Can you advise what this does and what considerations to make whether to use it or not please?
LastGenOutput
is not used by the build itself, and I believe it can be dropped now, but there may be some Visual Studio scenario where it is relevant--nothing stood out to me from a quick search of the internal codebase but that's not a guarantee. I'd leave it out and wait for further information myself, but I don't know what harm it would cause to leave it in.
I've been leaving LastGenOutput and other boilerplate there for the reason that the VS resx designer will reinsert it anyway on each save, and I don't want to have to undo .csproj changes each time. (Same with the .settings
designer.) I haven't tried this MSBuild:Compile
generator yet though.
FWIW, Arcade has a solution for this (used by MSBuild itself AFAICT) which further points at https://github.com/dotnet/sdk/issues/94 as an existing issue tracking this problem.
@mhutch, MSBuild itself does not use the Arcade reimplementation (in fact we do not use strongly typed resources at all).
I believe the Arcade reimplementation of the MSBuild feature was done because its authors were unaware of the MSBuild feature.
@mhutch, MSBuild itself does not use the Arcade reimplementation (in fact we do not use strongly typed resources at all).
Ah you're right, we had to do that to consume a source package that requires the Arcade approach. That's a bug in the package IMO.
Expanded on @tillig's approach and made it a general-purpose drop-in for Directory.Build.targets
:
<Project>
<PropertyGroup>
<!-- For VSCode/Razor compat -->
<CoreCompileDependsOn>PrepareResources;$(CoreCompileDependsOn)</CoreCompileDependsOn>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Update="@(EmbeddedResource)">
<Generator>MSBuild:Compile</Generator>
<StronglyTypedFileName>$(IntermediateOutputPath)\$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.'))%(Filename).g$(DefaultLanguageSourceExtension)</StronglyTypedFileName>
<StronglyTypedLanguage>$(Language)</StronglyTypedLanguage>
<StronglyTypedNamespace Condition="'%(RelativeDir)' == ''">$(RootNamespace)</StronglyTypedNamespace>
<StronglyTypedNamespace Condition="'%(RelativeDir)' != ''">$(RootNamespace).$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.').TrimEnd('.'))</StronglyTypedNamespace>
<StronglyTypedClassName>%(Filename)</StronglyTypedClassName>
</EmbeddedResource>
</ItemGroup>
</Project>
Expanded on @tillig's approach and made it a general-purpose drop-in for
Directory.Build.targets
:<Project> <PropertyGroup> <!-- For VSCode/Razor compat --> <CoreCompileDependsOn>PrepareResources;$(CoreCompileDependsOn)</CoreCompileDependsOn> </PropertyGroup> <ItemGroup> <EmbeddedResource Update="@(EmbeddedResource)"> <Generator>MSBuild:Compile</Generator> <StronglyTypedFileName>$(IntermediateOutputPath)\$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.'))%(Filename).g$(DefaultLanguageSourceExtension)</StronglyTypedFileName> <StronglyTypedLanguage>$(Language)</StronglyTypedLanguage> <StronglyTypedNamespace Condition="'%(RelativeDir)' == ''">$(RootNamespace)</StronglyTypedNamespace> <StronglyTypedNamespace Condition="'%(RelativeDir)' != ''">$(RootNamespace).$([MSBuild]::ValueOrDefault('%(RelativeDir)', '').Replace('\', '.').Replace('/', '.').TrimEnd('.'))</StronglyTypedNamespace> <StronglyTypedClassName>%(Filename)</StronglyTypedClassName> </EmbeddedResource> </ItemGroup> </Project>
I believe this generates classes for Resources.en-US.resx
along with Resources.resx
which might not be intended
<CoreCompileDependsOn>PrepareResources;$(CoreCompileDependsOn)</CoreCompileDependsOn>
FWIW, this seems to cause a recursive overflow in the inner markup build when UseWpf
is true
.
<CoreCompileDependsOn>PrepareResources;$(CoreCompileDependsOn)</CoreCompileDependsOn>
FWIW, this seems to cause a recursive overflow in the inner markup build when
UseWpf
istrue
.
Can confirm - I've circumvented the recursion by calling PrepareResources
as an InitialTarget
instead:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop" InitialTargets="PrepareResources">
But I end up with the CS2002 errors mentioned previously so it likely doesn't actually work š«
Does removing it work? I suspect WPF already has a similar setting
Is there any solution to this for WPF projects?
I managed to get WPF projects building via:
<PropertyGroup Condition="'$(UseWPF)' == 'true'">
<!-- Ensure WPF apps generate RESX designer files with namespace not taken from wpftmp file. -->
<RootNamespace Condition="$(RootNamespace.EndsWith('_wpftmp'))">$(_TargetAssemblyProjectName)</RootNamespace>
<!-- Ensure WPF apps invoke the RESX generator -->
<CoreCompileDependsOn>$(CoreCompileDependsOn);SplitResourcesByCulture;CreateManifestResourceNames;CoreResGen</CoreCompileDependsOn>
</PropertyGroup>
From https://github.com/microsoft/msbuild/issues/2272#issuecomment-532264659 cc @rainersigwald
Steps to reproduce
Expected behavior
Build works
Actual behavior
Build fails due to errors resulting from Strings.Designer.cs not being created.
It seems one needs to apply the following non-intuitive changes to the
Engine.csproj
:The
StronglyTypedNamespace
is due to the following in the csproj:<RootNamespace>Microsoft.Windows.PowerShell.ScriptAnalyzer</RootNamespace>
However, having to supply all the other additional parameters seems unintuitive. I'd like to see a minimal solution for a csproj that also works with VS btw.Environment data
msbuild /version
output:OS info:
Windows 10 1809 .Net Core 3.0-rc1