nuget

How to debug NuGet package scripts

You can step by step debug your Init.ps1, Install.ps1, or Uninstall.ps1 using NuGetDebugTools. It won’t be the best debugging experience you’ll ever have in your life, but it will surely get the job done!

At first you need to open the Package Manager Console in Visual Studio and:

  1. install the NuGetDebugTools package
  2. add the debugger
  3. set breakpoints for the scripts  you want to debug

Now you’re all set and you can begin installing your own package. Depending on which breakpoints you’ve set with the Set-PSBreakpoint command, when a break point gets hit you will be prompted with a dialog. This is where you will be entering your commands debugging commands (e.g. s for step into, v for step over)

 

NuGetDebugTools_cmdprompt

Dialog for entering debugging commands

The basic commands that you can enter are

  • s – StepInto (Step to the next statement into functions, scripts, etc)
  • v – StepOver (Step to the next statement over functions, scripts, etc)
  • o – StepOut (Step out of the current function, script, etc)
  • c – Continue (Continue operation, also on Cancel or empty)
  • q – Quit (Stop operation and exit the debugger)
  • ?, h – Help (Write this help message)
  • k – CallStack (Write call stack, aka Get-PSCallStack)
  • K – CallStackDetailed (Write detailed call stack using Format-List)

And you will be receiving feedback and additional information in the Package Manager Console

NuGetDebugTools_output
You can find an exhaustive list of commands and more detailed information on the NuGetDebugTools github page

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).

Running PowerShell scripts during NuGet package installation and removal

Your NuGet package can contain PowerShell scripts which, based on a convention, will be called during the package installation/removal process.

  • Init.ps1 runs the first time a package is installed in a solution
  • Install.ps1 runs when a package is installed in a project <- no longer supported in Visual Studio 2017 (read more)
  • Uninstall.ps1 runs every time a package is uninstalled <- no longer supported in Visual Studio 2017 (read more)
  • you can read more about it here

Your scripts should begin with the following line

param($installPath, $toolsPath, $package, $project)

Where

  • $installPath path to where the project is installed
  • $toolsPath path to the extracted tools directory
  • $package information about the currently installing package
  • $project reference to the EnvDTE project the package is being installed into

In order to be executed, the scripts need to be included in the tools directory of the nuget package. You can achieve this by editing your *.nuspec file and adding a file reference.

<?xml version="1.0"?>
<package >
  <metadata>...</metadata>
  <files>
	<file src="scripts\NugetScripts\Install.ps1" target="tools\Install.ps1" />
  </files>
</package>

You will quickly find debugging to be fairly painful if you need to operate with the $project reference. In this case, you are highly encouraged to test all your $project actions in the Visual Studio Package Manager Console by writing

$project = Get-Project

Now you can interactively test your operations in the console, and you don’t have to constantly pack and install/uninstall your package to check if the scripts work as intended.