Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a custom powershell script for nuget that adds a custom target to the csproj BeforeBuild step

I want to create a nuget package that adds a BeforeBuild step to my csproj using a custom MSBuild task I have created. Ideally, I want to:

  1. Add a new Target into the csproj file (MyCustomBeforeBuildTarget)
  2. Add the BeforeBuild target if it is not already there
  3. Edit the BeforeBuild DependsOnTargets attribute to include my custom target

So after install my csproj should have the following in:

<Target Name="MyCustomBeforeBuildTarget" Condition="SomeCondition">
     <MyCustomTask />
</Target>
<Target Name="BeforeBuild" DependsOnTargets="MyCustomBeforeBuildTarget">

</Target>

Also, it would be nice that when my package is removed, the custom target disappears too, although I have added a condition that should make it ignore the target if my custom task DLL is not present.

What is the simplest Powershell nuget install script I can write that will add my custom target? I have a feeling that the PowerShell scripts here might form part of the solution, but I don't have enough PowerShell experience to know how to actually use them to edit the csproj.

like image 762
Mark Heath Avatar asked Jun 13 '11 10:06

Mark Heath


2 Answers

The NuGetPowerTools is a package that adds some PowerShell functions that makes it easier to work with the project setup of the project you're adding a package to. To use the functions available you only need to make your package depend on the NuGetPowerTools, using the dependencies tag in your packages nuspec file like this:

<dependencies>
  <dependency id="NuGetPowerTools" version="0.26" />
</dependencies>

This will make it possible to grab a reference to a build project representation of your project.

Then you need to put an install.ps1 file in the tools folder of your NuGet package, this PowerShell file will run when you install the package and never again after installation.

The file should look something like this:

#First some common params, delivered by the nuget package installer
param($installPath, $toolsPath, $package, $project)

# Grab a reference to the buildproject using a function from NuGetPowerTools
$buildProject = Get-MSBuildProject

# Add a target to your build project
$target = $buildProject.Xml.AddTarget("PublishWebsite")

# Make this target run before build
# You don't need to call your target from the beforebuild target,
# just state it using the BeforeTargets attribute
$target.BeforeTargets = "BeforeBuild"

# Add your task to the newly created target
$target.AddTask("MyCustomTask")

# Save the buildproject
$buildProject.Save()

# Save the project from the params
$project.Save()

That should be it.

Regards

Jesper Hauge

like image 177
Hauge Avatar answered Nov 09 '22 14:11

Hauge


The following code comes from a package called CodeAssassin.WixWebProjectReferences. It adds and removes the following import-tag to the project file upon install and uninstall. The package does not require any dependencies.

<Import Project="..\packages\CodeAssassin.WixWebProjectReferences.1.0\tools\CodeAssassin.WixWebProjectReferences.targets" />

Download the package and open it using NuGetPackageExplorer to se how it's done.

Below is the code from install.ps1 and uninstall.ps1 (they are only executed if the content folder of the NuGet-package is non-empty).

(I couldn't find any powershell-highlighting so I used php instead and it's not perfect.)

install.ps1

param (
    $InstallPath,
    $ToolsPath,
    $Package,
    $Project
)

$TargetsFile = 'CodeAssassin.WixWebProjectReferences.targets'
$TargetsPath = $ToolsPath | Join-Path -ChildPath $TargetsFile

Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

$MSBProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($Project.FullName) |
    Select-Object -First 1

$ProjectUri = New-Object -TypeName Uri -ArgumentList "file://$($Project.FullName)"
$TargetUri = New-Object -TypeName Uri -ArgumentList "file://$TargetsPath"

$RelativePath = $ProjectUri.MakeRelativeUri($TargetUri) -replace '/','\'

$ExistingImports = $MSBProject.Xml.Imports |
    Where-Object { $_.Project -like "*\$TargetsFile" }
if ($ExistingImports) {
    $ExistingImports | 
        ForEach-Object {
            $MSBProject.Xml.RemoveChild($_) | Out-Null
        }
}
$MSBProject.Xml.AddImport($RelativePath) | Out-Null
$Project.Save()

uninstall.ps1

param (
    $InstallPath,
    $ToolsPath,
    $Package,
    $Project
)

$TargetsFile = 'CodeAssassin.WixWebProjectReferences.targets'

Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

$MSBProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($Project.FullName) |
    Select-Object -First 1

$ExistingImports = $MSBProject.Xml.Imports |
    Where-Object { $_.Project -like "*\$TargetsFile" }
if ($ExistingImports) {
    $ExistingImports | 
        ForEach-Object {
            $MSBProject.Xml.RemoveChild($_) | Out-Null
        }
    $Project.Save()
}

Sample targets-file that copies some files to the output path

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
    <ItemGroup>
        <Files Include="..\packages\Insert_Path_To_Your_Package_Folder_Here\bin\*" />
    </ItemGroup>
    <Target Name="Insert_Name_of_Your_Target_Here" AfterTargets="AfterBuild">
        <Copy SourceFiles="@(Files)" DestinationFolder="$(TargetDir)\bin\" SkipUnchangedFiles="true" />
    </Target>
</Project>
like image 23
haiiaaa Avatar answered Nov 09 '22 15:11

haiiaaa