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:
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:
Given the following project structure
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
Keeping the same project structure as above
Standard BuildAction values are:
- 0 = None
- 1 = Compile
- 2 = Content
- 3 = EmbeddedResource
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.
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”
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
do you how to do that programmatically? with your method?
What you need to do is use the CopyToOutputDirectory example from above (the integer value of 2 means PreserveNewest).
$mydll = $project.ProjectItems.Item(“mynative.dll”);
$mydll.Properties.Item(“BuildAction”).Value = [int]2;
thank you !