Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit includes of resource files should include appropriate metadata #3764

Open
dsplaisted opened this issue Jul 20, 2018 · 24 comments
Open
Assignees
Labels
Feature-Project-File-Simplification Related to cleaning up and simplification of the project format. Triage-Approved Reviewed and prioritized
Milestone

Comments

@dsplaisted
Copy link
Member

From @dsplaisted on May 10, 2017 18:48

Adding a .resx file to an SDK project using Visual Studio results in the following added to the project file:

  <ItemGroup>
    <Compile Update="Resource1.Designer.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>Resource1.resx</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Resource1.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resource1.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

Ideally, nothing would be added to the project file when you add a .resx to the project. We may be able to do this in the SDK just by setting the appropriate metadata on the implicit EmbeddedResource items and the Compile items for .Designer.cs files.

Copied from original issue: dotnet/sdk#1199

@dsplaisted dsplaisted self-assigned this Jul 20, 2018
@dsplaisted
Copy link
Member Author

From @wli3 on September 14, 2017 0:48

It need a way to link each other, by convention? I am thinking start from blah.resx, and look for in @(Compile), if there is a file matches blah.Designer.cs then "link" them.

@dsplaisted
Copy link
Member Author

Related #1441

@dsplaisted
Copy link
Member Author

From @wli3 on September 20, 2017 17:12

(Some what related microsoft/VSProjectSystem#169 #1467 (comment))

@dsplaisted
Copy link
Member Author

From @davkean on September 26, 2017 0:39

I've done some research on what some of this metadata is used for:

  • LastGenOutput:
    • Used by legacy project system to delete the file when generation fails or when the generator is removed.
    • Used by legacy project system as the document to populate the error list when generator fails. Not needed if we auto-gen the file in obj.
  • DesignTime:
    • Used by legacy project system to determine when it's a TempPE input, which is a file that used for WinForms designer so that it has "access" to the types in the project (for binding, etc I think). Not needed at the moment but we'll probably need to revisit when we bring up WinForms.
  • AutoGen:
    • Used by legacy project system and extensions to avoid manipulating these files in rename, copy and other manipulations because we're touching their "parent" which already affects it. Don't need if we generate in obj.
  • DependentUpon:
    • Used to group the file under the specified parent. Don't need if we generate in obj.

Conclusion, we can remove all metadata from the designer file.

@dsplaisted
Copy link
Member Author

From @wli3 on July 19, 2018 18:46

@dsplaisted @davkean sorry for the long delay. Conclude from David's research, can we close this issue?

@dsplaisted
Copy link
Member Author

From @davkean on July 19, 2018 23:31

We never removed the metadata, so the bug is still relevant. We should move to project system.

@Pilchie
Copy link
Member

Pilchie commented Jul 23, 2018

@davkean is this distinct from #1158?

@Pilchie Pilchie added this to the 16.0 milestone Jul 23, 2018
@Pilchie Pilchie added Feature-Project-File-Simplification Related to cleaning up and simplification of the project format. Project-System-CPS labels Jul 23, 2018
@jjmew jjmew modified the milestones: 16.0, 16.X Feb 6, 2019
@jjmew jjmew added the Triage-Approved Reviewed and prioritized label Feb 6, 2019
@cartermp
Copy link
Contributor

cartermp commented Sep 10, 2019

Is the result of this isseu a failure to nest files in the solution explorer?

Using this explicitly:

    <EmbeddedResource Update="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
      <SubType>Designer</SubType>
    </EmbeddedResource>
    <Compile Update="Properties\Resources.Designer.cs">
      <AutoGen>True</AutoGen>
      <Dependent

image

But commenting that out yield this:

image

@zastrowm
Copy link

zastrowm commented Apr 6, 2020

Is there a workaround for this? I attempted to follow this stackoverflow answer to implicitly add the metadata, but then VS still doesn't seem to nest or autogenerate the file as needed.

@dsplaisted
Copy link
Member Author

I think that in recent VS releases, the items in solution explorer should now be automatically nested appropriately without any explicit metadata in the project file. Is this correct @davkean?

@zastrowm
Copy link

zastrowm commented Apr 6, 2020

It doesn't seem to be the behavior that I have observed, using VS2019 16.5 and donetsdk 3.1.201 - at least for a Microsoft.NET.Sdk project.

I did determine that you can use the stackoverflow answer, but if you're intending to share the settings, it must be done via Directory.Build.targets instead of Directory.Build.props - which is where I first attempted to use it and it didn't work.

@jnm2
Copy link

jnm2 commented Apr 6, 2020

I don't think it nests yet, and besides that it also adds or fills this back in to the csproj every time you save the .resx file:

  <ItemGroup>
    <Compile Update="Properties\Resources.Designer.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>

    <EmbeddedResource Update="Properties\Resources.resx">
      <Generator>PublicResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

@jnm2
Copy link

jnm2 commented Apr 6, 2020

(By constrast, non-standalone .resx files nest automatically and don't insert pollution into the csproj when edited.)

@davidwengier
Copy link
Contributor

it also adds or fills this back in to the csproj every time you save the .resx file

What version of Visual Studio are you using? #2873 should have solved that in 16.5, where any item metadata will not be written to the project file if it already has that value at that point of the evaluation (ie, if your globbing is in Directory.Build.props or something imported at the start of the project)

@jnm2
Copy link

jnm2 commented Apr 6, 2020

It's reproing in 16.5.2, both the lack of nesting and generating the block I posted above. I'll put a repro project here I guess.

@jnm2
Copy link

jnm2 commented Apr 6, 2020

Use any SDK csproj, such as this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

</Project>

And add this empty .resx file at Properties\Resources.resx, or add one using the Add New Item dialog:

Click to expand
<?xml version="1.0" encoding="utf-8"?>
<root>
	<!-- 
		Microsoft ResX Schema

		Version 1.3

		The primary goals of this format is to allow a simple XML format 
		that is mostly human readable. The generation and parsing of the 
		various data types are done through the TypeConverter classes 
		associated with the data types.

		Example:

		... ado.net/XML headers & schema ...
		<resheader name="resmimetype">text/microsoft-resx</resheader>
		<resheader name="version">1.3</resheader>
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
		<data name="Name1">this is my long string</data>
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
			[base64 mime encoded serialized .NET Framework object]
		</data>
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
		</data>

		There are any number of "resheader" rows that contain simple 
		name/value pairs.

		Each data row contains a name, and value. The row also contains a 
		type or mimetype. Type corresponds to a .NET class that support 
		text/value conversion through the TypeConverter architecture. 
		Classes that don't support this are serialized and stored with the 
		mimetype set.

		The mimetype is used for serialized objects, and tells the 
		ResXResourceReader how to depersist the object. This is currently not 
		extensible. For a given mimetype the value must be set accordingly:

		Note - application/x-microsoft.net.object.binary.base64 is the format 
		that the ResXResourceWriter will generate, however the reader can 
		read any of the formats listed below.

		mimetype: application/x-microsoft.net.object.binary.base64
		value   : The object must be serialized with 
			: System.Serialization.Formatters.Binary.BinaryFormatter
			: and then encoded with base64 encoding.

		mimetype: application/x-microsoft.net.object.soap.base64
		value   : The object must be serialized with 
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
			: and then encoded with base64 encoding.

		mimetype: application/x-microsoft.net.object.bytearray.base64
		value   : The object must be serialized into a byte array 
			: using a System.ComponentModel.TypeConverter
			: and then encoded with base64 encoding.
	-->
	
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
		<xsd:element name="root" msdata:IsDataSet="true">
			<xsd:complexType>
				<xsd:choice maxOccurs="unbounded">
					<xsd:element name="data">
						<xsd:complexType>
							<xsd:sequence>
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
							</xsd:sequence>
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
						</xsd:complexType>
					</xsd:element>
					<xsd:element name="resheader">
						<xsd:complexType>
							<xsd:sequence>
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
							</xsd:sequence>
							<xsd:attribute name="name" type="xsd:string" use="required" />
						</xsd:complexType>
					</xsd:element>
				</xsd:choice>
			</xsd:complexType>
		</xsd:element>
	</xsd:schema>
	<resheader name="resmimetype">
		<value>text/microsoft-resx</value>
	</resheader>
	<resheader name="version">
		<value>1.3</value>
	</resheader>
	<resheader name="reader">
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
	</resheader>
	<resheader name="writer">
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
	</resheader>
</root>

If you didn't use the Add New Item dialog, open the .resx and select either the Internal or Public access modifier to get it to generate the corresponding designer file. Now, save and look at the csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Update="Properties\Resources.Designer.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Properties\Resources.resx">
      <Generator>ResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

</Project>

Why is this there? Surely Internal is the default and there should be nothing at all in the csproj representing that, and Public would be a single piece of metadata added as an attribute to save space.

Here's the result:

image

Now, let's remove these values from the csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

</Project>

The nesting is gone:

image

@jnm2
Copy link

jnm2 commented Apr 6, 2020

Now, let's say I remove what seems to be inessential or should be SDK defaults, but I leave the metadata that appears to be required in order to nest, and I leave the metadata that appears to be required to keep the RESX designer from switching from Internal to (custom):

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Update="Properties\Resources.Designer.cs" DependentUpon="Resources.resx" />
    <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator" />
  </ItemGroup>

</Project>

For one thing, VS constantly tries to auto expand these to fill out the metadata I removed, while I'm typing. It's a little weird. I fight it using Ctrl+Z several times and manage to save.

Now I open the RESX designer, add a single string entry, and click Save. The csproj bloats back up:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Update="Properties\Resources.Designer.cs" DependentUpon="Resources.resx">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
    </Compile>
    <EmbeddedResource Update="Properties\Resources.resx" Generator="ResXFileCodeGenerator">
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>

</Project>

@jnm2
Copy link

jnm2 commented Apr 6, 2020

.settings files act just the same as the description above of .resx files:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Update="Properties\Settings.Designer.cs">
      <DependentUpon>Settings.settings</DependentUpon>
      <DesignTimeSharedInput>True</DesignTimeSharedInput>
      <AutoGen>True</AutoGen>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <None Update="Properties\Settings.settings">
      <Generator>SettingsSingleFileGenerator</Generator>
      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
    </None>
  </ItemGroup>

</Project>

@jnm2
Copy link

jnm2 commented Apr 6, 2020

All the above appears to repro in VS 16.6-p2.1 as well.

@davidwengier
Copy link
Contributor

Sorry, I misunderstood what you were saying originally. I thought you meant you had globs in place that would mean all of that extra info in the project file wasn't needed. Without anything custom involved, indeed the SDK doesn't default any of that info. We do have #5707 logged to improve that situation, but what you're seeing in these comments is what would be expected with the current SDK.

@jnm2
Copy link

jnm2 commented Apr 7, 2020

I'm sorry! Thanks for that issue reference. I'll follow that then.

@cremor
Copy link

cremor commented Apr 7, 2020

So if #5707 handles the request that the metadata should already be added by the SDK, which issue handles the bug that Visual Studio adds the metadata for specifc files again even if all metadata is already correctly set by glob updates? This one?

I did determine that you can use the stackoverflow answer, but if you're intending to share the settings, it must be done via Directory.Build.targets instead of Directory.Build.props - which is where I first attempted to use it and it didn't work.

I also noticed that setting the metadata in glob updates in the Directory.Build.props file doesn't work. Very weird, since this is the correct location for such things, right? Will this also be handled in this issue? Or is there a seperate issue for it?

@davidwengier
Copy link
Contributor

which issue handles the bug that Visual Studio adds the metadata for specifc files again even if all metadata is already correctly set by glob updates?

#2873 fixed things so that metadata would not be written for items if it already matched the value at that point in evaluation, so it would rely on globs being in props and not targets. It sounds like its worth logging a new issue for the problems you had with using a props file to see if there is another bug there.

@onyxmaster
Copy link

It looks like something is off in the project system for the general (non-.resx) case.
Please consider the example -- CodegenProjects. Load it into VS (using 16.7.7 right now), then try to copy the Test.tt file. Also, try adding a new Test2.tt file, then save all files and observe the diff for the project file. It looks like it adds unnecessary files and sometimes even breaks evaluation (adding things like <LastGenOutput>%(Filename).cs</LastGenOutput> instead of <LastGenOutput>Test1 - Copy.cs</LastGenOutput>).

P.S. I'm not sure if this is a perfect issue to add this comment to; I would gladly move it to a new issue if considered a separate one.

@drewnoakes drewnoakes modified the milestones: 16.x, 17.x Oct 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature-Project-File-Simplification Related to cleaning up and simplification of the project format. Triage-Approved Reviewed and prioritized
Projects
None yet
Development

No branches or pull requests