visual-studiovisual-studio-2012nugetnuget-packagecopy-local

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


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>

Solution

  • You can use PowerShell and the Install.ps1 hook provided by NuGet.

    See the documentation.

    Via PowerShell you have to 'search' for the content element which includes your importantfile.xml in an attribute. When the script found it, it has to add <CopyToOutputDirectory>Always</CopyToOutputDirectory> as a child element.

        <Content Include="importantfile.xml">
          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
    

    You can find some PowerShell snippets here. Just take a look at the .ps1 files.

    You could try the following (not tested). The file has to be named Install.ps1 and copied into the tools folder:

    param($installPath, $toolsPath, $package, $project)
    
    # Load project XML.
    $doc = New-Object System.Xml.XmlDocument
    $doc.Load($project.FullName)
    $namespace = 'http://schemas.microsoft.com/developer/msbuild/2003'
    
    # Find the node containing the file. The tag "Content" may be replace by "None" depending of the case, check your .csproj file.
    $xmlNode = Select-Xml "//msb:Project/msb:ItemGroup/msb:Content[@Include='importantfile.xml']" $doc -Namespace @{msb = $namespace}
    
    
    #check if the node exists.
    if($xmlNode -ne $null)
    {
        $nodeName = "CopyToOutputDirectory"
    
        #Check if the property already exists, just in case.
        $property = $xmlNode.Node.SelectSingleNode($nodeName)
        if($property -eq $null)
        {
            $property = $doc.CreateElement($nodeName, $namespace)
            $property.AppendChild($doc.CreateTextNode("Always"))
            $xmlNode.Node.AppendChild($property)
    
            # Save changes.
            $doc.Save($project.FullName)
        }
    }
    

    You should also check if everything is removed completely when uninstalling the package.

    Note by Jonhhy5

    When updating the package via update-package, Visual Studio warns that the project is modified "outside the environnment". That's caused by $doc.Save($project.FullName). If I click reload before the command is fully terminated, it sometimes causes errors. The trick is to leave the dialog there until the process finishes, and then reload the projects.