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

Provide an option to hide content files from project tree in new-style csproj #4856

Open
davidmatson opened this issue Mar 20, 2017 · 29 comments
Labels
Area:ContentFiles PackageReference contentFiles folder Priority:2 Issues for the current backlog. Product:VS.Client Type:Feature

Comments

@davidmatson
Copy link

I'm currently using VS 2015 with project.json with a number of sources-style NuGet packages (packages that only have contentFiles with c# code as *.cs files).

There are quite a few files that come in this way, so having them not show up in the project root is key. When I try migrating to the new-style csproj, that's what happens, so I'm currently blocked (stuck back in project.json-style NuGet).

Two possible solutions here (I'm sure there are others as well):

  1. Change back the default for contentFiles to be hidden from the csproj like they were with project.json.
  2. Provide a per-csproj setting to turn off showing referenced packages' contentFiles in the project tree.

If there's a workaround I could use at the moment (perhaps getting the files to appear somewhere nested under a project folder rather than showing up directly in the project root), that would be great to know as well. (Hopefully long-term there is a way to control where these files appear, unless the default changes back. Project root-only doesn't seem sufficient for a large number of content files.)

@davidmatson
Copy link
Author

See also #4166 which may be related.

@davidmatson
Copy link
Author

I think there's a slightly broader question here to consider as well - if contentFiles do appear, how should they show up in the project tree? One possible option (my preference) would be to have them appear underneath the Dependencies -> NuGet node in the project (show the .cs files directly underneath the name of the package that includes them).

There may be other/better options as well. Having the ability to show these files is nice for setting breakpoints, but IMO we should have something better than just dumping everything in the project root.

@davidmatson
Copy link
Author

I'm currently using the following workaround (thanks to @emgarten for the idea):

  <ItemGroup>
    <Compile Update="@(Compile)">
      <Visible Condition="'%(NuGetItemType)' == 'Compile'">false</Visible>
    </Compile>
  </ItemGroup>

@davidmatson
Copy link
Author

Here's a fairly simple option that might improve things:
The props file currently does something like this:

    <Compile Include="$(NuGetPackageRoot){package ID}\{package version}\contentFiles\cs\net45\{relative path}" Condition="Exists('$(NuGetPackageRoot){package ID}\{package version}\contentFiles\cs\net45\{relative path}')">
      <NuGetPackageId>{package ID}</NuGetPackageId>
      <NuGetPackageVersion>{package version}</NuGetPackageVersion>
      <NuGetItemType>Compile</NuGetItemType>
      <Private>False</Private>
      <Link>{relative path}</Link>
    </Compile>

Note that the link item just has the relative path of the cs file. Add some prefix to the link item, such as "NuGetContent{package Id}":

      <Link>NuGetContent\{package Id}\{relative path}</Link>

Unfortunately, I think this approach would only work item types like Compile that aren't binplaced; items that are copied to the output directory would get messed up if they have some kind of prefix added to them, because it would affect the output path.

@davidmatson
Copy link
Author

The idea of the prefix was inspired by this technique from the pre-NuGet 3 days:
https://nikcodes.com/2013/10/23/packaging-source-code-with-nuget/

Though I'd suggest omitting the version from the path as only one version of a package can be installed anyway (and making the path longer might contribute to MAX_PATH issues; not sure if that matters in a Link though).

@rrelyea rrelyea added the Area:ContentFiles PackageReference contentFiles folder label Mar 21, 2017
@davidmatson
Copy link
Author

Actually it looks like there's a more fundamental problem here and something like the NuGetContent approach mentioned above is essential to correct behavior, due to collisions with multiple packages using the same file name. See #5048 for details.

@tmat
Copy link

tmat commented Jun 13, 2017

I'm using the above workaround like so:

<Compile Update="@(Compile)">
  <Link Condition="'%(NuGetPackageId)' != ''">%(NuGetPackageId)\%(Link)</Link>
</Compile> 

This serves two purposes:

  1. It's nicer to not have a bunch of links from referenced packages pollute the project root.
  2. Correctness in case there are two files of the same name in the referenced packages.

This should imo be the default.

@emgarten emgarten added this to the Future-0 milestone Jun 13, 2017
@emgarten emgarten modified the milestones: Future-0, Backlog Oct 17, 2017
@emgarten emgarten added the Priority:3 Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog. label Oct 17, 2017
@leak
Copy link

leak commented Dec 5, 2017

What is the state of this issue?
MySql.Data is shipping tons of content files that are polluting my solution explorer in VS 2017. I cannot remove or edit those file and folder links in any way without getting tons of errors.

@emgarten
Copy link
Member

emgarten commented Dec 5, 2017

@leak use @tmat's example to remove the links.

@leak
Copy link

leak commented Dec 6, 2017

I did and changed it to "Content", but it does not get rid of folders for me.

@mscrivo
Copy link

mscrivo commented Jun 28, 2018

Are the above workarounds supposed to work for dependencies of a referenced nuget package? They don't seem to.

For example, I am referencing the CefSharp.Common package which has a dependency on CefSharp.Redist.x64 which has a bunch of compiled C++ libs that are to be pulled into the bin folder upon building, but are also showing up in the root of my project structure in Solution Explorer, and cannot figure out how to make those go away.

@amaitland
Copy link

For example, I am referencing the CefSharp.Common package which has a dependency on CefSharp.Redist.x64 which has a bunch of compiled C++ libs that are to be pulled into the bin folder upon building, but are also showing up in the root of my project structure in Solution Explorer, and cannot figure out how to make those go away.

For reference the CefSharp Items are included as <None/> entries, perhaps you can try dotnet/project-system#1126 (comment)

@Barsonax
Copy link

Barsonax commented Jun 15, 2020

We are also running into this in our .net based game engine in our sample packages that also include files like shaders, textures etc:
84692048-81974480-af45-11ea-9f32-1565373e3615

This is very confusing behavior and I would like these files to be either:

  • Hidden from the solution explorer.
  • Moved to the dependencies node under the respective package.

I also noticed that these files are editable and its possible to save these edits which shouldn't be possible as they are supposed to be readonly. This is a quite serious bug as this package could possibly be shared with other projects!!

@JVimes
Copy link

JVimes commented Jul 15, 2020

I love David's idea of showing content files under their package in Dependencies:

image

@Kryptos-FR
Copy link

Kryptos-FR commented Jul 20, 2020

Copying my proposal from dotnet/project-system#6290 (comment)

Whether a file is "visible" or not shouldn't even be a responsibility of the package/library maker. Some users of those packages might decide they want to display, some might not. So there should be an option to show/hide this on a per-project basis.

Look at dotnet/project-system#3302 for an idea of the mess it can be. I have to scroll down more than 60 items before seeing the first class of my project.

So here is a proposal:

<PackageReference Include="ThirdParty" Version="1.0">
    <ContentFilter Include="*.config" Visible="False" />
    <ContentFilter Include="*.dll" Visible="False" />
</PackageReference>

Note that this proposal is compatible with the above proposal to show those files under the package node.

@kzu
Copy link

kzu commented Oct 8, 2020

I think making contentFiles visible by default under the dependency node makes the most sense.

@drewnoakes
Copy link

@kzu since VS 16.7, contentFiles should be already be visible under package nodes. This was merged in dotnet/project-system#6066, though the implementation has since moved to NuGet.Client.

This issue is just tracking removal of content files from the rest of the tree.

@StingyJack
Copy link
Contributor

I think making contentFiles visible by default under the dependency node makes the most sense.

if I cant change them or use them, I dont want to see them.

I've finally been more or less coerced into using package reference style of nuget after holding out with packages.config for as long as I could (3+ years) and yet there are still these obvious, productivity crippling bugs with package reference. How much time does this cost users trying to navigate project structure to "just deal with it", and how much time is it costing for those who tried to troubleshoot before they find this issue report? It cost me few hours already and is still accruing.

@remcoros
Copy link

remcoros commented Mar 10, 2021

Any update on this?

Navigating a project like this is pretty awful:
(this project references a nuget package with a lot of contentFiles)

Adding '<Visible>False</Visible>' to the content item does not seem to do anything (this is what the MSTest package also does, but doesn't have any effect in the solution explorer)

image

@kzu
Copy link

kzu commented Mar 10, 2021

You have turned on "Show All Files", that icon:

image

So that's expected I think.

@remcoros
Copy link

@kzu how embarrassing... I totally missed that.

@StingyJack
Copy link
Contributor

Even so, they aren't part of the project. You can't do anything with them except be confused by them.

@abatishchev
Copy link

4 years later, any progress on this one, please?

@luojunyuan
Copy link

I still waiting for this

@nkolev92 nkolev92 added Product:VS.Client Priority:2 Issues for the current backlog. Type:Feature and removed Triage:NeedsTriageDiscussion Priority:3 Issues under consideration. With enough upvotes, will be reconsidered to be added to the backlog. Pipeline:Icebox labels Sep 26, 2022
@nkolev92 nkolev92 removed this from the Backlog milestone Sep 26, 2022
@AdamVicente
Copy link

This is what worked for me, I used Nuget Package Explorer (NPE) but this should work with other packers as well. Create a new nuget package in NPE. Add a folder "contentFiles" and "Build". In the "contentFiles" folder add a folder "any" and inside that another folder "any." (The any any has to do with the target build, if it does not matter any any is safest). Add all unmanaged DLLs/files in this inner most folder. Edit the metadata source code (.nuspec file) so that it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>Example.Code</id>
    <version>1.0.0</version>
    <title></title>
    <authors>Me</authors>
    <owners>Me</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Description</description>
    <contentFiles>
      <files include="any\any\File1.dll" buildAction="None" copyToOutput="true" flatten="false" />
      <files include="any\any\File2.txt" buildAction="None" copyToOutput="true" flatten="false" />
    </contentFiles>
  </metadata>
</package>

Where all unmanaged files are listed in the content file section. If build action is needed that can be set, make sure copyToOutput is set to True, and flatten can be set to false unless folder structure must be retained.

In the "Build" folder you will need to add a .props file with the same name as your project. The .props file should be set up as following:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <None Include="File1.dll">
      <Visible>false</Visible>
    </None> 
    <None Include="File2.txt">
      <Visible>false</Visible>
    </None> 
  </ItemGroup>
</Project>

Where again you list every file. The Visible false tag will keep these from showing up in the solution explorer in Visual studio. If the build action for a file is not None in the .nuspec file then you may have to use a Compile instead of None ItemGroup.

@tqk2811
Copy link

tqk2811 commented Dec 23, 2022

Thanks for .props file

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<ItemGroup>
		<None Update="@(None)">
			<Visible Condition="'%(NuGetItemType)' == 'None' and '%(NuGetPackageId)' == 'My.Package.Id'">False</Visible>
		</None>
                <Compile Update="@(Compile)">
			<Visible Condition="'%(NuGetItemType)' == 'Compile' and '%(NuGetPackageId)' == 'My.Package.Id'">False</Visible>
		</Compile>
	</ItemGroup>
</Project>

@AdamVicente
Copy link

Now this method will work to add unmanaged files to the bin directory only on project build. If you are sending the executable of your project somewhere else additional steps will be required. The .nuspec file can stay the same with None items for each required file but the items must be EmbeddedResource in the .props file. When you add the nuget file to a project this will add the unmanaged files as an embedded resource for the project. Now before calling these unmanaged DLLs they must be copied to the bin directory. I do this using a constructor on the containing class. Example is in C#:

using System.Reflection;
using System.IO;

public class UnManagedDLL{

//constructor
public UnManagedDLL()
{
            Assembly assem = Assembly.GetExecutingAssembly();
            string[] embededFiles = assem.GetManifestResourceNames();
            string[] unManagedFiles = new string[]{
                "File1.Dll",
                "File2.txt"
                  };
            string outPutDirectory = Path.GetDirectoryName(assem.CodeBase);
            outPutDirectory = outPutDirectory.Replace("file:\\", "");
            foreach (string file in unManagedFiles)
            {
                string path = Path.Combine(outPutDirectory, file);
                    foreach (string eFile in embededFiles)
                    {
                        if (eFile ==  file)
                        {
                            using (Stream fs = assem.GetManifestResourceStream(eFile))
                            {
                                byte[] fsBytes = new byte[fs.Length];
                                fs.Read(fsBytes, 0, fsBytes.Length);
                                File.WriteAllBytes(path, fsBytes);
                            }
                            break;
                        }
                }
            }
}

//variables for unmanaged dll
public int n {get;set;}

//unmanaged dll specific functions
[DllImport("UnManaged.dll", EntryPoint = "Func")]
protected static extern void Func(int n);

}

Note that the IO operations performed here are not thread safe. This will try to write the embedded files to the output directory every time a new instance of the containing class is created. To save time during execution I also check to see if the files already exist and if they do I don't write over them.

@abhinavmathur06
Copy link

6 years later, any progress on this one, please?

@tqk2811
Copy link

tqk2811 commented Dec 5, 2023

6 years later, any progress on this one, please?

you can done it with .props file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area:ContentFiles PackageReference contentFiles folder Priority:2 Issues for the current backlog. Product:VS.Client Type:Feature
Projects
None yet
Development

No branches or pull requests