Modifying the project during NuGet package installation and removal

By default NuGet does a great job installing and referencing all the assemblies present in the ‘lib‘ directory of the NuGet package. Furthermore, if you have other files which need to be added to the solution, you can simply add them to the ‘content‘ directory of the package. The files added to the ‘content‘ directory will be added to the solution with the following properties:

  • BuildAction – Content
  • CopyToOutputDirectory – Do not copy

This is extremely nice if you have images, unmanaged dlls, or other things that just need to be added to the solution, and not referenced.

However, there are times when the default behavior is not enough, and you need control over how your items get added to the project (e.g. if you need to have CopyToOutputDirectory=CopyIfNewer).

Running scripts when installing the package

You will need to add a Install.ps1 script to your package. You can read more on how to do that here.

Now that you added Install.ps1 to your package, it’s time to change some values here and there. Of course, there are several ways this behavior can be achieved, I will start with how NOT to do it, and continue with how to do it.

How NOT to do it

While trying to figure out myself how to achieve this, I found this answer on stackoverflow.com. Unfortunately, I was hasty enough to take that solution as “good enough”, and didn’t scrolled down a few more answers (spoiler alert: one of them contains the correct way of doing it).

I adjusted the PowerShell code with a more “mean” xquery and got this bad boy

The above script loads the project file from the disk, performs some changes to it, and then flushes it back to the disk. Mission accomplished, however, you are actually changing the project “behind the scenes”, and big surprise, Visual Studio is not entirely happy about it:

Project modified outside Visual Studio

Project modified outside Visual Studio

Q: Should I Save, Discard, Overwrite, Ignore?

A: Can I chose more than one option?

If you saved all your changes before running the package installation, in most of the cases you would be able to click Discard
and get away with it, but not all the times. What’s left, is Save As and later on performing a merge of the two project files (highly uncool). Moreover, I wonder how this will work if there are errors while installing the package and a rollback is needed.

How to do it

The parameters that your Install.ps1 script is receiving are:

You can review the details of each parameter here.

The $project is a reference to the EnvDTE project the package is being installed into. This means that you get complete control over the project file in a standard way.

Here are a few examples on how to use it:

CopyToOutputDirectory

Given the following project structure

RoccatSolutionDLL

Changing the CopyToOutputDirectory property of the talkfx-c.dll can be done with the following construct

Note: You need to work your way down the project structure hierarchy until you reach the item you are after (in this case CroccatTalkWrapper/win32-x86/talkfx-c.dll)

The possible values for CopyToOutputDirectory are

  • 0 = Do not copy
  • 1 = Copy always
  • 2 = Copy if newer

BuildAction

Keeping the same project structure as above

Standard BuildAction values are:

  • 0 = None
  • 1 = Compile
  • 2 = Content
  • 3 = EmbeddedResource

Testing

If you need to test your $project manipulations, you can do it in the Visual Studio Package Manager Console

Now you can test all your manipulations in the console.

Compatibility

Starting with Visual Studio 2017 install.ps1 and uninstall.ps1 are no longer supported (read more).

3 comments on “Modifying the project during NuGet package installation and removal

  1. walid March 22, 2016 00:34

    Hey Cosmin,

    Thanks for sharing this. I unfortunately need to do more with some of the content files that I add. for native references I need to create a child in the content tag as such

    native dll
    PreserveNewest

    do you how to do that programmatically? with your method?

    Walid

  2. Cosmin March 22, 2016 19:01

    What you need to do is use the CopyToOutputDirectory example from above (the integer value of 2 means PreserveNewest).

    example:
    $mydll = $project.ProjectItems.Item(“mynative.dll”);
    $mydll.Properties.Item(“BuildAction”).Value = [int]2;

  3. KveX April 10, 2017 17:31

    thank you !

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>