Set content files to "copy local : always" in a nuget package

Johnny5 picture Johnny5 · Jan 15, 2014 · Viewed 35.4k times · Source

I generate a nuget package from a project with this command in the post-build event. the variable %conf% is set to the right configuration (debug or release) and %1 is the project name (e.g. "MyCompany.MyProject").

nuget pack -Prop Configuration=%conf% "%1.csproj" -exclude *.sql -IncludeReferencedProjects

This package is for our own usage only, it will never be published on nuget. It ends in our private repository.

In the project, there is a file that is set to generate action : content and copy local : always. (My Visual Studio is in French, so I'm not 100% sure of the translation). Let's name it importantfile.xml.

In the generated package, I end up with this structure :

- content
    - importantfile.xml
- lib
    -net45 (.NetFramework,Version=v4.5)
        -MyCompany.MyProject.dll

Which is fine, I want importantfile.xml to be deployed in the package, because, well, this file is important!

When I install the package in another project, importantfile.xml is deployed at the root of the project. That's OK. But it is not set to copy local : always.

I need importantfile.xml to be copy local : always in this project where I install my package.

How can I achieve that?

Notes :

I can set copy local : always on the file just after installing the package, that's no big deal. I would live with it if later updates of the package would let this property as-is, which is not the case. When updating the package, copy local is reset to never (as stated here).

There's a nuspec file in the project's folder, here it is :

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <copyright>Copyright 2014</copyright>
    <tags>some random tags</tags>
  </metadata>
</package>

Answer

kjbartel picture kjbartel · May 22, 2015

Instead of using a PowerShell script another approach is to use an MSBuild targets or props file with the same name as the package id:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <None Include="$(MSBuildThisFileDirectory)importantfile.xml">
      <Link>importantfile.xml</Link>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

In the nuspec file then, instead of adding the required files to the Content directory, add them to the Build directory along with the targets file.

  • Build
    • importantfile.xml
    • MyPackage.targets
  • lib
    • net45
      • MyAssembly.dll

If you require different content for different architectures then you can add architecture folders under Build also each with their own targets file.

Benefits to using a targets file over the PowerShell script with NuGet Content directory:

  • required content files aren't shown in the project in Visual Studio
  • content files are linked to rather than copied into the directory of each project which references the NuGet package (preventing there being multiple copies and keeping behaviour the same as for assemblies / libraries from NuGet packages)
  • PowerShell scripts only work in Visual Studio and aren't run when NuGet is run from the commandline (build servers, other IDEs and other OS), this approach will work everywhere
  • PowerShell install scripts are not supported in NuGet 3.x project.json system.