34

I recently moved to Visual Studio 2017 Community Edition. It has 2 nice new features:

  1. You don't need to explicitly include your source files in the csproj. It does this automatically.

  2. It can build NuGet packages directly.

I want to package up my open source CodeFirstWebFramework DLL as a NuGet package. As well as including the DLL, the package has to include a whole directory tree of other files (including .js, .tmpl, .css and .md files).

How do I tell Visual Studio that I want this directory tree included in the package?

From what information I have found with extensive searching, and ignoring all the out-of-date information that involves adding files to the csproj, all I could find was to place them in a contentFiles folder, but this does not seem to work.

My project file looks like this:

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

  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
    <Authors>Nikki Locke</Authors>
    <Company>Trumphurst Ltd</Company>
    <Description>Easy to use web server for building web apps that use sql databases generated from c# classes</Description>
    <Copyright>2017 Trumphurst Ltd.</Copyright>
    <PackageProjectUrl>https://github.com/nikkilocke/CodeFirstWebFramework</PackageProjectUrl>
    <RepositoryUrl>https://github.com/nikkilocke/CodeFirstWebFramework</RepositoryUrl>
    <RepositoryType>Github</RepositoryType>
    <PackageTags>C# SQL Code First Web Server</PackageTags>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Markdig" Version="0.12.1" />
    <PackageReference Include="Mono.Data.Sqlite.Portable" Version="1.0.3.5" />
    <PackageReference Include="mustache-sharp" Version="0.2.10" />
    <PackageReference Include="MySql.Data" Version="6.9.9" />
    <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="System.Net" />
    <Reference Include="System.Web" />
  </ItemGroup>

</Project>

Wildly guessing what I might need to do from the conflicting and out-of-date information on the web, I have added the following:

  <ItemGroup>
    <Content Include="contentFiles/**/*.*" copyToOutput="true">
      <IncludeInPackage>true</IncludeInPackage>
    </Content>
  </ItemGroup>

Now the .nupkg file has the contents of the contentFiles folder inside it (in two places, a content folder, and a contentFiles folder).

However, when I install the package in another project, the content files do not appear, although they are listed in the project.assets.json file in the obj folder.

Pang
  • 9,564
  • 146
  • 81
  • 122
Nikki Locke
  • 2,759
  • 6
  • 29
  • 53
  • 1
    *you don't need to explicitly include your source files in the csproj, it does this automatically, and it can build nuget packages directly* - where did you read that? This doco (it's a .net core example not sure if you're targeting full) has a few steps https://learn.microsoft.com/en-us/nuget/guides/create-net-standard-packages-vs2017 have you added the DLL to project and Build Output Dir or is it referenced in the csproj? – Jeremy Thompson Jun 12 '17 at 11:56
  • e.g. http://www.natemcmaster.com/blog/2017/03/09/vs2015-to-vs2017-upgrade/ – Nikki Locke Jun 12 '17 at 12:37
  • "have you added the DLL to project and Build Output Dir or is it referenced in the csproj?" - don't understand the question. What dll? – Nikki Locke Jun 13 '17 at 07:19
  • Looks like you are using .Net Core, is the DLL referenced as a dependency? (Some times when you use unmanaged DLLs you add them to project, not via reference but instead just adding the file and set build to output so it ends up in the bin folder). Where did you get up to with the link I provided? – Jeremy Thompson Jun 13 '17 at 07:21
  • BTW the article in your comment clearly says: *Disclaimer: this only works for a small set of project types. class library projects console apps ASP.NET Core web apps .NET Core* - I'm guessing yours IS a class library, is that correct? – Jeremy Thompson Jun 13 '17 at 07:27
  • Yes, I am building a class library (i.e. a dll). I still don't know what dll you are referring to in your comments. I am building it for .Net 4.5. – Nikki Locke Jun 13 '17 at 13:21
  • Any other way of building the DLL, and adding a load of other files to the nuget package, such that they get added to any project that references the nuget package would be just as good - it's just the documentation is so confusing and contradictory that I don't know how to proceed. – Nikki Locke Jun 13 '17 at 13:23

6 Answers6

13

I have now changed my project file again, so it now reads:

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

  <PropertyGroup>
    <TargetFramework>net45</TargetFramework>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
    <Authors>Nikki Locke</Authors>
    <Company>Trumphurst Ltd</Company>
    <Description>Easy to use web server for building web apps that use sql databases generated from c# classes</Description>
    <Copyright>2017 Trumphurst Ltd.</Copyright>
    <PackageProjectUrl>https://github.com/nikkilocke/CodeFirstWebFramework</PackageProjectUrl>
    <RepositoryUrl>https://github.com/nikkilocke/CodeFirstWebFramework</RepositoryUrl>
    <RepositoryType>Github</RepositoryType>
    <PackageTags>C# SQL Code First Web Server</PackageTags>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Markdig" Version="0.12.1" />
    <PackageReference Include="Mono.Data.Sqlite.Portable" Version="1.0.3.5" />
    <PackageReference Include="mustache-sharp" Version="0.2.10" />
    <PackageReference Include="MySql.Data" Version="6.9.9" />
    <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
  </ItemGroup>

  <ItemGroup>
    <Reference Include="System.Net" />
    <Reference Include="System.Web" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="contentFiles/**/*.*" copyToOutput="true">
      <IncludeInPackage>true</IncludeInPackage>
      <CopyToOutput>true</CopyToOutput>
      <BuildAction>Content</BuildAction>
      <copyToOutput>true</copyToOutput>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <Compile Remove="Phone\**" />
    <EmbeddedResource Remove="Phone\**" />
    <None Remove="Phone\**" />
  </ItemGroup>

</Project>

The contentFiles are now successfully copied into a folder of the same name (i.e. contentFiles) in any project that downloads the NuGet package - not sure which directive worked, but one of them did.

The content is not automatically copied to the project's output folder on compile, unfortunately - suggestions welcome.

Pang
  • 9,564
  • 146
  • 81
  • 122
Nikki Locke
  • 2,759
  • 6
  • 29
  • 53
  • 7
    > The content is not automatically copied to the project's output folder on compile, unfortunately - suggestions welcome. @Nikki Locke Take a look at https://stackoverflow.com/questions/44073501/how-do-you-set-nuget-contentfiles-copytooutput-value-to-true-when-using-a-net-s You need to add the undocumented `PackageCopyToOutput` switch to mark the nuget content file as 'CopyToOutput` in projects referencing it: ``` true ``` See PR https://github.com/NuGet/NuGet.Client/pull/1450 – luwei Jul 24 '19 at 01:24
8

First, please be aware that you need to put your files either into a content or contentFiles folder depending on how you NuGet package is referenced. See the end of this section.

Basically, if a NuGet project is referenced by package.config file, the files from the content folder of the NuGet package will be copied to the referencing project. If the NuGet package is referenced by PackageReference in the project file, the files from the contentFiles folder within the package will be used. It is recommended to include both.

The structure of the contentFiles folder can be found here. It is possible to specify the language and the framework for the files to be copied.

So in your case:

<ItemGroup>
    <Content Include="contentFiles/**/*.*">
        <Pack>true</Pack>
        <PackagePath>contentFiles\any\any;content</PackagePath>
    </Content>
</ItemGroup>
Pang
  • 9,564
  • 146
  • 81
  • 122
Fabian
  • 1,100
  • 11
  • 14
  • 1
    thank you for your response. Could you please let me know why it is recommended to include both `contentFiles` and `content ` folders? – SaiyanGirl May 08 '19 at 18:58
  • 2
    The way the nuget package is referenced in the consuming project decides which folder is used. Thus, to make your package usable by both referencing ways, it is recommended to add both. – Fabian May 09 '19 at 06:53
  • Keep in mind that adding both also results in the files being included twice in the package. If you have a particularly large set of static dependencies included this way, you'll basically double the size of your package. – Alexander Trauzzi Jan 31 '22 at 15:32
  • On a separate note, @Fabian, is there any reason to continue using `content` these days with .NET 6? I'm under the impression that all projects will support `contentFiles` now? – Alexander Trauzzi Jan 31 '22 at 15:32
  • 1
    @AlexanderTrauzzi: I have not been confronted with creating .nuget packages for .NET 6 yet. So I cannot give a qualified answer. But I hope/expect that Microsoft cleaned up this mess. Regarding your other comment, yes it doubles the size of your nuget package. This is a pain, but it was the only way I found at the time to allow the different referencing styles. – Fabian Feb 02 '22 at 08:08
3

I got the solution.


Below first snippet of code is not sufficient in order to copy files on target application

<ItemGroup>
<Content Include="contentFiles/**/*.*">
    <Pack>true</Pack>
    <PackagePath>contentFiles\any\any;content</PackagePath>
</Content>

Solution-: This will copy files on root folder.

<ItemGroup>
<Content Include="contentFiles/**/*.*">
    <Pack>true</Pack>
    <PackagePath>contentFiles;content</PackagePath>
    <IncludeInPackage>true</IncludeInPackage>
    <CopyToOutput>true</CopyToOutput>
    <BuildAction>Content</BuildAction>
    <copyToOutput>true</copyToOutput>
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    <CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>

This is working for me. Hope it will work for you.

Raghubir Singh
  • 187
  • 1
  • 5
  • hmm, this looks like the most promising solution, but it doesn't work for me with the latest VS. The content files do show up in the packages/MyNugetPkg/.../Content and contentFiles folders, my nuspec has the BuildAction specified but not any 'Copy...' flag specified. And even if I edit the nuspec with the 'Copy...' flags, it still isn't copied to output folder. I'm wondering if getting the content will have to be a separate manual download on the part of the developer (gag) – Digiproc Oct 18 '20 at 19:36
  • ...or maybe I'll have to manage it all programmatically the first time my library runs. – Digiproc Oct 18 '20 at 19:43
  • CopyToOutputDirectory=Always entirely breaks incremental builds in MSBuild/VS. Plz don't do that. Use PreserveNewst. – kzu Jan 25 '22 at 21:32
1

Wrapping in "Content" and "IncludeInPackage" is good enough to copy the file(s) to the package.

user3724031
  • 195
  • 2
  • 7
0

Addition to above

Below image has my project contentFiles folder structure

Attached code image has my contentFiles folder structure

Design of my .csproj file

my .Csproj file

Raghubir Singh
  • 187
  • 1
  • 5
0

I did following to simply put content in the Nuget package.

<ItemGroup>
  <Content Include="myContentFolder/**/*.*">
    <Pack>true</Pack>
    <PackagePath>contentFiles\any\any;content</PackagePath>
  </Content>
</ItemGroup>

When I want content of myContentFolder to be inside build output, I simply add <PackageCopyToOutput>true</PackageCopyToOutput>. Like this

<ItemGroup>
  <Content Include="myContentFolder/**/*.*">
    <Pack>true</Pack>
    <PackagePath>contentFiles\any\any;content</PackagePath>
    <PackageCopyToOutput>true</PackageCopyToOutput>
  </Content>
</ItemGroup>

Here code which do this magic. https://github.com/NuGet/NuGet.Client/blob/81a398e094281bcf00634252f0b460faffce3e55/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs#L749-L756

codevision
  • 5,165
  • 38
  • 50