http://blogs.clariusconsulting.net/kzu

Daniel Cazzulino's Blog

Go Back to
kzu′s Latest post

How to add an MSBuild import to a project on NuGet install

This functionality has been discussed and requested as a feature for a future version of NuGet.

While this gets into the core of NuGet, you can use this code in an install.ps1 script (see Automatically Running PowerShell Scripts During Package Installation and Removal section if you don’t know what that file is for):

param($installPath, $toolsPath, $package, $project)
    # This is the MSBuild targets file to add
    $targetsFile = [System.IO.Path]::Combine($toolsPath, 'Funq.Build.targets')

    # Need to load MSBuild assembly if it's not loaded yet.
    Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
    # Grab the loaded MSBuild project for the project
    $msbuild = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1

    # Make the path to the targets file relative.
    $projectUri = new-object Uri('file://' + $project.FullName)
    $targetUri = new-object Uri('file://' + $targetsFile)
    $relativePath = $projectUri.MakeRelativeUri($targetUri).ToString().Replace([System.IO.Path]::AltDirectorySeparatorChar, [System.IO.Path]::DirectorySeparatorChar)

    # Add the import and save the project
    $msbuild.Xml.AddImport($relativePath) | out-null
    $project.Save()

 

When I have the uninstall part figured out, this will probably make it to the Package authoring tips page.

Update: uninstall part via @teajay on the comments:

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

  # Need to load MSBuild assembly if it’s not loaded yet.
  Add-Type -AssemblyName ‘Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’
  # Grab the loaded MSBuild project for the project
  $msbuild = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1
  $importToRemove = $msbuild.Xml.Imports | Where-Object { $_.Project.Endswith(‘Funq.Build.targets’) }
  # Add the import and save the project
  $msbuild.Xml.RemoveChild($importToRemove) | out-null
  $project.Save()

Enjoy!

Comments

6 Comments

  1. This is a great example and it works!

    Because you work for Clarius, I assume you have some knowledge of T4. I am trying to deliver some basic T4 templates via NuGet and have a problem because each of them have an entry in the csproj file that says “TextTemplatingFileGenerator”. I would like to remove this setting using the basic method you described above. Any suggestions?

  2. Hi Kzu!

    I was able to remove an import by first finding the one I’d like to remove within project.Xml.Imports collection, then project.Xml.RemoveChild(elementFound) removed that successfully!

    Hope this helps,
    Attila

  3. What happens when you do an update with NuGet?
    Does the new version of your targets file get loaded?
    Does this need to also have a command to reload the project or solution?

  4. No reload is necessary.
    No additional “magic” happens, ever. You have to do all by yourself (i.e. in update scenarios)

  5. Here’s the solution to remove the added import when uninstalling the package:
    param($installPath, $toolsPath, $package, $project)

    # Need to load MSBuild assembly if it’s not loaded yet.
    Add-Type -AssemblyName ‘Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’
    # Grab the loaded MSBuild project for the project
    $msbuild = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1
    $importToRemove = $msbuild.Xml.Imports | Where-Object { $_.Project.Endswith(‘Funq.Build.targets’) }
    # Add the import and save the project
    $msbuild.Xml.RemoveChild($importToRemove) | out-null
    $project.Save()